diff -c2 -P -r xscreensaver-4.06/driver/XScreenSaver.ad.in xscreensaver-4.06-glsperm/driver/XScreenSaver.ad.in
*** xscreensaver-4.06/driver/XScreenSaver.ad.in 2002-10-23 20:34:46.000000000 -0500
--- xscreensaver-4.06-glsperm/driver/XScreenSaver.ad.in 2002-12-09 09:59:26.000000000 -0600
***************
*** 327,330 ****
--- 327,331 ----
@GL_KLUDGE@ GL: flipscreen3d -root \n\
@GL_KLUDGE@ GL: glsnake -root \n\
+ @GL_KLUDGE@ GL: glsperm -root -fpslo 25 \n\
@GL_KLUDGE@ GL: boxed -root \n\
@GL_KLUDGE@ GL: glforestfire -root \n\
***************
*** 621,624 ****
--- 622,626 ----
*hacks.speedmine.name: SpeedMine
*hacks.glsnake.name: GLSnake
+ *hacks.glsperm.name: GLSperm
*hacks.glforestfire.name: GLForestFire
*hacks.sballs.name: SBalls
diff -c2 -P -r xscreensaver-4.06/driver/XScreenSaver_ad.h xscreensaver-4.06-glsperm/driver/XScreenSaver_ad.h
*** xscreensaver-4.06/driver/XScreenSaver_ad.h 2002-10-23 22:26:18.000000000 -0500
--- xscreensaver-4.06-glsperm/driver/XScreenSaver_ad.h 2002-12-09 09:59:54.000000000 -0600
***************
*** 229,232 ****
--- 229,233 ----
GL: flipscreen3d -root \\n\
GL: glsnake -root \\n\
+ GL: glsperm -root -fpslo 25 \\n\
GL: boxed -root \\n\
GL: glforestfire -root \\n\
diff -c2 -P -r xscreensaver-4.06/hacks/config/glsperm.xml xscreensaver-4.06-glsperm/hacks/config/glsperm.xml
*** xscreensaver-4.06/hacks/config/glsperm.xml 1969-12-31 18:00:00.000000000 -0600
--- xscreensaver-4.06-glsperm/hacks/config/glsperm.xml 2002-12-09 09:24:04.000000000 -0600
***************
*** 0 ****
--- 1,64 ----
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ <_description>
+
+ An OpenGL world of little writhing tadpole creatures,
+ complete with illumination during the night part of
+ the cycle modeled after the creature lights of the
+ deep sea.
+
+ The default Critter Count is 100.
+
+ Setting the Frames/s Max to 0 unlimits it; anything
+ else sets an upper bound.
+
+ Setting the Frames/s Min (Adaptive) to anything above
+ Off (range 1 to 100 in the GUI) selects that as a
+ target framerate to retain by dynamic world
+ modification, including overriding the Light Max
+ Index, Frames/s Max, and dynamically modifying the
+ critter count.
+
+ The default Seconds in Day in 60.
+ The default Epoch basetime is 1970-01-01 00:00:00 GMT.
+ Use these together to set the day start-time and cycle length.
+ The Local Midnight option sets the Epoch to be the
+ most recent midnight in the local timezone.
+
+
+
diff -c2 -P -r xscreensaver-4.06/hacks/glx/Makefile.in xscreensaver-4.06-glsperm/hacks/glx/Makefile.in
*** xscreensaver-4.06/hacks/glx/Makefile.in 2002-10-23 22:26:20.000000000 -0500
--- xscreensaver-4.06-glsperm/hacks/glx/Makefile.in 2002-12-09 08:15:38.000000000 -0600
***************
*** 87,91 ****
cubenetic.c spheremonics.c marching.c lavalite.c rotator.c \
trackball.c gltrackball.c queens.c endgame.c chessmodels.c \
! glblur.c
OBJS = xscreensaver-gl-helper.o \
--- 87,91 ----
cubenetic.c spheremonics.c marching.c lavalite.c rotator.c \
trackball.c gltrackball.c queens.c endgame.c chessmodels.c \
! glblur.c glsperm.c glsperm-random.c
OBJS = xscreensaver-gl-helper.o \
***************
*** 106,110 ****
cubenetic.o spheremonics.o marching.o lavalite.o rotator.o \
trackball.o gltrackball.o queens.o endgame.o chessmodels.o \
! glblur.o
GL_EXES = cage gears moebius pipes sproingies stairs superquadrics \
--- 106,110 ----
cubenetic.o spheremonics.o marching.o lavalite.o rotator.o \
trackball.o gltrackball.o queens.o endgame.o chessmodels.o \
! glblur.o glsperm.o glsperm-random.o
GL_EXES = cage gears moebius pipes sproingies stairs superquadrics \
***************
*** 113,117 ****
dangerball circuit menger engine flipscreen3d glsnake boxed \
glforestfire sballs cubenetic spheremonics lavalite queens \
! endgame glblur
GLE_EXES = extrusion
GL_UTIL_EXES = xscreensaver-gl-helper
--- 113,118 ----
dangerball circuit menger engine flipscreen3d glsnake boxed \
glforestfire sballs cubenetic spheremonics lavalite queens \
! endgame glblur glsperm
!
GLE_EXES = extrusion
GL_UTIL_EXES = xscreensaver-gl-helper
***************
*** 130,134 ****
stonerview-move.h stonerview-osc.h glutstroke.h \
glut_roman.h marching.h rotator.h trackball.h gltrackball.h \
! chessmodels.h chessgames.h
GL_MEN = atlantis.man boxed.man bubble3d.man cage.man circuit.man \
cubenetic.man dangerball.man engine.man extrusion.man \
--- 131,136 ----
stonerview-move.h stonerview-osc.h glutstroke.h \
glut_roman.h marching.h rotator.h trackball.h gltrackball.h \
! chessmodels.h chessgames.h glsperm-random.h
!
GL_MEN = atlantis.man boxed.man bubble3d.man cage.man circuit.man \
cubenetic.man dangerball.man engine.man extrusion.man \
***************
*** 139,143 ****
spheremonics.man sproingies.man stairs.man starwars.man \
stonerview.man superquadrics.man xscreensaver-gl-helper.man \
! endgame.man glblur.man
MEN = @GL_MEN@
EXTRAS = README Makefile.in
--- 141,146 ----
spheremonics.man sproingies.man stairs.man starwars.man \
stonerview.man superquadrics.man xscreensaver-gl-helper.man \
! endgame.man glblur.man glsperm.man
!
MEN = @GL_MEN@
EXTRAS = README Makefile.in
***************
*** 496,499 ****
--- 499,506 ----
$(CC_HACK) -o $@ $@.o tube.o $(HACK_OBJS) $(HACK_LIBS)
+ GLSPERM_OBJS=glsperm.o glsperm-random.o xpm-ximage.o
+ glsperm: $(GLSPERM_OBJS)
+ $(CC_HACK) -o $@ $(GLSPERM_OBJS) $(HACK_LIBS) $(XPM_LIBS)
+
##############################################################################
#
***************
*** 569,572 ****
--- 576,586 ----
glplanet.o: $(srcdir)/xpm-ximage.h
glsnake.o: ../../config.h
+ glsperm.o: ../../config.h
+ glsperm.o: $(srcdir)/glsperm-random.h
+ glsperm.o: $(HACK_SRC)/images/glsperm-sky.xpm
+ glsperm.o: $(UTILS_SRC)/version.h
+ glsperm.o: $(UTILS_SRC)/vroot.h
+ glsperm.o: $(srcdir)/xpm-ximage.h
+ glsperm-random.o: $(srcdir)/glsperm-random.h
gltext.o: ../../config.h
gltext.o: $(srcdir)/gltrackball.h
diff -c2 -P -r xscreensaver-4.06/hacks/glx/glsperm-random.c xscreensaver-4.06-glsperm/hacks/glx/glsperm-random.c
*** xscreensaver-4.06/hacks/glx/glsperm-random.c 1969-12-31 18:00:00.000000000 -0600
--- xscreensaver-4.06-glsperm/hacks/glx/glsperm-random.c 2002-12-09 08:15:38.000000000 -0600
***************
*** 0 ****
--- 1,162 ----
+ /* random.c Copyright (c) 1992, 2002 Christopher Alexander North-Keys */
+ /* incept 1992-02-01 */
+
+ #include
+ #include
+ #include "glsperm-random.h"
+
+ #ifdef TEST
+
+ #include
+
+ static int all_tolerances_met = 1;
+
+ int isAbout(float sample, float expected, float tolerance)
+ {
+ float tol = (tolerance > 0) ? tolerance : -tolerance; /* overkill */
+ float lo = expected - tolerance;
+ float hi = expected + tolerance;
+ return ((lo <= sample) && (sample <= hi));
+ }
+
+ void outResult(const char *desc, float sample, float expected, float tolerance)
+ {
+ static const char *fmt = "%9.3f %9.3f %9.3f %9.3f [%s] %s\n";
+ int tolerance_met = isAbout(sample, expected, tolerance);
+ all_tolerances_met = all_tolerances_met && tolerance_met; /* fail lasts */
+ printf(fmt, sample, expected, sample - expected, tolerance,
+ (tolerance_met ? "pass" : "fail"),
+ desc);
+ }
+
+ int main()
+ {
+ int max = 10;
+ int min = -max; /* don't change this, or an exact center will be lost */
+ int trackers_raw[max - min + 1]; /* for -10 to 10, this yields 21 */
+ int *trackers = &trackers_raw[max]; /* point to the exact center */
+
+ int count = 210000; /* generate this many */
+ int sum = 0; /* track the sum, should be about zero */
+ int sumpos = 0; /* track the sum of positives (and zeros)*/
+ int sumneg = 0; /* track the sum of negatives (and zeros)*/
+ int toopos = 0; /* track out-of-range positives */
+ int tooneg = 0; /* track out-of-range negatives */
+ int countpos = 0;
+ int countneg = 0;
+ int count0 = 0;
+ int i;
+
+ for(i = min ; i <= max ; ++i) /* initial trackers */
+ trackers[i] = 0;
+
+ for(i = 1 ; i <= count ; ++i)
+ {
+ int number = Random(min, max);
+ sum += number;
+ if( number < min) ++tooneg;
+ else if(number > max) ++toopos;
+ else {
+ trackers[number]++; /* yes, that's with negative subscripts */
+ if( number < 0 ) ++countneg, sumneg += number;
+ else if(number > 0 ) ++countpos, sumpos += number;
+ else ++count0, ++countneg, ++countpos; /* neg/pos includes 0s */
+ }
+ }
+
+ printf("numbers: %d from %d to %d\n", count, min, max);
+ puts(" sample expected deviation tolerance pass? description");
+ {
+ float possible_values = (float)(max - min + 1);
+ float count_of_each_value_expected = (float)count / (max-min+1);
+ outResult("average", (float)sum / count, 0.0, 0.5);
+ outResult("average non-negatives (0 and up)",
+ (float)sumpos / countpos, (float)max/2, 0.25);
+ outResult("average non-positive (0 and down)",
+ (float)sumneg / countneg, (float)min/2, 0.25);
+ outResult("out-of-range positives", toopos, 0.0, 0.0);
+ outResult("out-of-range negatives", tooneg, 0.0, 0.0);
+ outResult("zeros", (float)count0,
+ count_of_each_value_expected,
+ count_of_each_value_expected / 30);
+
+ for(i = min ; i <= max ; ++i)
+ {
+ char buf[128];
+ sprintf(&buf[0], "value count for %d", i);
+ outResult(&buf[0],
+ (float)trackers[i],
+ count_of_each_value_expected,
+ count_of_each_value_expected / 30);
+ }
+ }
+
+ for(i = 1 ; i < 100 ; ++i)
+ {
+ double number = RandomDouble(0, 10);
+ printf("%lf\n", number);
+ }
+
+ printf("summary [%s], returning %d\n",
+ (all_tolerances_met ? "pass" : "fail"),
+ (all_tolerances_met ? 0 : -1));
+ return (all_tolerances_met ? 0 : -1);
+ }
+ #endif /* TEST */
+
+ /* SVID 3 states rand() should now be a complete replacement for lrand48() */
+ #if 0
+ #define rand lrand48
+ #define srand srand48
+ #endif
+
+ void RandomSeed(void)
+ {
+ time_t t;
+ time(&t);
+ srand((long)t);
+ return;
+ }
+
+ int Random(int low, int high)
+ {
+ static int initialized = 0;
+ register int result = 0;
+ register int possibiities = -1;
+
+ if( ! initialized)
+ RandomSeed(), ++initialized;
+
+ if(low > high)
+ {
+ int tmp = low;
+ low = high;
+ high = tmp;
+ }
+ possibiities = high - low + 1;
+
+ /* see Knuth's "Numerical Recipes in C" ch 7, or the Linux rand(3) manpage.
+ * "If you want to generate a random integer between 1
+ * and 10, you should always do it by using high-order bits, as in
+ * j=1+(int) (10.0*rand()/(RAND_MAX+1.0));
+ */
+
+ /* 0 to (possibiities - 1) */
+ result = low + (int)( ((float)possibiities * rand()) / (RAND_MAX + 1.0) );
+
+ return result;
+ }
+
+ double RandomDouble(double low, double high)
+ {
+ /* working with RAND_MAX directly causes trouble, so half is used here. */
+ const unsigned long int Max = RAND_MAX / 2;
+ long double double_base = Random(0, Max);
+ long double range = high - low;
+ /* a rational number */
+ /* |- between [0,1] -| */
+ return low + ( (range * double_base) / Max );
+ }
+
+
+ /* - - - - - - - - - - - - - - - - - - - - - - - eof - - - - - - - - - */
diff -c2 -P -r xscreensaver-4.06/hacks/glx/glsperm-random.h xscreensaver-4.06-glsperm/hacks/glx/glsperm-random.h
*** xscreensaver-4.06/hacks/glx/glsperm-random.h 1969-12-31 18:00:00.000000000 -0600
--- xscreensaver-4.06-glsperm/hacks/glx/glsperm-random.h 2002-12-09 08:15:38.000000000 -0600
***************
*** 0 ****
--- 1,11 ----
+ /* random.h Copyright (c) 1992 Christopher Alexander North-Keys */
+ /* random.h geboren Sat d.01.02.1992 */
+
+ #ifndef _seen_random_h
+ #define _seen_random_h
+
+ extern int Random(int low, int high);
+ extern double RandomDouble(double low, double high);
+
+ /* nu"r #endif herunten */
+ #endif /* _seen_random_h */
diff -c2 -P -r xscreensaver-4.06/hacks/glx/glsperm.c xscreensaver-4.06-glsperm/hacks/glx/glsperm.c
*** xscreensaver-4.06/hacks/glx/glsperm.c 1969-12-31 18:00:00.000000000 -0600
--- xscreensaver-4.06-glsperm/hacks/glx/glsperm.c 2002-12-09 08:15:38.000000000 -0600
***************
*** 0 ****
--- 1,1901 ----
+ /* glsperm.c : cute colorful "tadpoles" in a variably-lit world.
+ * Copyright (C) 2002 Christopher Alexander North-Keys
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Alternatively, the GNU General Public License can be found[1] at the
+ * GNU website at http://www.gnu.org/
+ *
+ * The author of this program maintains[1] a website where contact info and
+ * other information may be found at http://www.talisman.org/~erlkonig/
+ * Bear in mind that this software is UNSUPPORTED, and any replies or actions
+ * pursuant to requests are at the sole whim and discretion of the author.
+ *
+ * [1] At the time of this writing.
+ */
+
+ static const char *Version[] = {
+ ("$Copyright: "
+ "@(#) glsperm.c Copyright (C)2002 Christopher Alexander North-Keys, "
+ "under terms of the GNU General Public License, version 2 "
+ "$"),
+ "$Group: Talisman $",
+ "$Incept: 2002-09-12 10:13:01 GMT (Sep Thu) 1031825581 $",
+ "$Compiled: "__DATE__" " __TIME__ " $",
+ "$Source: /home/erlkonig/z/xscreensaver-mod/hacks/glx/glsperm.c $",
+ "$State: alpha $",
+ "$Revision: 0.9 $",
+ "$Date: 2002-09-16 02:28:49 CDT (Sep Mon) 1032161329 $",
+ "$Author: erlkonig $",
+ "$Website: http://www.talisman.org/~erlkonig/ $",
+ (const char*)0
+ };
+
+ /* Translated from my original C++ program pursuant to joining xscreensaver */
+
+ /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Includes */
+ #ifdef __cplusplus
+ extern "C" {
+ #endif
+
+ #include
+
+ #include
+ #include
+ #include
+
+ #include
+ #include
+ #include
+ #include /* for memset(), etc. */
+ #include
+ #include
+ #include
+ #include
+ #include
+
+ #include
+ #include
+ #include "vroot.h"
+ #include "version.h"
+
+ #include "glsperm-random.h"
+ #include "xpm-ximage.h"
+ #include "../images/glsperm-sky.xpm"
+
+ #ifdef __cplusplus
+ };
+ #endif
+
+ /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Types */
+
+ #ifndef __cplusplus
+ typedef char bool;
+ enum { false = 0, true = 1 };
+ #endif
+
+ typedef struct _Port_t
+ {
+ char *display_string; /* don`t free this - src is getenv or argv */
+ char *geometry; /* don`t free this - src is default or argv */
+ Display *dpy; /* X display handle */
+ Atom XA_WM_PROTOCOLS;
+ Atom XA_WM_DELETE_WINDOW;
+ Colormap colormap;
+ int screen;
+ int depth;
+ Visual *visual; /* no apparent need to free after use */
+ bool running_on_root; /* default: false */
+ bool running_in_prior_window; /* default: false */
+ bool running_fullscreen; /* default: false */
+ Window window;
+ Window root_window;
+ int root_w, root_h; /* width and height on root screen */
+ int w, h, x, y; /* window size and position */
+ bool geometry_has_xy_offsets; /* set by PortGeometryInterpret */
+ GLXContext glx_context;
+ int lights_available; /* set in PortOpen() */
+ bool is_nonrotating; /* toggle the viewpoint rotation */
+ /* actually rendered, and may also affect */
+ /* statistics */ /* critter count and other features. */
+ unsigned long int frames_drawn;
+ double frame_first_t0; /* first frame start time of rendering */
+ double frame_prior_t0; /* prior frame start time */
+ double frame_prior_t1; /* prior frame time at render completion */
+ double frame_this_t0; /* this frame`s render start time */
+ double frame_this_t1; /* this frame`s render completion timestamp */
+ } Port_t;
+
+ typedef struct _Coord_t
+ {
+ float x, y, z;
+ } Coord_t;
+
+ typedef struct _Segment_t
+ {
+ struct _Segment_t *prev, *next;
+ Coord_t position;
+ } Segment_t;
+
+ typedef struct _Sperm_t
+ {
+ struct _Sperm_t *next;
+ Segment_t *segments;
+ Segment_t *segment_last;
+ unsigned int length;
+ unsigned int length_target;
+ float velocity_limit;
+ float displacement_limit;
+ float acceleration_limit;
+ float head_radius;
+ float tail_radius;
+ bool lit;
+ float bright_suppress;
+ Coord_t velocity;
+ } Sperm_t;
+
+ typedef struct _World_t
+ {
+ Sperm_t *swarm;
+ unsigned int desired_count;
+ unsigned int population;
+ float sky_radius;
+ unsigned long int seconds_in_day; /* default 60 */
+ unsigned long int timebase; /* default to unix epoch */
+ float cycle_point; /* subpoint in cycle, range [0, 1) */
+ float light_level; /* [light_bmin, light_max] */
+ float light_min; /* [0, 1] constraint on light_level */
+ float light_max; /* [0, 1] constraint on light_level */
+ float night_bias; /* 0.0 = all daytime, 1.0 = all nighttime */
+ unsigned int desired_lit_count;
+ bool is_aquatic; /* affects render, may add bubbles someday */
+ bool is_strayer_hostile; /* kill sperm which are too far */
+ bool is_sperm_variable; /* different-looking sperm? */
+ bool is_environ_free; /* for speed purposes, disables sky */
+ } World_t;
+
+ /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Globals */
+
+ /* NOTE: the X and OpenGL machines carry, of course, lots of additional
+ * global info. While the X is pretty much directed entirely via Port
+ * calls, be aware that quite a bit of work would be needed setting up the
+ * Ports for multiple OpenGL contexts before you`d be able to spawn two
+ * windows simultaneously, especially for the same World. :-)
+ */
+
+ char *progname = "";
+
+ /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Prototypes */
+
+ int Cout(const char *format, ...);
+ int Cerr(const char *format, ...);
+ void DefaultMaterial(void);
+ unsigned long int MicroAndSeconds(unsigned long int *seconds);
+ double TimeAsDouble(void);
+ double SleepDouble(double secs); /* return time actually slept */
+ GLuint NewListID(void); /* this should probably be window-specific */
+ int Sphere(int detail, bool inside_only_flag); /* radius 1 sphere. */
+ void PortOutF(Port_t *p, FILE *out);
+ Port_t *PortNew(void);
+ bool PortOpen(Port_t *port, int argc, char **argv);
+ void PortDelete(Port_t **doomed_addr); /* shutdown Glx window and toss info */
+ char PortEvents(Port_t *p); /* return keypress cmds as single bytes */
+ bool PortGeometryInterpret(Port_t *p);
+ void PortViewpoint(Port_t *p);
+ void PortReshape(Port_t *port);
+ bool PortDefaults(Port_t *port); /* run initial GL commands */
+ int PortRender(Port_t *p, World_t *w);
+ int PortRenderWorld(Port_t *p, World_t *w);
+ int PortRenderSegment(Port_t *p, World_t *w, Segment_t *s, float scale, int detail);
+ int PortRenderSky(Port_t *p, World_t *w);
+ bool PortRenderSpermLight(Port_t *p, World_t *w, Sperm_t *s, GLenum light);
+ int PortRenderSpermBulb(Port_t *p, World_t *w, Sperm_t *s);
+ int PortRenderSperm(Port_t *p, World_t *w, Sperm_t *s);
+ bool PortDepthFromVisual(Port_t *p);
+ float CoordAmplitude(Coord_t *c);
+ void CoordSetAmplitude(Coord_t *c, float amplitude_desired);
+ void CoordUnitRandom(Coord_t *c); /* convert to a random amplitude 1 vector */
+ void CoordInvert(Coord_t *c);
+ void CoordPlusEquals(Coord_t *accum, const Coord_t *addthis);
+ void CoordMinusEquals(Coord_t *accum, const Coord_t *subthis);
+ unsigned long int LocalMidnight(void);
+ Segment_t *SegmentNew(Coord_t *pos);
+ Segment_t *SegmentExtract(Segment_t *extractee); /* return a list neighbor */
+ void SegmentDeleteAll(Segment_t **doomed_addr); /* eradicates neighbors */
+ void SegmentSetPosition(Segment_t *s, Coord_t *pos);
+ Sperm_t *SpermNew(bool is_variable);
+ void SpermDelete(Sperm_t **doomed_addr);
+ unsigned int SpermLength(Sperm_t *s);
+ void SpermLonger(Sperm_t *s); /* lengthen by one segment, the new head */
+ void SpermShorter(Sperm_t *s);
+ void SpermUpdate(Sperm_t *s);
+ bool SpermLightDetails(Sperm_t *s, Coord_t *pos, Coord_t *dir);
+ void WorldOutF(World_t *w, FILE *out);
+ World_t *WorldNew(void);
+ void WorldDelete(World_t **doomed);
+ unsigned int WorldPopulation(World_t *w);
+ int WorldLightCount(World_t *w);
+ GLenum WorldLightForIndex(World_t *w, int index);
+ int WorldLitCount(World_t *w);
+ void WorldUpdate(World_t *w);
+ char *MakeTitleString(void);
+ void Usage(void);
+
+ /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Functions */
+
+ int Cout(const char *format, ...)
+ {
+ int count = 0;
+ va_list parms;
+ va_start(parms, format);
+ count += vfprintf(stdout, format, parms);
+ va_end(parms);
+ return count;
+ }
+
+ int Cerr(const char *format, ...)
+ {
+ int count = 0;
+ va_list parms;
+ va_start(parms, format);
+ count += fprintf(stderr, "%s", progname);
+ count += vfprintf(stderr, format, parms);
+ va_end(parms);
+ return count;
+ }
+
+ unsigned long int MicroAndSeconds(unsigned long int *seconds)
+ {
+ struct timeval tv;
+ gettimeofday(&tv, (struct timezone*)0);
+ *seconds = tv.tv_sec;
+ return tv.tv_usec;
+ }
+
+ double TimeAsDouble(void)
+ {
+ unsigned long int secs, microsecs;
+ microsecs = MicroAndSeconds(&secs);
+ return (double)secs + (double)microsecs / 1000000.0;
+ }
+
+ double SleepDouble(double secs) /* return actual */
+ {
+ double before = TimeAsDouble(), after;
+ struct timespec req;
+ req.tv_sec = (int)secs; /*1---___--- */
+ req.tv_nsec = (secs - req.tv_sec) * 1000000000;
+ if((-1 == nanosleep(&req, &req)) && (errno == EINTR))
+ nanosleep(&req, &req); /* give up after a possible 2nd try */
+ after = TimeAsDouble();
+ return after - before;
+ }
+
+ unsigned long int LocalMidnight(void)
+ {
+ time_t now = time(0);
+ struct tm *tv = localtime(&now);
+ tv->tm_hour = tv->tm_min = tv->tm_sec = 0; /* set time to local midnight */
+ return mktime(tv);
+ }
+
+ void DefaultMaterial(void)
+ {
+ static bool initialized = false;
+ static GLfloat ambient[4];
+ static GLfloat diffuse[4];
+ static GLfloat specular[4];
+ static GLfloat emission[4];
+ static GLfloat shininess[1];
+
+ if( ! initialized)
+ {
+ glGetMaterialfv(GL_FRONT, GL_AMBIENT, ambient);
+ glGetMaterialfv(GL_FRONT, GL_DIFFUSE, diffuse);
+ glGetMaterialfv(GL_FRONT, GL_SPECULAR, specular);
+ glGetMaterialfv(GL_FRONT, GL_EMISSION, emission);
+ glGetMaterialfv(GL_FRONT, GL_SHININESS, shininess);
+ initialized = true;
+ }
+ else
+ {
+ glMaterialfv(GL_FRONT, GL_AMBIENT, ambient);
+ glMaterialfv(GL_FRONT, GL_DIFFUSE, diffuse);
+ glMaterialfv(GL_FRONT, GL_SPECULAR, specular);
+ glMaterialfv(GL_FRONT, GL_EMISSION, emission);
+ glMaterialfv(GL_FRONT, GL_SHININESS, shininess);
+ }
+ return;
+ }
+
+ GLuint NewListID(void) /* this should probably be window-specific */
+ {
+ static GLuint current = 0;
+ return ++current; /* first is 1 */
+ }
+
+ int Sphere(int detail, bool inside_only_flag) /* radius 1. return tri count */
+ {
+ /* support complexity levels from 1 to 20 */
+ enum { min = 1, max = 20 };
+ static GLUquadricObj *sphere_inner[max + 1];
+ static GLUquadricObj *sphere_outer[max + 1];
+ static GLuint id_inner[max + 1];
+ static GLuint id_outer[max + 1];
+ static bool initialized = false;
+ if( ! (min <= detail) && (detail <= max)) {
+ static bool warned = false;
+ if( ! warned) {
+ Cerr(":%s: detail %d out of range [%d, %d]\n",
+ __FUNCTION__, detail, min, max);
+ warned = true;
+ }
+ return 0;
+ }
+ if( ! initialized) {
+ int i;
+ for(i = min ; i <= max ; ++i)
+ {
+ int detail = i;
+ /* inner glu */
+ sphere_inner[i] = gluNewQuadric();
+ gluQuadricOrientation(sphere_inner[i], GLU_INSIDE);
+ gluQuadricTexture(sphere_inner[i], GLU_TRUE);
+ /* outer glu */
+ sphere_outer[i] = gluNewQuadric();
+ gluQuadricTexture(sphere_outer[i], GLU_TRUE);
+ /* inner gl */
+ id_inner[i] = NewListID();
+ glNewList(id_inner[i], GL_COMPILE);
+ gluSphere(sphere_inner[i], 1, detail, detail);
+ glEndList();
+ /* outer gl */
+ id_outer[i] = NewListID();
+ glNewList(id_outer[i], GL_COMPILE);
+ gluSphere(sphere_outer[i], 1, detail, detail);
+ glEndList();
+ }
+ initialized = true;
+ }
+ glCallList(inside_only_flag
+ ? id_inner[detail]
+ : id_outer[detail]);
+ return detail * detail * 2;
+ }
+
+ int PortRenderSky(Port_t *p, World_t *w) /* 0 = dawn, 0.25 = noon... */
+ {
+ int faces = 0;
+ GLfloat au = 0.90; /* distance to sun from world (A.U.), sky is 1.0 */
+ GLfloat lite = w->light_level;
+ GLfloat lite_sky = lite * 0.8 + 0.2; /* dark gray to white + sky tex */
+ float cycle = w->cycle_point;
+ GLfloat sunset = (sin((cycle - 0.25) * 2 * M_PI) + 1.0) / 2.0;/*peak @.25*/
+
+ /* Sun mode */
+ GLfloat sunny = pow((double)lite, (double)0.58); /* lower for more light */
+ GLfloat lite_ambS = sunny * 0.5;
+ GLfloat lite_difS = sunny * 1.5;
+ GLfloat lightambS[4] = { lite_ambS, lite_ambS, lite_ambS, 1.0 };
+ GLfloat lightdifS[4] = { lite_difS, lite_difS, lite_difS + 0.1, 1.0 };
+ GLfloat lightspeS[4] = { lite_difS, lite_difS, lite_difS + 0.1, 1.0 };
+ GLfloat lightposS[4] = { cos(cycle * 2*M_PI) * au, /* right 0 left 0 */
+ sin(cycle * 2*M_PI) * au, /* 0 up 0 down */
+ sin(cycle * 2*M_PI) * au, /* 0 front 0 back */
+ 1.0 }; /* positional */
+
+ glLightfv(GL_LIGHT0, GL_AMBIENT, lightambS);
+ glLightfv(GL_LIGHT0, GL_DIFFUSE, lightdifS);
+ glLightfv(GL_LIGHT0, GL_SPECULAR, lightspeS);
+ glLightfv(GL_LIGHT0, GL_POSITION, lightposS);
+ glLightf (GL_LIGHT0, GL_CONSTANT_ATTENUATION, 1.0);
+ glLightf (GL_LIGHT0, GL_LINEAR_ATTENUATION, 0.0);
+ glLightf (GL_LIGHT0, GL_QUADRATIC_ATTENUATION, 0.0);
+
+ glPushMatrix();
+ glRotatef(90, 1, 0, 0); /* hide less-well-textured sphere areas. */
+
+ glEnable(GL_LIGHT0);
+ /* glDisable(GL_FOG); */
+
+ if(w->is_environ_free)
+ {
+ glColor3f(0.0 + 0.5*sunset,
+ 0.0 + 0.1*sunset,
+ 0.8 - 0.2*sunset);
+ Sphere(12, true); /* args: 'detail, 'inner-only-flag */
+ } else {
+ static bool initialized = false;
+ if( ! initialized)
+ {
+ XImage *image = xpm_to_ximage(p->dpy, p->visual, p->colormap,
+ glsperm_sky);
+ if(image)
+ {
+ while(glGetError() != GL_NO_ERROR);
+ glTexImage2D(GL_TEXTURE_2D,
+ 0, GL_RGBA, image->width, image->height, 0,
+ GL_RGBA, GL_UNSIGNED_BYTE, image->data);
+ if(glGetError() == GL_NO_ERROR)
+ initialized = true;
+ }
+ 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);
+
+ glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+
+ glTexGeni (GL_S, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
+ glTexGeni (GL_T, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
+ }
+ glColor3f(lite_sky + 0.5*sunset,
+ lite_sky + 0.1*sunset,
+ lite_sky - 0.2*sunset);
+ glEnable(GL_TEXTURE_2D);
+ DefaultMaterial();
+ faces += Sphere(12, true); /* args: 'detail, 'inner-only-flag */
+ glDisable(GL_TEXTURE_2D);
+ }
+
+ /* glEnable(GL_FOG);*/
+ glPopMatrix();
+
+ /* glDisable(GL_LIGHT0); */
+ return faces;
+ }
+
+ /* - - - - - - - - - - - - - - - - - - - - - - - - - - Functions - Port */
+
+ void PortOutF(Port_t *p, FILE *out)
+ {
+ fprintf(out, "Port %#lx\n", (unsigned long int)p);
+ fprintf(out, " display: \"%s\" -> %#lx\n",
+ p->display_string, (unsigned long int)(p->dpy));
+ fprintf(out, " screen %d, depth %d, visual id %ld\n",
+ p->screen, p->depth, p->visual ? p->visual->visualid : -1);
+ fprintf(out, " colormap %ld\n", p->colormap);
+ fprintf(out, " root window %ld, window in use %ld, at =%dx%d+%d+%d\n",
+ p->root_window, p->window,
+ p->w, p->h, p->x, p->y);
+ fprintf(out, " on root? %s, in prior window? %s, fullscreen? %s\n",
+ p->running_on_root ? "yes" : "no",
+ p->running_in_prior_window ? "yes" : "no",
+ p->running_fullscreen ? "yes" : "no");
+ fprintf(out, " root size %dx%d\n", p->root_w, p->root_h);
+ fprintf(out, " geometry %dx%d+%d+%d\n",
+ p->w, p->h, p->x, p->y);
+ fprintf(out, " GLX context %#lx\n", (unsigned long int)p->glx_context);
+ fprintf(out, " lights available %d\n", p->lights_available);
+ }
+
+ Port_t *PortNew()
+ {
+ Port_t *hi = (Port_t*)calloc(1, sizeof(*hi));
+ if(hi)
+ {
+ /* default values */
+ hi->display_string = getenv("DISPLAY");
+ hi->geometry = "=400x300";
+ } else {
+ Cerr(":%s:calloc failed - %s\n", __FUNCTION__, strerror(errno));
+ return hi;
+ }
+ return hi;
+ }
+
+ /* create and setup a Glx window */
+ bool PortOpen(Port_t *port, int argc, char **argv)
+ {
+ /* note that this function doesn't make any gl* calls */
+ Window rootwindow;
+
+ if( ! port)
+ return Cerr(":%s: error - port param null\n", __FUNCTION__), false;
+
+ if( ! (port->dpy = XOpenDisplay(port->display_string)))
+ return Cerr(":%s:XOpenDisplay failed\n", __FUNCTION__), false;
+
+ port->w = port->h = 500; /* '' */
+ port->screen = DefaultScreen(port->dpy);
+ port->root_window = RootWindow(port->dpy, port->screen);
+
+ port->XA_WM_PROTOCOLS = XInternAtom(port->dpy,"WM_PROTOCOLS", False);
+ port->XA_WM_DELETE_WINDOW= XInternAtom(port->dpy,"WM_DELETE_WINDOW",False);
+
+ rootwindow = RootWindow(port->dpy, port->screen);
+
+ /* arrange for port->window to override and set port->running_on_root */
+ if(port->window && port->running_on_root)
+ if(port->window != rootwindow)
+ port->running_on_root = false;
+
+ if(port->running_on_root)
+ {
+ port->window = rootwindow;
+ /* the port->fullscreen action isn`t necessary. */
+ }
+
+ if(port->window)
+ {
+ if(port->window != rootwindow)
+ port->running_on_root = false;
+ XWindowAttributes xgwa;
+ XGetWindowAttributes(port->dpy, port->window, &xgwa);
+ port->visual = xgwa.visual;
+ port->w = xgwa.width;
+ port->h = xgwa.height;
+ } else {
+ XSizeHints hints; memset(&hints, 0, sizeof(hints));
+ port->root_w = WidthOfScreen(DefaultScreenOfDisplay(port->dpy));
+ port->root_h = HeightOfScreen(DefaultScreenOfDisplay(port->dpy));
+
+ if(port->running_fullscreen)
+ {
+ port->w = port->root_w;
+ port->h = port->root_h;
+ }
+ else if(PortGeometryInterpret(port))
+ {
+ hints.flags = USSize;
+ hints.width = port->w;
+ hints.height = port->h;
+ if(port->geometry_has_xy_offsets)
+ {
+ hints.flags |= USPosition;
+ hints.x = port->x;
+ hints.y = port->y;
+ }
+ }
+
+ { /* Pick a good GL visual */
+ enum { RS = GLX_RED_SIZE,
+ GS = GLX_GREEN_SIZE,
+ BS = GLX_BLUE_SIZE,
+ DS = GLX_DEPTH_SIZE,
+ IS = GLX_BUFFER_SIZE,
+ DB = GLX_DOUBLEBUFFER };
+
+ int attrs[][20] = {
+ { GLX_RGBA, RS, 8, GS, 8, BS, 8, DS, 8, DB, 0 },/*rgb double*/
+ { GLX_RGBA, RS, 4, GS, 4, BS, 4, DS, 4, DB, 0 },
+ { GLX_RGBA, RS, 2, GS, 2, BS, 2, DS, 2, DB, 0 },
+ { GLX_RGBA, RS, 8, GS, 8, BS, 8, DS, 8, 0 },/*rgb single*/
+ { GLX_RGBA, RS, 4, GS, 4, BS, 4, DS, 4, 0 },
+ { GLX_RGBA, RS, 2, GS, 2, BS, 2, DS, 2, 0 },
+ { IS, 8, DS, 8, DB, 0 },/*cmap double*/
+ { IS, 4, DS, 4, DB, 0 },
+ { IS, 8, DS, 8, 0 },/*cmap single*/
+ { IS, 4, DS, 4, 0 },
+ { GLX_RGBA, RS, 1, GS, 1, BS, 1, DS, 1, 0 } /*monochrome*/
+ };
+
+ unsigned int i;
+ for(i = 0 ; i < (sizeof attrs / sizeof *attrs); ++i)
+ {
+ XVisualInfo *vi = glXChooseVisual(port->dpy, port->screen,
+ attrs[i]);
+ if(vi)
+ {
+ port->visual = vi->visual;
+ XFree(vi);
+ break;
+ }
+ }
+ }
+
+ if( ! port->visual)
+ return Cerr(":%s: GL visual unavailable.\n", __FUNCTION__), false;
+
+ {
+ XSetWindowAttributes xswa;
+ unsigned long xswa_mask = ( CWEventMask | CWColormap
+ | CWBackPixel | CWBackingPixel
+ | CWBorderPixel );
+ xswa.colormap = XCreateColormap(port->dpy,
+ port->root_window,
+ port->visual, AllocNone);
+ xswa.background_pixel = BlackPixel(port->dpy, port->screen);
+ xswa.backing_pixel = xswa.background_pixel;
+ xswa.border_pixel = xswa.background_pixel;
+ xswa.event_mask = ( KeyPressMask
+ | ButtonPressMask
+ | StructureNotifyMask);
+
+ PortDepthFromVisual(port);
+ port->window = XCreateWindow(port->dpy, port->root_window,
+ port->x, port->y, port->w, port->h,
+ 0, port->depth,
+ InputOutput, port->visual,
+ xswa_mask, &xswa);
+ }
+
+ {
+ XWMHints wmhints;
+ XTextProperty tp1, tp2;
+ char *title = MakeTitleString(); /* check fun: may need freeing */
+ XStringListToTextProperty(&title, 1, &tp1);
+ XStringListToTextProperty(&progname, 1, &tp2);
+ wmhints.flags = InputHint;
+ wmhints.input = True;
+
+ XSetWMProperties(port->dpy, port->window, &tp1, &tp2,
+ argv, argc, &hints, &wmhints, 0);
+ }
+
+ XChangeProperty (port->dpy, port->window,
+ port->XA_WM_PROTOCOLS, XA_ATOM, 32,
+ PropModeReplace,
+ (unsigned char *)&(port->XA_WM_DELETE_WINDOW), 1);
+
+ XMapRaised (port->dpy, port->window);
+ XSync (port->dpy, False);
+ }
+
+ /* Now hook up to GLX */
+ {
+ XVisualInfo vi_in, *vi_out;
+ int out_count;
+ vi_in.screen = port->screen;
+ vi_in.visualid = XVisualIDFromVisual(port->visual);
+ vi_out = XGetVisualInfo(port->dpy,
+ VisualScreenMask | VisualIDMask,
+ &vi_in, &out_count);
+ if(vi_out)
+ {
+ port->glx_context = glXCreateContext(port->dpy, vi_out, 0,GL_TRUE);
+ XFree((char*)vi_out);
+
+ if(port->glx_context)
+ {
+ glXMakeCurrent(port->dpy, port->window, port->glx_context);
+ /* SUCCESS ! */
+ } else Cerr(": couldn't create GL context for root window.\n");
+ } else Cerr(": couldn't get visual info.\n");
+ }
+
+ glGetIntegerv(GL_MAX_LIGHTS, &(port->lights_available));
+ --(port->lights_available); /* decrement to account for LIGHT0 always on */
+ PortReshape(port);
+ PortDefaults(port);
+
+ return true;
+ }
+
+ void PortDelete(Port_t **doomed_addr) /* shutdown Glx window and toss info */
+ {
+ if(doomed_addr)
+ {
+ Port_t *doomed = *doomed_addr;
+ if(doomed) /* the following is mostly raving compulsiveness :-) */
+ {
+ if(doomed->glx_context)
+ glXDestroyContext(doomed->dpy, doomed->glx_context);
+ if(doomed->window && ! ( doomed->running_in_prior_window
+ || doomed->running_on_root))
+ XDestroyWindow(doomed->dpy, doomed->window);
+ if(doomed->colormap)
+ XFreeColormap(doomed->dpy, doomed->colormap);
+ if(doomed->dpy)
+ XCloseDisplay(doomed->dpy);
+ free(doomed);
+ }
+ *doomed_addr = 0;
+ }
+ }
+
+ bool PortGeometryInterpret(Port_t *p) /* format: [[=]NxN]][±N±N] */
+ {
+ bool succp = false; /* skip leading `=` if present */
+ char *geometry = ('=' == p->geometry[0]) ? (p->geometry + 1) : p->geometry;
+ if( ! geometry) /* sanity check */
+ return succp; /* still false of course */
+ /* insure valid chars and nothing line =100x100--4+-4 */
+ if( strspn(geometry, "0123456789=+-x") == strlen(geometry)
+ && ! ( strstr(geometry, "--") || strstr(geometry, "++")
+ || strstr(geometry, "+-") || strstr(geometry, "-+")))
+ {
+ int w = -1, h = -1, x = -1, y = -1; /* invalids */
+ /* delimiters we expect: */
+ char d_wh = '\0'; /* the `x` between width and height */
+ char d_xo = '\0'; /* the `±` before the x offset */
+ char d_yo = '\0'; /* the `±` before the y offset */
+ char oops = '\0'; /* this one should -not- end up being read. */
+ int fields = (isdigit(geometry[0])
+ ? sscanf(geometry, "%d%c%d%c%d%c%d%c",
+ &w, &d_wh, &h, &d_xo, &x, &d_yo, &y, &oops)
+ : sscanf(geometry, "%c%d%c%d%c",
+ &d_xo, &x, &d_yo, &y, &oops));
+ if((3 == fields) || (7 == fields)) /* may have W and H */
+ if((succp = ((w > 0) && ('x' == d_wh) && (h > 0))))
+ p->w = w, p->h = h;
+ if((4 == fields) || (succp && (7 == fields))) /* may have X and Y */
+ if((succp = ( strchr("+-", d_xo) && (x >= 0)
+ && strchr("+-", d_yo) && (y >= 0))))
+ {
+ p->x = (('-' == d_xo) ? (p->root_w - p->w - x) : x);
+ p->y = (('-' == d_yo) ? (p->root_h - p->h - y) : y);
+ p->geometry_has_xy_offsets = true;
+ }
+ if( ! succp) Cerr(":%s: bad field count in geometry\n", __FUNCTION__);
+ } else Cerr(":%s: invalid characters in geometry\n", __FUNCTION__);
+ return succp;
+ }
+
+ bool PortDepthFromVisual(Port_t *p) /* prereqs: p->{dpy,screen,visual} */
+ {
+ bool succp = false;
+ XVisualInfo vi_in, *vi_out = 0;
+ int out_count = 0;
+ vi_in.screen = p->screen;
+ vi_in.visualid = XVisualIDFromVisual(p->visual);
+ vi_out = XGetVisualInfo(p->dpy, VisualScreenMask | VisualIDMask,
+ &vi_in, &out_count);
+ if(vi_out)
+ {
+ p->depth = vi_out[0].depth;
+ succp = true;
+ XFree((char*)vi_out);
+ }
+ return succp;
+ }
+
+ char PortEvents(Port_t *p) /* return keypress cmds as single bytes */
+ {
+ while(XPending(p->dpy))
+ {
+ XEvent ev;
+ XNextEvent(p->dpy, &ev);
+ switch(ev.xany.type)
+ {
+ case ConfigureNotify:
+ {
+ XWindowAttributes xgwa;
+ XGetWindowAttributes(p->dpy, p->window, &xgwa);
+ p->w = xgwa.width, p->h = xgwa.height;
+ PortReshape(p);
+ break;
+ }
+ case KeyPress:
+ {
+ KeySym keysym;
+ char c = 0;
+ if(0 < XLookupString(&ev.xkey, &c, 1, &keysym, 0)) /*get any?*/
+ /* note that (ev.xkey.x, ev.xkey.y) are also available */
+ /* Preėmptive Return */
+ return c; /* return for next events later */
+ }
+ break;
+ case ButtonPress:
+ XBell(p->dpy, 0);
+ break;
+ case ClientMessage:
+ if(p->XA_WM_PROTOCOLS != ev.xclient.message_type)
+ {
+ char *s = XGetAtomName(p->dpy, ev.xclient.message_type);
+ if( ! s) s = "(null)";
+ Cerr(":%s: unknown ClientMessage \"%s\" recvd\n", __FUNCTION__,
+ s);
+ }
+ else if(p->XA_WM_DELETE_WINDOW != (unsigned long)ev.xclient.data.l[0])
+ {
+ char *s1 = XGetAtomName(p->dpy, ev.xclient.message_type);
+ char *s2 = XGetAtomName(p->dpy, ev.xclient.data.l[0]);
+ if( ! s1) s1 = "(null)";
+ if( ! s2) s2 = "(null)";
+ Cerr(":%s: unknown ClientMessage \"%s\" [\"%s\"] received\n",
+ __FUNCTION__,
+ s1, s2);
+ }
+ break;
+ }
+ }
+ return '\0';
+ }
+
+ void PortViewpoint(Port_t *p)
+ {
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+ gluPerspective(30.0, 1280.0/1024.0, 0.1, 50);
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+ gluLookAt(0, 0, 30, /* eye pos */
+ 0, 0, 0, /* reference point */
+ 0, 1, 0); /* up */
+ {
+ static GLfloat rotation = 0;
+ if( ! p->is_nonrotating)
+ rotation += 0.5;
+ glRotatef(rotation, 0, 1, 0);
+ }
+ }
+ void PortReshape(Port_t *port)
+ {
+ glMatrixMode(GL_MODELVIEW);
+ glViewport(0, 0, port->w, port->h);
+ glLoadIdentity();
+ }
+
+ bool PortDefaults(Port_t *port) /* run initial GL commands */
+ {
+ glShadeModel(GL_SMOOTH);
+ glEnable(GL_COLOR_MATERIAL);
+
+ glEnable(GL_CULL_FACE); glCullFace(GL_BACK);
+ glEnable(GL_NORMALIZE);
+
+ glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LESS);
+
+ {
+ GLfloat amb[4] = { 0.0, 0.0, 0.0, 1.0 };
+ glLightModelfv(GL_LIGHT_MODEL_AMBIENT, amb);
+ }
+ glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, 1);
+ glEnable(GL_LIGHTING);
+
+ glColor3f(1.0, 1.0, 1.0);
+ glClearColor(0.0, 0.0, 0.0, 1.0);
+
+ #if 0
+ {
+ int light_count = -1;
+ glGetIntegerv(GL_MAX_LIGHTS, &light_count);
+ Cout("[actual light max is %d]\n", light_count);
+ }
+ #endif
+
+ return true;
+ }
+
+ /* this is lighting setup only - the bright aura is in PortRenderSpermBulb */
+ bool PortRenderSpermLight(Port_t *p, World_t *w, Sperm_t *s, GLenum light)
+ {
+ bool succp = false;
+ if(s && s->lit && (w->light_level < s->bright_suppress)
+ && (GL_NONE != light))
+ {
+ Coord_t bulb, dir; /* bulb position and beam (cone) direction */
+ if(SpermLightDetails(s, &bulb, &dir))
+ {
+ GLfloat lightpos[4] = { bulb.x, bulb.y, bulb.z, 1 };
+ GLfloat lightamb[4] = { 0.0, 0.0, 0.0, 1.0 };
+ GLfloat lightdif[4] = { 0.8, 0.8, 0.7, 1.0 };
+ GLfloat lightspe[4] = { 0.8, 0.8, 0.4, 1.0 };
+ /* GLfloat lightdir[3] = { dir.x, dir.y, dir.z}; */
+ glLightfv(light, GL_POSITION, lightpos);
+ glLightfv(light, GL_AMBIENT, lightamb);
+ glLightfv(light, GL_DIFFUSE, lightdif);
+ glLightfv(light, GL_SPECULAR, lightspe);
+ glLightf (light, GL_CONSTANT_ATTENUATION, 0.00);
+ glLightf (light, GL_LINEAR_ATTENUATION, 0.00);
+ glLightf (light, GL_QUADRATIC_ATTENUATION,
+ w->is_aquatic ? 0.25 : 0.12);
+ glEnable (light);
+ succp = true;
+ }
+ } else {
+ glDisable(light);
+ }
+ return succp;
+ }
+
+ int PortRenderSpermBulb(Port_t *p, World_t *w, Sperm_t *s)
+ {
+ int faces = 0;
+ if(s->lit) /* render the light and glowing aura around */
+ {
+ Segment_t bulb; /* position needs to be set */
+ Coord_t bulbdir;
+ GLboolean was_lit;
+ /**/ glPushMatrix();
+ glGetBooleanv(GL_LIGHTING, &was_lit);
+ SpermLightDetails(s, &bulb.position, &bulbdir);
+
+ if(w->light_level < s->bright_suppress)
+ {
+ if(was_lit) glDisable(GL_LIGHTING);
+ glDisable(GL_TEXTURE);
+ glEnable(GL_BLEND);
+ glDepthMask(GL_FALSE);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE);
+ }
+ /* the light */
+ {
+ float scale0 = 0.25;
+ float rgb = 0.4; /* default if suppressed */
+ /* the aura shells */
+ if(w->light_level < s->bright_suppress)
+ {
+ float scalemax = 12 - (12 * w->light_level);
+ float scale;
+ float alpha = w->is_aquatic ? 0.020 : 0.015;
+ /* float alpha_adjust = pow((1 - w->light_level), 3.0); */
+ /* alpha *= alpha_adjust; */
+ alpha += (double)(1 - w->light_level) / 1000.0;
+ /* Cerr("%f\n", alpha_adjust);
+ */
+ for(scale = scale0 ; scale <= scalemax ; scale *= 1.1)
+ {
+ if(w->is_aquatic)
+ glColor4f(0.7, 0.8, 1.0, alpha);
+ else
+ glColor4f(1, 1, 1, alpha);
+ faces += PortRenderSegment(p, w, &bulb,
+ s->head_radius * scale, 9);
+ }
+ glDepthMask(GL_TRUE);
+ glDisable(GL_BLEND);
+ glEnable(GL_TEXTURE);
+ if(was_lit) glEnable(GL_LIGHTING);
+ rgb = 1.0;
+ }
+ /* the bulb */
+ {
+ static GLfloat bulb_glow[4] = { 1, 1, 1, 1 };
+ static GLfloat no_glow[4] = { 0, 0, 0, 1 };
+ if(w->light_level < s->bright_suppress)
+ glMaterialfv(GL_FRONT, GL_EMISSION, &bulb_glow[0]);
+ glColor4f(rgb, rgb, rgb, 1.00);
+ faces += PortRenderSegment(p, w, &bulb,
+ s->head_radius * scale0, 0);
+ if(w->light_level < s->bright_suppress)
+ glMaterialfv(GL_FRONT, GL_EMISSION, &no_glow[0]);
+ }
+ }
+ /* the post */
+ {
+ Coord_t *bulbpos = &bulb.position;
+ Coord_t *headpos = &(s->segments->position);
+ glBegin(GL_LINES);
+ glColor3f(sqrt(fabs(headpos->x) / s->displacement_limit),
+ sqrt(fabs(headpos->y) / s->displacement_limit),
+ sqrt(fabs(headpos->z) / s->displacement_limit));
+ glVertex3f(headpos->x, headpos->y, headpos->z);
+ glColor3f(1, 1, 1);
+ glVertex3f(bulbpos->x, bulbpos->y, bulbpos->z);
+ glEnd();
+ faces += 1;
+ }
+ /**/ glPopMatrix();
+ }
+ return faces;
+ }
+
+ int PortRenderSperm(Port_t *p, World_t *w, Sperm_t *s)
+ {
+ int faces = 0;
+ Segment_t *seg = 0;
+ Segment_t *prior = 0;
+ glPushMatrix();
+ faces += PortRenderSpermBulb(p, w, s);
+ {
+ double fract = 1.0;
+ double fract_final = 0.4; /* sz of tail end as ratio vs tail begin */
+ double fract_delta = pow(fract_final, (1/(double)s->length_target));
+
+ for(seg = s->segments ; seg ; prior = seg, seg = seg->next)
+ {
+ Coord_t *here = &(seg->position);
+
+ glColor3f(sqrt(fabs(here->x) / s->displacement_limit),
+ sqrt(fabs(here->y) / s->displacement_limit),
+ sqrt(fabs(here->z) / s->displacement_limit));
+ if(seg == s->segments) /* first one, the head */
+ {
+ /* set the texture for the head, when implemented */
+ faces += PortRenderSegment(p, w, seg, s->head_radius, 10);
+ /* set the texture for the tail, when implemented */
+ } else {
+ #if 0
+ glBegin(GL_LINES);
+ glVertex3f(prior->x, prior->y, prior->z),
+ glVertex3f(here.x, here.y, here.z),
+ glEnd();
+ #endif
+ faces += PortRenderSegment(p, w, seg,
+ s->tail_radius * fract, 4);
+ }
+ fract *= fract_delta;
+ }
+ }
+ glPopMatrix();
+ return faces;
+ }
+
+ int PortRenderSegment(Port_t *p, World_t *w, Segment_t *s, float scale, int detail)
+ {
+ int faces = 0;
+ if( ! detail) detail = 6;
+ glPushMatrix();
+ glTranslatef(s->position.x, s->position.y, s->position.z);
+ glScalef(scale, scale, scale);
+ faces += Sphere(detail, 0); /* no 2nd arg defaults to outer faces */
+ glPopMatrix();
+ return faces;
+ }
+
+ int PortRenderWorld(Port_t *p, World_t *w)
+ { /* 0 = dawn, 0.25 = noon...*/
+ int faces = 0;
+ glPushMatrix();
+ glScalef(w->sky_radius, w->sky_radius, w->sky_radius);
+ faces += PortRenderSky(p, w);
+ glPopMatrix();
+
+ {
+ GLfloat gold[] = { 1, 1, 0, 1 };
+ GLfloat cyan[] = { 0, 1, 1, 1 };
+ GLfloat mat_shininess[] = { 60.0 };
+ glMaterialfv(GL_FRONT, GL_SPECULAR, gold);
+ glMaterialfv(GL_FRONT, GL_SHININESS, mat_shininess);
+ glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, cyan);
+ }
+
+ if(w->population > 0)
+ {
+ int lights_used = -1;
+ Sperm_t *s = 0;
+ for(s = w->swarm ; s ; s = s->next)
+ {
+ /* was a light-carrying, un-light-suppressed critter */
+ /* successful in getting an OpenGL light (of the set) to use? */
+ if(PortRenderSpermLight(p, w, s,
+ WorldLightForIndex(w, lights_used+1)))
+ {
+ ++lights_used;
+ if(lights_used >= WorldLightCount(w))
+ {
+ static bool warned = false;
+ if( ! warned) {
+ Cerr(":%s: too many lights used (%d) max is %d\n",
+ __FUNCTION__,
+ lights_used, WorldLightCount(w));
+ warned = true;
+ }
+ break;
+ }
+ }
+ }
+
+ { /* now render the whole set normally */
+ int light;
+ /* static Texture tex("texture/ice.rgb"); */
+ /* tex.enable(GL_MODULATE); */
+ for(s = w->swarm ; s ; s = s->next)
+ faces += PortRenderSperm(p, w, s);
+ /* tex.disable(); */
+ /* and disable whichever lights we used */
+ for(light = 1 ; light <= lights_used ; ++light)
+ glDisable(WorldLightForIndex(w, light));
+ }
+
+ }
+ return faces;
+ }
+
+ int PortRender(Port_t *p, World_t *w)
+ {
+ int faces = 0;
+
+ { /* statistics: pre-render stage */
+ p->frame_prior_t0 = p->frame_this_t0; /* old "this" info to "prior" */
+ p->frame_prior_t1 = p->frame_this_t1; /* old "this" info to "prior" */
+ p->frame_this_t0 = TimeAsDouble();
+ if( ! p->frame_first_t0)
+ p->frame_first_t0 = p->frame_this_t0; /* potential div-by-0 risk */
+ }
+
+ glDrawBuffer(GL_BACK);
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+ PortViewpoint(p);
+
+ DefaultMaterial();
+
+ glPushMatrix();
+
+ if(w->is_aquatic)
+ {
+ GLfloat fog[4] = { 0.1 * w->light_level,
+ 0.1 * w->light_level,
+ 0.3 * w->light_level, 1.0 };
+ glEnable(GL_FOG);
+ glFogi(GL_FOG_MODE, GL_LINEAR);
+ glFogf(GL_FOG_START, 1.0);
+ glFogf(GL_FOG_END, 30 + (0.9 * w->sky_radius)); /* 30: PortViewpoint */
+ glFogfv(GL_FOG_COLOR, fog);
+ } else {
+ glDisable(GL_FOG);
+ }
+
+ faces += PortRenderWorld(p, w);
+ /* printf("faces: %d\n", faces);
+ */
+ glPopMatrix();
+
+ glFlush();
+ glXSwapBuffers(p->dpy, p->window);
+
+ { /* statistics: post-render stage */
+ ++(p->frames_drawn);
+ p->frame_this_t1 = TimeAsDouble();
+ }
+
+ return faces;
+ }
+
+ /* - - - - - - - - - - - - - - - - - - - - - - - - - - Functions - Coord */
+
+ float CoordAmplitude(Coord_t *c)
+ {
+ return sqrt( c->x * c->x
+ + c->y * c->y
+ + c->z * c->z);
+ }
+
+ void CoordSetAmplitude(Coord_t *c, float amplitude_desired)
+ {
+ float len = CoordAmplitude(c);
+ if(len > 0) {
+ float scale = amplitude_desired / len;
+ c->x *= scale;
+ c->y *= scale;
+ c->z *= scale;
+ } else {
+ c->x = 1;
+ c->y = 0;
+ c->z = 0;
+ }
+ }
+
+ void CoordUnitRandom(Coord_t *c) /* convert to a random amplitude 1 vector */
+ {
+ /* r^2 = x^2 + y^2 + z^2 = 1 -- looks linear for the squares */
+ double xsq = RandomDouble(0.1, 1.0); /* avoid zeros */
+ double ysq = RandomDouble(0.1, 1.0);
+ double zsq = RandomDouble(0.1, 1.0);
+ double amplitude = sqrt(xsq + ysq + zsq); /* why we avoided zeros */
+ c->x = sqrt(xsq) * (Random(0,1) ? 1 : -1);
+ c->y = sqrt(ysq) * (Random(0,1) ? 1 : -1);
+ c->z = sqrt(zsq) * (Random(0,1) ? 1 : -1);
+ c->x /= amplitude;
+ c->y /= amplitude;
+ c->z /= amplitude;
+ }
+
+ void CoordInvert(Coord_t *c)
+ {
+ c->x *= -1;
+ c->y *= -1;
+ c->z *= -1;
+ }
+
+ void CoordPlusEquals(Coord_t *accum, const Coord_t *addthis)
+ {
+ if(accum && addthis)
+ {
+ accum->x += addthis->x;
+ accum->y += addthis->y;
+ accum->z += addthis->z;
+ } else
+ Cerr(":%s: error - args of (%#lx, %#lx) not both valid\n",
+ __FUNCTION__,
+ accum, addthis);
+ }
+
+ void CoordMinusEquals(Coord_t *accum, const Coord_t *subthis)
+ {
+ if(accum && subthis)
+ {
+ accum->x -= subthis->x;
+ accum->y -= subthis->y;
+ accum->z -= subthis->z;
+ } else
+ Cerr(":%s: error - args of (%#lx, %#lx) not both valid\n",
+ __FUNCTION__,
+ accum, subthis);
+ }
+
+ /* - - - - - - - - - - - - - - - - - - - - - - - - - - Functions - Segment */
+
+ Segment_t *SegmentNew(Coord_t *pos)
+ {
+ Segment_t *hi = (Segment_t*)calloc(1, sizeof *hi);
+ if(hi)
+ {
+ hi->position = *pos;
+ } else Cerr(":%s: calloc failed - %s\n", __FUNCTION__, strerror(errno));
+ return hi;
+ }
+
+ Segment_t *SegmentExtract(Segment_t *extractee) /* return a list neighbor */
+ {
+ Segment_t *prev = extractee->prev;
+ Segment_t *next = extractee->next;
+ if(prev) prev->next = next;
+ if(next) next->prev = prev;
+ extractee->prev = extractee->next = 0;
+ return next ? next : prev;
+ }
+
+ void SegmentDeleteAll(Segment_t **doomed_addr) /* eradicates neighbors too */
+ {
+ if(doomed_addr && *doomed_addr)
+ {
+ Segment_t *doomed = *doomed_addr;
+ Segment_t *prev = doomed->prev;
+ Segment_t *next = doomed->next;
+ /* disconnect the 2 sublists and recurse */
+ if(prev)
+ prev->next = 0, SegmentDeleteAll(&prev);
+ if(next)
+ next->prev = 0, SegmentDeleteAll(&next);
+ free(doomed);
+ *doomed_addr = 0;
+ } else Cerr(":: called with null parm or parm target\n", __FUNCTION__);
+ }
+
+ void SegmentSetPosition(Segment_t *s, Coord_t *pos)
+ {
+ s->position = *pos; /* struct assign */
+ }
+
+ /* - - - - - - - - - - - - - - - - - - - - - - - - - - Functions - Sperm */
+
+ Sperm_t *SpermNew(bool is_variable)
+ {
+ Sperm_t *hi = (Sperm_t*)calloc(1, sizeof *hi);
+ if(hi)
+ {
+ hi->length = 0;
+ hi->length_target = is_variable ? Random(10, 30) : 20;
+ hi->velocity_limit = is_variable ? RandomDouble(0.04, 0.08) : 0.07;
+ hi->displacement_limit = 10;
+ hi->acceleration_limit = is_variable ? RandomDouble(0.02, 0.04) : 0.03;
+ hi->head_radius = is_variable ? RandomDouble(0.18, 0.25) : 0.2;
+ hi->tail_radius = is_variable ? RandomDouble(0.08, 0.15) : 0.15;
+ hi->lit = false;
+ hi->bright_suppress = RandomDouble(0.3, 0.55);
+ CoordUnitRandom(&(hi->velocity));
+ CoordSetAmplitude(&(hi->velocity), hi->velocity_limit);
+ } else Cerr(":%s: calloc failed - %s\n", __FUNCTION__, strerror(errno));
+ SpermLonger(hi);
+ return hi;
+ }
+
+ void SpermDelete(Sperm_t **doomed_addr)
+ {
+ if(doomed_addr)
+ {
+ Sperm_t *doomed = *doomed_addr;
+ if(doomed)
+ {
+ if(doomed->segments)
+ SegmentDeleteAll(&(doomed->segments));
+ if(doomed->next)
+ SpermDelete(&(doomed->next));
+ free(doomed);
+ *doomed_addr = 0;
+ } else Cerr(":%s: called on null Sperm_t via addr %#lx\n",
+ __FUNCTION__, (unsigned long int)doomed);
+ } else Cerr(":%s: called with null parm or parm target\n", __FUNCTION__);
+ }
+
+ unsigned int SpermLength(Sperm_t *s)
+ {
+ return s->length;
+ }
+
+ void SpermLonger(Sperm_t *s) /* lengthen by one segment, the new head */
+ {
+ static Coord_t origin = {0,0,0};
+ Segment_t *newseg = 0;
+ if(SpermLength(s) == 0)
+ newseg = SegmentNew(&origin);
+ else {
+ Segment_t *oldhead = s->segments;
+ Coord_t new_position = oldhead->position; /* struct copy */
+ CoordPlusEquals(&new_position, &(s->velocity));
+ newseg = SegmentNew(&new_position);
+ }
+ if(s->segments) s->segments->prev = newseg; /* point head's prev to new */
+ newseg->next = s->segments; /* push newseg onto head of sperm`s segments */
+ s->segments = newseg;
+ if( ! s->segment_last) s->segment_last = newseg; /* insure tail ptr */
+ ++(s->length);
+ }
+
+ void SpermShorter(Sperm_t *s)
+ {
+ if(s->segment_last) /* don`t bother if no tail is recorded */
+ {
+ Segment_t *doomed = s->segment_last;
+ if(s->segments == doomed)
+ s->segments = doomed->next;
+ s->segment_last = doomed->prev;
+ SegmentExtract(doomed);/* automatically does neighbor next/prevs */
+ SegmentDeleteAll(&doomed); /* tail remains, since doomed`s extracted */
+ --(s->length);
+ }
+ }
+
+ void SpermUpdate(Sperm_t *s)
+ {
+ int length = SpermLength(s);
+ if(length > 0)
+ {
+ Coord_t acceleration; /* jitter the velocity */
+ Coord_t newpos; /* hypothetical next position */
+ CoordUnitRandom(&acceleration);
+ CoordSetAmplitude(&acceleration, s->acceleration_limit);
+ CoordPlusEquals(&(s->velocity), &acceleration);
+ CoordSetAmplitude(&(s->velocity), s->velocity_limit);
+ newpos = s->segments->position;
+ CoordPlusEquals(&newpos, &(s->velocity));
+ if(CoordAmplitude(&newpos) > s->displacement_limit)
+ CoordInvert(&(s->velocity));
+
+ SpermLonger(s);
+ if(length > s->length_target)
+ SpermShorter(s);
+ }
+ }
+
+ bool SpermLightDetails(Sperm_t *s, Coord_t *pos, Coord_t *dir)
+ {
+ bool succp = false;
+ if(s && pos && dir)
+ {
+ Coord_t *head = 0;
+ Coord_t *tail = 0;
+ if(s->segments) head = &(s->segments->position);
+ if(s->segment_last) tail = &(s->segment_last->position);
+ if(head && tail)
+ {
+ Coord_t bulb = *head; /* struct assign */
+ CoordMinusEquals(&bulb, tail); /* now this is the direction */
+ *dir = bulb;
+ CoordPlusEquals(&bulb, head); /* now it`s the bulb`s position */
+ *pos = bulb;
+ succp = true;
+ }
+ } else Cerr(":%s: parms (%#lx, %#lx, %#lx) not all valid\n",
+ __FUNCTION__,
+ (unsigned long int)s,
+ (unsigned long int)pos, (unsigned long int)dir);
+ return succp;
+ }
+
+ /* - - - - - - - - - - - - - - - - - - - - - - - - - - Functions - World */
+
+ void WorldOutF(World_t *w, FILE *out)
+ {
+ fprintf(out, "World %#lx\n", (unsigned long int)w);
+ fprintf(out, " sky radius %f\n", w->sky_radius);
+ fprintf(out, " swarm %#lx, population %d (out of %d desired)\n",
+ (unsigned long int)w->swarm, w->population, w->desired_count);
+ fprintf(out, " lights %d (out of %d desired)\n",
+ WorldLightCount(w), w->desired_lit_count);
+ fprintf(out, " light level %f, min %f, max %f\n",
+ w->light_level, w->light_min, w->light_max);
+ fprintf(out, " night bias %f\n", w->night_bias);
+ fprintf(out, " seconds in day %ld, timebase %ld, cycle point %f",
+ w->seconds_in_day, w->timebase, w->cycle_point);
+ {
+ enum { bufmax = 64 };
+ char buf[bufmax + 1];
+ struct tm *tv = localtime((time_t*)&(w->timebase));
+ if(0 < strftime(&buf[0], bufmax, "%Y-%m-%d %H:%M:%S %Z", tv))
+ fprintf(out, " (%s)", &buf[0]);
+ }
+ fprintf(out, "\n");
+ }
+
+ World_t *WorldNew(void)
+ {
+ World_t *hi = (World_t*)calloc(1, sizeof *hi);
+ if(hi)
+ {
+ hi->desired_count = 100;
+ hi->sky_radius = 20;
+ hi->desired_lit_count = 7;
+ hi->light_min = 0; /* redundant, but pairs with light_max */
+ hi->light_max = 1.0;
+ hi->night_bias = 0.50;
+ hi->seconds_in_day = 60;
+ } else Cerr(":%s: calloc failed - %s\n", __FUNCTION__, strerror(errno));
+ return hi;
+ }
+
+ void WorldDelete(World_t **doomed_addr)
+ {
+ if(doomed_addr && *doomed_addr)
+ {
+ free(*doomed_addr);
+ *doomed_addr = 0;
+ }
+ }
+
+ unsigned int WorldPopulation(World_t *w)
+ {
+ unsigned int population = 0;
+ Sperm_t *s = 0;
+ for(s = w->swarm ; s ; s = s->next)
+ ++population;
+ return population;
+ }
+
+ int WorldLightCount(World_t *w)
+ {
+ static int count = 0;
+ int i;
+ if(0 == count)
+ {
+ for(i = 0 ; i < 1024 ; ++i) /* 1024 to avoid runaway loop */
+ if(WorldLightForIndex(w, i))
+ ++count;
+ else
+ break;
+ }
+ return count;
+ }
+
+ GLenum WorldLightForIndex(World_t *w, int index)
+ {
+ GLenum result = GL_NONE;
+ static const GLenum lights[] = {
+ GL_LIGHT1, GL_LIGHT2, GL_LIGHT3, GL_LIGHT4,
+ GL_LIGHT5, GL_LIGHT6, GL_LIGHT7 /* these numbers are all offset by 1 */
+ };
+ enum { lightcount = sizeof lights / sizeof lights[0] };
+ if(index < lightcount)
+ result = lights[index];
+ return result;
+ }
+
+ int WorldLitCount(World_t *w)
+ {
+ int count = 0;
+ Sperm_t *s = 0;
+ for(s = w->swarm ; s ; s = s->next)
+ if(s->lit)
+ ++count;
+ return count;
+ }
+
+ void WorldUpdate(World_t *w)
+ {
+ if(w->population < w->desired_count) {
+ Sperm_t *wiggler = SpermNew(w->is_sperm_variable);
+ if(wiggler)
+ {
+ if(WorldLitCount(w) < w->desired_lit_count)
+ wiggler->lit = true;
+ wiggler->next = w->swarm, w->swarm = wiggler;
+ ++(w->population);
+ }
+ } else if(w->population > w->desired_count) {
+ if(w->swarm) /* we`d better have a swarm to reduce */
+ {
+ Sperm_t *doomed = 0;
+ Sperm_t *s = 0, *prior = 0; /* look for an unlit one 1st */
+ for(s = w->swarm ; s ; prior = s, s = s->next)
+ {
+ if( ! s->lit) /* ah! move it to the head of w->swarm */
+ {
+ if(prior) /* if not, it -is- the head; nothing to do */
+ {
+ prior->next = s->next;
+ s->next = w->swarm;
+ w->swarm = s;
+ }
+ break;
+ }
+ }
+ /* if we didn`t find a better candidate, the head dies anyway */
+ doomed = w->swarm;
+ w->swarm = doomed->next;
+ doomed->next = 0;
+ SpermDelete(&doomed);
+ --(w->population);
+ }
+ } else { /* population is at target_pop, but are lights at target? */
+ Sperm_t *s = 0;
+ int lit = WorldLitCount(w);
+ if(lit < w->desired_lit_count)
+ for(s = w->swarm ; s ; s = s->next)
+ {
+ if( ! s->lit)
+ s->lit = true, ++lit;
+ if(lit == w->desired_lit_count)
+ break;
+ }
+ }
+ if(WorldLitCount(w) > w->desired_lit_count)
+ {
+ Sperm_t *s = 0;
+ int lit = WorldLitCount(w);
+ for(s = w->swarm ; s ; s = s->next)
+ {
+ if(s->lit)
+ {
+ s->lit = false;
+ --lit;
+ }
+ if(lit <= w->desired_lit_count)
+ break;
+ }
+ }
+
+ { /* compute current light level, including effect of night_bias */
+ /* cycle point 0 = dawn, 0.25 = noon, etc. */
+ unsigned long int seconds;
+ unsigned long int microseconds = MicroAndSeconds(&seconds);
+ unsigned long int whole_sec_in_cycle = ((seconds - w->timebase)
+ % w->seconds_in_day);
+ double sec_in_cycle = (double)whole_sec_in_cycle + (microseconds
+ / 1000000.0);
+ double light_range = w->light_max - w->light_min; /* [0,1] */
+ w->cycle_point = sec_in_cycle / w->seconds_in_day; /* [0,1) */
+ w->light_level = ((sin(w->cycle_point *2*M_PI) + 1.0) / 2.0) /*[0,1]*/
+ * light_range + w->light_min;
+ }
+
+ {
+ Sperm_t *s = 0;
+ Sperm_t *last = 0;
+ for(s = w->swarm ; s ; last = s, s = (s ? s->next : w->swarm))
+ {
+ /* do the normal update */
+ SpermUpdate(s);
+ if(w->is_strayer_hostile)
+ {
+ /* hmm, what happens if we delete straying ones? */
+ if(s->segments
+ && (CoordAmplitude(&s->segments->position)
+ > (0.95 * s->displacement_limit)))
+ {
+ Sperm_t *doomed = s;
+ if(last)
+ {
+ last->next = doomed->next; /* connect around doomed */
+ s = last; /* don`t use use s until next iteration */
+ }
+ if(w->swarm == doomed)
+ w->swarm = doomed->next; /* insure valid head */
+ doomed->next = 0;
+ SpermDelete(&doomed);
+ --(w->population);
+ }
+ }
+ /* NOTE: (s) may be off swarm's end, so don`t add code below */
+ }
+ }
+ }
+
+ /* - - - - - - - - - - - - - - - - - - - - - - - - - - Functions - X/GL mgmt */
+
+ char *MakeTitleString(void)
+ {
+ char *result = 0;
+ static char string[128] = "GLSperm: from the XScreenSaver ";
+ /* example of screensaver_id:
+ * "@(#)xscreensaver 4.03 (30-Apr-2002), by Jamie Zawinski (jwz@jwz.org)"
+ * we want -> +--+ +---------+
+ * for "GLSperm: from the XScreenSaver 4.03 distribution (30-Apr-2002)"
+ */
+ char *string_to_parse = strdup(screensaver_id);
+ char *version = 0;
+ char *datever = 0; /* date of version */
+ char *paren = 0;
+ char *space = 0; /* er 4.03 (30-Apr-2002), by J */
+ if( ! string_to_parse)
+ {
+ Cerr(":%s:strdup failed - %s\n", __FUNCTION__, strerror(errno));
+ return result;
+ }
+ if((version = strchr(string_to_parse, ' ')))/* v */
+ { /* | */
+ if((paren = strchr(++version, ')'))) /* v c */
+ { /* | | */
+ paren[0] = '\0'; /* v 0 */
+ if((space = strchr(version, ' ')))/* v s c */
+ {
+ datever = space + 2; /* v s d----------0 */
+ *space = '\0'; /* v---0 d----------0 */
+ strcat(&string[0], version);
+ strcat(&string[0], " distribution (");
+ strcat(&string[0], datever);
+ strcat(&string[0], ")");
+ result = &string[0];
+ } else Cerr(":%s: problem finding ' ' in \"%s\"\n",
+ __FUNCTION__, version);
+ } else Cerr(":%s: problem finding ')' in \"%s\"\n",
+ __FUNCTION__, version);
+ } else Cerr(":%s: problem finding ' ' in \"%s\"\n",
+ __FUNCTION__, screensaver_id);
+ free(string_to_parse);
+ return result;
+ }
+
+ /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Main */
+
+ void VersionOut(FILE *out)
+ {
+ int veri = 0;
+ fprintf(out, "Version information on %s\n", progname);
+ for(veri = 0 ; Version[veri] ; ++veri)
+ fprintf(out, " %s\n", Version[veri]);
+ }
+
+ void UsageOut(FILE *out)
+ {
+ fprintf
+ (out, "%s: %s", progname,
+ " [-h] | [