X-Git-Url: http://git.hungrycats.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=hacks%2Fglx%2Fglforestfire.c;fp=hacks%2Fglx%2Fglforestfire.c;h=dee6a56670c86a7027c41cc82d8a555110af54ea;hb=a94197e76a5dea5cb60542840809d6c20d0abbf3;hp=0000000000000000000000000000000000000000;hpb=8eb2873d7054e705c4e83f22d18c40946a9e2529;p=xscreensaver diff --git a/hacks/glx/glforestfire.c b/hacks/glx/glforestfire.c new file mode 100644 index 00000000..dee6a566 --- /dev/null +++ b/hacks/glx/glforestfire.c @@ -0,0 +1,1124 @@ +/* -*- Mode: C; tab-width: 4 -*- */ +/* fire --- 3D fire or rain landscape */ + +#if !defined( lint ) && !defined( SABER ) +static const char sccsid[] = "@(#)fire.c 5.02 2001/09/26 xlockmore"; +#endif + +/* Copyright (c) E. Lassauge, 2001. */ + +/* + * 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. + * + * The original code for this mode was written by David Bucciarelli + * (tech.hmw@plus.it) and could be found in the demo package + * of Mesa (Mesa-3.2/3Dfx/demos/). This mode is the result of the merge of + * two of the David's demos (fire and rain). + * + * Eric Lassauge (October-10-2000) + * http://lassauge.free.fr/linux.html + * + * REVISION HISTORY: + * + * E.Lassauge - 26-Sep-2001: + * - add wander option and code + * - cleanups for xscreensaver + * + * E.Lassauge - 09-Mar-2001: + * - get rid of my framerate options to use showfps + * + * E.Lassauge - 12-Jan-2001: + * - add rain particules, selected if count=0 (no fire means rain !) + * + * E.Lassauge - 28-Nov-2000: + * - modified release part to add freeing of GL objects + * + * E.Lassauge - 14-Nov-2000: + * - use new common xpm_to_ximage function + * + * E.Lassauge - 25-Oct-2000: + * - add the trees (with a new resource '-trees') + * - corrected handling of color (textured vs untextured) + * - corrected handling of endiannes for the xpm files + * - inverted ground pixmap file + * - use malloc-ed tree array + * + * TSchmidt - 23-Oct-2000: + * - added size option like used in sproingies mode + * + * E.Lassauge - 13-Oct-2000: + * - when trackmouse and window is iconified (login screen): stop tracking + * - add pure GLX handling of framerate display (erased GLUT stuff) + * - made count a per screen variable and update it only if framemode + * - changes for no_texture an wireframe modes + * - change no_texture color for the ground + * - add freeing of texture image + * - misc comments and little tweakings + * + * TODO: + * - perhaps use a user supplied xpm for ground image (or a whatever image + * file using ImageMagick ?) + * - random number of trees ? change trees at change_fire ? + * - fix wireframe mode: it's too CPU intensive. + * - look how we can get the Wheel events (Button4&5). + */ + + +#ifdef STANDALONE /* xscreensaver mode */ +#define PROGCLASS "Fire" +#define HACK_INIT init_fire +#define HACK_DRAW draw_fire +#define HACK_RESHAPE reshape_fire +#define fire_opts xlockmore_opts +#define DEFAULTS "*delay: 10000 \n" \ + "*count: 800 \n" \ + "*size: 0 \n" \ + "*trees: 5 \n" \ + "*showFPS: False \n" \ + "*trackmouse: False \n" \ + "*wander: True \n" \ + "*wireframe: False \n" \ + "*fog: False \n" \ + "*shadows: True \n" \ + "*texture: True \n" + +#define MODE_fire +#include "xlockmore.h" /* from the xscreensaver distribution */ +#else /* !STANDALONE */ +#include "xlock.h" /* from the xlockmore distribution */ +#include "visgl.h" +#endif /* !STANDALONE */ + +#ifdef MODE_fire + +#define MINSIZE 32 + +#include +#include +#include + +#if defined( USE_XPM ) || defined( USE_XPMINC ) || defined( HAVE_XPM ) +/* USE_XPM & USE_XPMINC in xlock mode ; HAVE_XPM in xscreensaver mode */ +#include "xpm-ximage.h" +#define I_HAVE_XPM + +#ifdef STANDALONE +#include "../images/ground.xpm" +#include "../images/tree.xpm" +#else /* !STANDALONE */ +#include "pixmaps/ground.xpm" +#include "pixmaps/tree.xpm" +#endif /* !STANDALONE */ +#endif /* HAVE_XPM */ + +/* vector utility macros */ +#define vinit(a,i,j,k) {\ + (a)[0]=i;\ + (a)[1]=j;\ + (a)[2]=k;\ +} + +#define vinit4(a,i,j,k,w) {\ + (a)[0]=i;\ + (a)[1]=j;\ + (a)[2]=k;\ + (a)[3]=w;\ +} + +#define vadds(a,dt,b) {\ + (a)[0]+=(dt)*(b)[0];\ + (a)[1]+=(dt)*(b)[1];\ + (a)[2]+=(dt)*(b)[2];\ +} + +#define vequ(a,b) {\ + (a)[0]=(b)[0];\ + (a)[1]=(b)[1];\ + (a)[2]=(b)[2];\ +} + +#define vinter(a,dt,b,c) {\ + (a)[0]=(dt)*(b)[0]+(1.0-dt)*(c)[0];\ + (a)[1]=(dt)*(b)[1]+(1.0-dt)*(c)[1];\ + (a)[2]=(dt)*(b)[2]+(1.0-dt)*(c)[2];\ +} + +#define clamp(a) ((a) < 0.0 ? 0.0 : ((a) < 1.0 ? (a) : 1.0)) + +#define vclamp(v) {\ + (v)[0]=clamp((v)[0]);\ + (v)[1]=clamp((v)[1]);\ + (v)[2]=clamp((v)[2]);\ +} + +/* Manage option vars */ +#define DEF_TEXTURE "True" +#define DEF_FOG "False" +#define DEF_SHADOWS "True" +#define DEF_FRAMERATE "False" +#define DEF_TRACKMOUSE "False" +#define DEF_WANDER "True" +#define DEF_TREES "5" +#define MAX_TREES 20 +static Bool do_texture; +static Bool do_fog; +static Bool do_shadows; +static Bool do_trackmouse; +static Bool do_wander; +static int num_trees; +static int frame = 0; +static XFontStruct *mode_font = None; + +static XrmOptionDescRec opts[] = { + {(char *) "-texture", (char *) ".fire.texture", XrmoptionNoArg, (caddr_t) "on"}, + {(char *) "+texture", (char *) ".fire.texture", XrmoptionNoArg, (caddr_t) "off"}, + {(char *) "-fog", (char *) ".fire.fog", XrmoptionNoArg, (caddr_t) "on"}, + {(char *) "+fog", (char *) ".fire.fog", XrmoptionNoArg, (caddr_t) "off"}, + {(char *) "-shadows", (char *) ".fire.shadows", XrmoptionNoArg, (caddr_t) "on"}, + {(char *) "+shadows", (char *) ".fire.shadows", XrmoptionNoArg, (caddr_t) "off"}, + {(char *) "-trackmouse", (char *) ".fire.trackmouse", XrmoptionNoArg, (caddr_t) "on"}, + {(char *) "+trackmouse", (char *) ".fire.trackmouse", XrmoptionNoArg, (caddr_t) "off"}, + {(char *) "-wander", (char *) ".fire.wander", XrmoptionNoArg, (caddr_t) "on"}, + {(char *) "+wander", (char *) ".fire.wander", XrmoptionNoArg, (caddr_t) "off"}, + {(char *) "-trees", (char *) ".fire.trees", XrmoptionSepArg, (caddr_t) NULL}, + +}; + +static argtype vars[] = { + {(caddr_t *) & do_texture, (char *) "texture", (char *) "Texture", (char *) DEF_TEXTURE, t_Bool}, + {(caddr_t *) & do_fog, (char *) "fog", (char *) "Fog", (char *) DEF_FOG, t_Bool}, + {(caddr_t *) & do_shadows, (char *) "shadows", (char *) "Shadows", (char *) DEF_SHADOWS, t_Bool}, + {(caddr_t *) & do_trackmouse, (char *) "trackmouse", (char *) "TrackMouse", (char *) DEF_TRACKMOUSE, t_Bool}, + {(caddr_t *) & do_wander, (char *) "wander", (char *) "Wander", (char *) DEF_WANDER, t_Bool}, + {(caddr_t *) & num_trees, (char *) "trees", (char *) "Trees", (char *) DEF_TREES, t_Int}, +}; + +static OptionStruct desc[] = { + {(char *) "-/+texture", (char *) "turn on/off texturing"}, + {(char *) "-/+fog", (char *) "turn on/off fog"}, + {(char *) "-/+shadows", (char *) "turn on/off shadows"}, + {(char *) "-/+trackmouse", (char *) "turn on/off the tracking of the mouse"}, + {(char *) "-/+wander", (char *) "turn on/off wandering"}, + {(char *) "-trees num", (char *) "number of trees (0 disables)"}, +}; + +ModeSpecOpt fire_opts = + { sizeof opts / sizeof opts[0], opts, sizeof vars / sizeof vars[0], vars, desc }; + +#ifdef USE_MODULES +ModStruct fire_description = + { "fire", "init_fire", "draw_fire", "release_fire", + "draw_fire", "change_fire", (char *) NULL, &fire_opts, + 10000, 800, 1, 400, 64, 1.0, "", + "Shows a 3D fire-like image", 0, NULL +}; +#endif /* USE_MODULES */ + +/* misc defines */ +#define TREEINR 2.5 /* tree min distance */ +#define TREEOUTR 8.0 /* tree max distance */ +#define FRAME 50 /* frame count interval */ +#define DIMP 20.0 /* dimension of ground */ +#define DIMTP 16.0 /* dimension of ground texture */ + +#define RIDCOL 0.4 /* factor for color blending */ + +#define AGRAV -9.8 /* gravity */ + +#define NUMPART 7500 /* rain particles */ + +/* fire particle struct */ +typedef struct { + int age; + float p[3][3]; + float v[3]; + float c[3][4]; +} part; + +/* rain particle struct */ +typedef struct { + float age; + float acc[3]; + float vel[3]; + float pos[3]; + float partLength; + float oldpos[3]; +} rain; + +/* colors */ +static float black[3] = { 0.0, 0.0, 0.0 }; /* shadow color */ +static float partcol1[3] = { 1.0, 0.2, 0.0 }; /* initial color: red-ish */ +static float partcol2[3] = { 1.0, 1.0, 0.0 }; /* blending color: yellow-ish */ +static float fogcolor[4] = { 0.9, 0.9, 1.0, 1.0 }; + +/* ground */ +static float q[4][3] = { + {-DIMP, 0.0, -DIMP}, + {DIMP, 0.0, -DIMP}, + {DIMP, 0.0, DIMP}, + {-DIMP, 0.0, DIMP} +}; + +/* ground texture */ +static float qt[4][2] = { + {-DIMTP, -DIMTP}, + {DIMTP, -DIMTP}, + {DIMTP, DIMTP}, + {-DIMTP, DIMTP} +}; + +/* default values for observer */ +static const float DEF_OBS[3] = { 2.0f, 1.0f, 0.0f }; +#define DEV_V 0.0 +#define DEF_ALPHA -90.0 +#define DEF_BETA 90.0 + +/* tree struct */ +typedef struct { + float x,y,z; +} treestruct; + +/* the mode struct, contains all per screen variables */ +typedef struct { + GLint WIDTH, HEIGHT; /* display dimensions */ + GLXContext *glx_context; + + int np; /* number of fire particles : set it through 'count' resource */ + float eject_r; /* emission radius */ + float dt, maxage, eject_vy, eject_vl; + float ridtri; /* fire particle size */ + Bool shadows; /* misc booleans: set them through specific resources */ + Bool fog; + + part *p; /* fire particles array */ + rain *r; /* rain particles array */ + + XImage *gtexture; /* ground texture image bits */ + XImage *ttexture; /* tree texture image bits */ + GLuint groundid; /* ground texture id: GL world */ + GLuint treeid; /* tree texture id: GL world */ + GLuint fontbase; /* fontbase id: GL world */ + + int num_trees; /* number of trees: set it through 'trees' resource */ + treestruct *treepos; /* trees positions: float treepos[num_trees][3] */ + + float min[3]; /* raining area */ + float max[3]; + + float obs[3]; /* observer coordinates */ + float dir[3]; /* view direction */ + float v; /* observer velocity */ + float alpha; /* observer angles */ + float beta; +} firestruct; + +/* array of firestruct indexed by screen number */ +static firestruct *fire = (firestruct *) NULL; + +/* + *----------------------------------------------------------------------------- + *----------------------------------------------------------------------------- + * Misc funcs. + *----------------------------------------------------------------------------- + *----------------------------------------------------------------------------- + */ + +/* utility function for the rain particles */ +static float gettimerain(void) +{ +#if 0 + /* Oh yeah, *that's* portable! WTF. */ + + static clock_t told=0; + clock_t tnew,ris; + + tnew=clock(); + + ris=tnew-told; + + told=tnew; + + return (ris/(float)CLOCKS_PER_SEC); +#else + return 0; +#endif +} + +/* my RAND */ +static float vrnd(void) +{ + return ((float) LRAND() / (float) MAXRAND); +} + +/* initialise new fire particle */ +static void setnewpart(firestruct * fs, part * p) +{ + float a, vi[3], *c; + + p->age = 0; + + a = vrnd() * M_PI * 2.0; + + vinit(vi, sin(a) * fs->eject_r * vrnd(), 0.15, cos(a) * fs->eject_r * vrnd()); + vinit(p->p[0], vi[0] + vrnd() * fs->ridtri, vi[1] + vrnd() * fs->ridtri, vi[2] + vrnd() * fs->ridtri); + vinit(p->p[1], vi[0] + vrnd() * fs->ridtri, vi[1] + vrnd() * fs->ridtri, vi[2] + vrnd() * fs->ridtri); + vinit(p->p[2], vi[0] + vrnd() * fs->ridtri, vi[1] + vrnd() * fs->ridtri, vi[2] + vrnd() * fs->ridtri); + + vinit(p->v, vi[0] * fs->eject_vl / (fs->eject_r / 2), + vrnd() * fs->eject_vy + fs->eject_vy / 2, + vi[2] * fs->eject_vl / (fs->eject_r / 2)); + + c = partcol1; + + vinit4(p->c[0], c[0] * ((1.0 - RIDCOL) + vrnd() * RIDCOL), + c[1] * ((1.0 - RIDCOL) + vrnd() * RIDCOL), + c[2] * ((1.0 - RIDCOL) + vrnd() * RIDCOL), 1.0); + vinit4(p->c[1], c[0] * ((1.0 - RIDCOL) + vrnd() * RIDCOL), + c[1] * ((1.0 - RIDCOL) + vrnd() * RIDCOL), + c[2] * ((1.0 - RIDCOL) + vrnd() * RIDCOL), 1.0); + vinit4(p->c[2], c[0] * ((1.0 - RIDCOL) + vrnd() * RIDCOL), + c[1] * ((1.0 - RIDCOL) + vrnd() * RIDCOL), + c[2] * ((1.0 - RIDCOL) + vrnd() * RIDCOL), 1.0); +} + +/* initialise new rain particle */ +static void setnewrain(firestruct * fs, rain * r) +{ + r->age=0.0f; + + vinit(r->acc,0.0f,-0.98f,0.0f); + vinit(r->vel,0.0f,0.0f,0.0f); + + r->partLength=0.2f; + + vinit(r->oldpos,fs->min[0]+(fs->max[0]-fs->min[0])*vrnd(), + fs->max[1]+0.2f*fs->max[1]*vrnd(), + fs->min[2]+(fs->max[2]-fs->min[2])*vrnd()); + vequ(r->pos,r->oldpos); + vadds(r->oldpos,-(r->partLength),r->vel); + + r->pos[1]=(fs->max[1]-fs->min[1])*vrnd()+fs->min[1]; + r->oldpos[1]=r->pos[1]-r->partLength*r->vel[1]; +} + +/* set fire particle values */ +static void setpart(firestruct * fs, part * p) +{ + float fact; + + if (p->p[0][1] < 0.1) { + setnewpart(fs, p); + return; + } + + p->v[1] += AGRAV * fs->dt; + + vadds(p->p[0], fs->dt, p->v); + vadds(p->p[1], fs->dt, p->v); + vadds(p->p[2], fs->dt, p->v); + + p->age++; + + if ((p->age) > fs->maxage) { + vequ(p->c[0], partcol2); + vequ(p->c[1], partcol2); + vequ(p->c[2], partcol2); + } else { + fact = 1.0 / fs->maxage; + vadds(p->c[0], fact, partcol2); + vclamp(p->c[0]); + p->c[0][3] = fact * (fs->maxage - p->age); + + vadds(p->c[1], fact, partcol2); + vclamp(p->c[1]); + p->c[1][3] = fact * (fs->maxage - p->age); + + vadds(p->c[2], fact, partcol2); + vclamp(p->c[2]); + p->c[2][3] = fact * (fs->maxage - p->age); + } +} + +/* set rain particle values */ +static void setpartrain(firestruct * fs, rain * r, float dt) +{ + r->age += dt; + + vadds(r->vel,dt,r->acc); + vadds(r->pos,dt,r->vel); + + if(r->pos[0]min[0]) + r->pos[0]=fs->max[0]-(fs->min[0]-r->pos[0]); + if(r->pos[2]min[2]) + r->pos[2]=fs->max[2]-(fs->min[2]-r->pos[2]); + + if(r->pos[0]>fs->max[0]) + r->pos[0]=fs->min[0]+(r->pos[0]-fs->max[0]); + if(r->pos[2]>fs->max[2]) + r->pos[2]=fs->min[2]+(r->pos[2]-fs->max[2]); + + vequ(r->oldpos,r->pos); + vadds(r->oldpos,-(r->partLength),r->vel); + if(r->pos[1]min[1]) + setnewrain(fs, r); +} + +/* draw a tree */ +static void drawtree(float x, float y, float z) +{ + glBegin(GL_QUADS); + glTexCoord2f(0.0,0.0); + glVertex3f(x-1.5,y+0.0,z); + + glTexCoord2f(1.0,0.0); + glVertex3f(x+1.5,y+0.0,z); + + glTexCoord2f(1.0,1.0); + glVertex3f(x+1.5,y+3.0,z); + + glTexCoord2f(0.0,1.0); + glVertex3f(x-1.5,y+3.0,z); + + + glTexCoord2f(0.0,0.0); + glVertex3f(x,y+0.0,z-1.5); + + glTexCoord2f(1.0,0.0); + glVertex3f(x,y+0.0,z+1.5); + + glTexCoord2f(1.0,1.0); + glVertex3f(x,y+3.0,z+1.5); + + glTexCoord2f(0.0,1.0); + glVertex3f(x,y+3.0,z-1.5); + + glEnd(); + +} + +/* calculate observer position : modified only if trackmouse is used */ +static void calcposobs(firestruct * fs) +{ + fs->dir[0] = sin(fs->alpha * M_PI / 180.0); + fs->dir[2] = + cos(fs->alpha * M_PI / 180.0) * sin(fs->beta * M_PI / 180.0); + fs->dir[1] = cos(fs->beta * M_PI / 180.0); + + fs->obs[0] += fs->v * fs->dir[0]; + fs->obs[1] += fs->v * fs->dir[1]; + fs->obs[2] += fs->v * fs->dir[2]; + + if (!fs->np) + { + vinit(fs->min,fs->obs[0]-7.0f,-0.2f,fs->obs[2]-7.0f); + vinit(fs->max,fs->obs[0]+7.0f,8.0f,fs->obs[2]+7.0f); + } +} + +/* track the mouse in a joystick manner : not perfect but it works */ +static void trackmouse(ModeInfo * mi) +{ + firestruct *fs = &fire[MI_SCREEN(mi)]; + /* we keep static values (not per screen) for the mouse stuff: in general you have only one mouse :-> */ + static int max[2] = { 0, 0 }; + static int min[2] = { 0x7fffffff, 0x7fffffff }, center[2]; + Window r, c; + int rx, ry, cx, cy; + unsigned int m; + + (void) XQueryPointer(MI_DISPLAY(mi), MI_WINDOW(mi), + &r, &c, &rx, &ry, &cx, &cy, &m); + + if (max[0] < cx) + max[0] = cx; + if (min[0] > cx) + min[0] = cx; + center[0] = (max[0] + min[0]) / 2; + + if (max[1] < cy) + max[1] = cy; + if (min[1] > cy) + min[1] = cy; + center[1] = (max[1] + min[1]) / 2; + + if (fabs(center[0] - (float) cx) > 0.1 * (max[0] - min[0])) + fs->alpha += 2.5 * (center[0] - (float) cx) / (max[0] - min[0]); + if (fabs(center[1] - (float) cy) > 0.1 * (max[1] - min[1])) + fs->beta += 2.5 * (center[1] - (float) cy) / (max[1] - min[1]); + + /* oops: can't get those buttons */ + if (m & Button4Mask) + fs->v += 0.01; + if (m & Button5Mask) + fs->v -= 0.01; + +} + +/* initialise textures */ +static void inittextures(ModeInfo * mi) +{ + firestruct *fs = &fire[MI_SCREEN(mi)]; + +#if defined( I_HAVE_XPM ) + if (do_texture) { + + glGenTextures(1, &fs->groundid); +#ifdef HAVE_GLBINDTEXTURE + glBindTexture(GL_TEXTURE_2D, fs->groundid); +#endif /* HAVE_GLBINDTEXTURE */ + + if ((fs->gtexture = xpm_to_ximage(MI_DISPLAY(mi), MI_VISUAL(mi), + MI_COLORMAP(mi), ground)) == None) { + (void) fprintf(stderr, "Error reading the ground texture.\n"); + glDeleteTextures(1, &fs->groundid); + do_texture = False; + fs->groundid = 0; + fs->treeid = 0; + return; + } + + glPixelStorei(GL_UNPACK_ALIGNMENT, 4); + clear_gl_error(); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, + fs->gtexture->width, fs->gtexture->height, 0, + GL_RGBA, GL_UNSIGNED_BYTE, fs->gtexture->data); + check_gl_error("texture"); + + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL); + + if (fs->num_trees) + { + glGenTextures(1, &fs->treeid); +#ifdef HAVE_GLBINDTEXTURE + glBindTexture(GL_TEXTURE_2D,fs->treeid); +#endif /* HAVE_GLBINDTEXTURE */ + if ((fs->ttexture = xpm_to_ximage(MI_DISPLAY(mi), MI_VISUAL(mi), + MI_COLORMAP(mi), tree)) == None) { + (void)fprintf(stderr,"Error reading tree texture.\n"); + glDeleteTextures(1, &fs->treeid); + fs->treeid = 0; + fs->num_trees = 0; + return; + } + + clear_gl_error(); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, + fs->ttexture->width, fs->ttexture->height, 0, + GL_RGBA, GL_UNSIGNED_BYTE, fs->ttexture->data); + check_gl_error("texture"); + + glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_REPEAT); + glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_REPEAT); + + glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST); + glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST); + + glTexEnvf(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_MODULATE); + } + } + else + { + fs->groundid = 0; /* default textures */ + fs->treeid = 0; + } +#else /* !I_HAVE_XPM */ + do_texture = False; + fs->groundid = 0; /* default textures */ + fs->treeid = 0; +#endif /* !I_HAVE_XPM */ +} + +/* init tree array and positions */ +static Bool inittree(ModeInfo * mi) +{ + firestruct *fs = &fire[MI_SCREEN(mi)]; + int i; + float dist; + + /* allocate treepos array */ + if ((fs->treepos = (treestruct *) malloc(fs->num_trees * + sizeof(treestruct))) == NULL) { + return False; + } + /* initialise positions */ + for(i=0;inum_trees;i++) + do { + fs->treepos[i].x =vrnd()*TREEOUTR*2.0-TREEOUTR; + fs->treepos[i].y =0.0; + fs->treepos[i].z =vrnd()*TREEOUTR*2.0-TREEOUTR; + dist=sqrt(fs->treepos[i].x *fs->treepos[i].x +fs->treepos[i].z *fs->treepos[i].z ); + } while((distTREEOUTR)); + return True; +} + +/* + *----------------------------------------------------------------------------- + *----------------------------------------------------------------------------- + * GL funcs. + *----------------------------------------------------------------------------- + *----------------------------------------------------------------------------- + */ + +#ifndef STANDALONE +static void Reshape(ModeInfo * mi) +#else +void reshape_fire(ModeInfo * mi, int width, int height) +#endif +{ + + firestruct *fs = &fire[MI_SCREEN(mi)]; + int size = MI_SIZE(mi); + + /* Viewport is specified size if size >= MINSIZE && size < screensize */ + if (size <= 1) { + fs->WIDTH = MI_WIDTH(mi); + fs->HEIGHT = MI_HEIGHT(mi); + } else if (size < MINSIZE) { + fs->WIDTH = MINSIZE; + fs->HEIGHT = MINSIZE; + } else { + fs->WIDTH = (size > MI_WIDTH(mi)) ? MI_WIDTH(mi) : size; + fs->HEIGHT = (size > MI_HEIGHT(mi)) ? MI_HEIGHT(mi) : size; + } + glViewport((MI_WIDTH(mi) - fs->WIDTH) / 2, (MI_HEIGHT(mi) - fs->HEIGHT) / 2, fs->WIDTH, fs->HEIGHT); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + gluPerspective(70.0, fs->WIDTH / (float) fs->HEIGHT, 0.1, 30.0); + + glMatrixMode(GL_MODELVIEW); + +} + +static void DrawFire(ModeInfo * mi) +{ + int j; + firestruct *fs = &fire[MI_SCREEN(mi)]; + Bool wire = MI_IS_WIREFRAME(mi); + + if (do_trackmouse && !MI_IS_ICONIC(mi)) + trackmouse(mi); + + if (do_wander) + { + GLfloat x, y, z; + +# define SINOID(SCALE,SIZE) \ + ((((1 + sin((frame * (SCALE)) / 2 * M_PI)) / 2.0) * (SIZE)) - (SIZE)/2) + + x = SINOID(0.031, 0.85); + y = SINOID(0.017, 0.25); + z = SINOID(0.023, 0.85); + frame++; + fs->obs[0] = x + DEF_OBS[0]; + fs->obs[1] = y + DEF_OBS[1]; + fs->obs[2] = z + DEF_OBS[2]; + fs->dir[1] = y; + fs->dir[2] = z; + } + + glEnable(GL_DEPTH_TEST); + + if (fs->fog) + glEnable(GL_FOG); + else + glDisable(GL_FOG); + + glDepthMask(GL_TRUE); + glClearColor(0.5, 0.5, 0.8, 1.0); /* sky in the distance */ + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + glPushMatrix(); + calcposobs(fs); + gluLookAt(fs->obs[0], fs->obs[1], fs->obs[2], + fs->obs[0] + fs->dir[0], fs->obs[1] + fs->dir[1], + fs->obs[2] + fs->dir[2], 0.0, 1.0, 0.0); + + + glEnable(GL_TEXTURE_2D); + + /* draw ground using the computed texture */ + if (do_texture) { + glColor4f(1.0,1.0,1.0,1.0); /* white to get texture in it's true color */ +#ifdef HAVE_GLBINDTEXTURE + glBindTexture(GL_TEXTURE_2D, fs->groundid); +#endif /* HAVE_GLBINDTEXTURE */ + } + else + glColor4f(0.54, 0.27, 0.07, 1.0); /* untextured ground color */ + glBegin(GL_QUADS); + glTexCoord2fv(qt[0]); + glVertex3fv(q[0]); + glTexCoord2fv(qt[1]); + glVertex3fv(q[1]); + glTexCoord2fv(qt[2]); + glVertex3fv(q[2]); + glTexCoord2fv(qt[3]); + glVertex3fv(q[3]); + glEnd(); + + glAlphaFunc(GL_GEQUAL, 0.9); + if (fs->num_trees) + { + /* here do_texture IS True - and color used is white */ + glEnable(GL_ALPHA_TEST); +#ifdef HAVE_GLBINDTEXTURE + glBindTexture(GL_TEXTURE_2D,fs->treeid); +#endif /* HAVE_GLBINDTEXTURE */ + for(j=0;jnum_trees;j++) + drawtree(fs->treepos[j].x ,fs->treepos[j].y ,fs->treepos[j].z ); + glDisable(GL_ALPHA_TEST); + } + glDisable(GL_TEXTURE_2D); + glDepthMask(GL_FALSE); + + if (fs->shadows) { + /* draw shadows with black color */ + glBegin(wire ? GL_LINE_STRIP : GL_TRIANGLES); + for (j = 0; j < fs->np; j++) { + glColor4f(black[0], black[1], black[2], fs->p[j].c[0][3]); + glVertex3f(fs->p[j].p[0][0], 0.1, fs->p[j].p[0][2]); + + glColor4f(black[0], black[1], black[2], fs->p[j].c[1][3]); + glVertex3f(fs->p[j].p[1][0], 0.1, fs->p[j].p[1][2]); + + glColor4f(black[0], black[1], black[2], fs->p[j].c[2][3]); + glVertex3f(fs->p[j].p[2][0], 0.1, fs->p[j].p[2][2]); + } + glEnd(); + } + + glBegin(wire ? GL_LINE_STRIP : GL_TRIANGLES); + for (j = 0; j < fs->np; j++) { + /* draw particles: colors are computed in setpart */ + glColor4fv(fs->p[j].c[0]); + glVertex3fv(fs->p[j].p[0]); + + glColor4fv(fs->p[j].c[1]); + glVertex3fv(fs->p[j].p[1]); + + glColor4fv(fs->p[j].c[2]); + glVertex3fv(fs->p[j].p[2]); + + setpart(fs, &fs->p[j]); + } + glEnd(); + + /* draw rain particles if no fire particles */ + if (!fs->np) + { + float timeused = gettimerain(); + glDisable(GL_TEXTURE_2D); + glShadeModel(GL_SMOOTH); + glBegin(GL_LINES); + for (j = 0; j < NUMPART; j++) { + glColor4f(0.7f,0.95f,1.0f,0.0f); + glVertex3fv(fs->r[j].oldpos); + glColor4f(0.3f,0.7f,1.0f,1.0f); + glVertex3fv(fs->r[j].pos); + setpartrain(fs, &fs->r[j],timeused); + } + glEnd(); + glShadeModel(GL_FLAT); + } + + glDisable(GL_TEXTURE_2D); + glDisable(GL_ALPHA_TEST); + glDisable(GL_DEPTH_TEST); + glDisable(GL_FOG); + + /* manage framerate display */ + if (MI_IS_FPS(mi)) do_fps (mi); + glPopMatrix(); +} + + +static Bool Init(ModeInfo * mi) +{ + int i; + firestruct *fs = &fire[MI_SCREEN(mi)]; + + /* default settings */ + fs->eject_r = 0.1 + NRAND(10) * 0.03; + fs->dt = 0.015; + fs->eject_vy = 4; + fs->eject_vl = 1; + fs->ridtri = 0.1 + NRAND(10) * 0.005; + fs->maxage = 1.0 / fs->dt; + vinit(fs->obs, DEF_OBS[0], DEF_OBS[1], DEF_OBS[2]); + fs->v = 0.0; + fs->alpha = DEF_ALPHA; + fs->beta = DEF_BETA; + + /* initialise texture stuff */ + if (do_texture) + inittextures(mi); + else + { + fs->ttexture = (XImage*) NULL; + fs->gtexture = (XImage*) NULL; + } + + if (MI_IS_DEBUG(mi)) { + (void) fprintf(stderr, + "%s:\n\tnum_part=%d\n\ttrees=%d\n\tfog=%s\n\tshadows=%s\n\teject_r=%.3f\n\tridtri=%.3f\n", + MI_NAME(mi), + fs->np, + fs->num_trees, + fs->fog ? "on" : "off", + fs->shadows ? "on" : "off", + fs->eject_r, fs->ridtri); + } + + glShadeModel(GL_FLAT); + glEnable(GL_DEPTH_TEST); + + /* makes particles blend with background */ + if (!MI_IS_WIREFRAME(mi)||(!fs->np)) + { + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + } + + /* fog stuff */ + glEnable(GL_FOG); + glFogi(GL_FOG_MODE, GL_EXP); + glFogfv(GL_FOG_COLOR, fogcolor); + glFogf(GL_FOG_DENSITY, 0.03); + glHint(GL_FOG_HINT, GL_NICEST); + + /* initialise particles and trees */ + for (i = 0; i < fs->np; i++) { + setnewpart(fs, &(fs->p[i])); + } + + if (fs->num_trees) + if (!inittree(mi)) { + return False; + } + + /* if no fire particles then initialise rain particles */ + if (!fs->np) + { + vinit(fs->min,-7.0f,-0.2f,-7.0f); + vinit(fs->max,7.0f,8.0f,7.0f); + for (i = 0; i < NUMPART; i++) { + setnewrain(fs, &(fs->r[i])); + } + } + + return True; +} + +/* + *----------------------------------------------------------------------------- + *----------------------------------------------------------------------------- + * Xlock hooks. + *----------------------------------------------------------------------------- + *----------------------------------------------------------------------------- + */ + + +static void +free_fire(firestruct *fs) +{ + if (mode_font != None && fs->fontbase != None) { + glDeleteLists(fs->fontbase, mode_font->max_char_or_byte2 - + mode_font->min_char_or_byte2 + 1); + fs->fontbase = None; + } + + if (fs->p != NULL) { + (void) free((void *) fs->p); + fs->p = (part *) NULL; + } + if (fs->r != NULL) { + (void) free((void *) fs->r); + fs->r = (rain *) NULL; + } + if (fs->treepos != NULL) { + (void) free((void *) fs->treepos); + fs->treepos = (treestruct *) NULL; + } + if (fs->ttexture != None) { + glDeleteTextures(1, &fs->treeid); + XDestroyImage(fs->ttexture); + fs->ttexture = None; + } + if (fs->gtexture != None) { + glDeleteTextures(1, &fs->groundid); + XDestroyImage(fs->gtexture); + fs->gtexture = None; + } +} + +/* + *----------------------------------------------------------------------------- + * Initialize fire. Called each time the window changes. + *----------------------------------------------------------------------------- + */ + +void +init_fire(ModeInfo * mi) +{ + firestruct *fs; + + /* allocate the main fire table if needed */ + if (fire == NULL) { + if ((fire = (firestruct *) calloc(MI_NUM_SCREENS(mi), + sizeof(firestruct))) == NULL) + return; + } + + /* initialise the per screen fire structure */ + fs = &fire[MI_SCREEN(mi)]; + fs->np = MI_COUNT(mi); + fs->fog = do_fog; + fs->shadows = do_shadows; + /* initialise fire particles if any */ + if ((fs->np)&&(fs->p == NULL)) { + if ((fs->p = (part *) calloc(fs->np, sizeof(part))) == NULL) { + free_fire(fs); + return; + } + } + else if (fs->r == NULL) { + /* initialise rain particles if no fire particles */ + if ((fs->r = (rain *) calloc(NUMPART, sizeof(part))) == NULL) { + free_fire(fs); + return; + } + } + + /* check tree number */ + if (do_texture) + fs->num_trees = (num_treesnum_trees = 0; + + /* check wander/trackmouse */ + if (do_trackmouse && do_wander) do_wander = 0; + + /* xlock GL stuff */ + if ((fs->glx_context = init_GL(mi)) != NULL) { + +#ifndef STANDALONE + Reshape(mi); /* xlock mode */ +#else + reshape_fire(mi,MI_WIDTH(mi),MI_HEIGHT(mi)); /* xscreensaver mode */ +#endif + glDrawBuffer(GL_BACK); + if (!Init(mi)) { + free_fire(fs); + return; + } + } else { + MI_CLEARWINDOW(mi); + } +} + +/* + *----------------------------------------------------------------------------- + * Called by the mainline code periodically to update the display. + *----------------------------------------------------------------------------- + */ +void draw_fire(ModeInfo * mi) +{ + firestruct *fs = &fire[MI_SCREEN(mi)]; + + Display *display = MI_DISPLAY(mi); + Window window = MI_WINDOW(mi); + + MI_IS_DRAWN(mi) = True; + + if (!fs->glx_context) + return; + + glXMakeCurrent(display, window, *(fs->glx_context)); + DrawFire(mi); +#ifndef STANDALONE + Reshape(mi); /* xlock mode */ +#else + reshape_fire(mi,MI_WIDTH(mi),MI_HEIGHT(mi)); /* xscreensaver mode */ +#endif + + glFinish(); + glXSwapBuffers(display, window); +} + + +/* + *----------------------------------------------------------------------------- + * The display is being taken away from us. Free up malloc'ed + * memory and X resources that we've alloc'ed. Only called + * once, we must zap everything for every screen. + *----------------------------------------------------------------------------- + */ + +void release_fire(ModeInfo * mi) +{ + if (fire != NULL) { + int screen; + for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++) + free_fire(&fire[screen]); + (void) free((void *) fire); + fire = (firestruct *) NULL; + } + if (mode_font != None) + { + /* only free-ed when there are no more screens used */ + XFreeFont(MI_DISPLAY(mi), mode_font); + mode_font = None; + } + FreeAllGL(mi); +} + +void change_fire(ModeInfo * mi) +{ + firestruct *fs = &fire[MI_SCREEN(mi)]; + + if (!fs->glx_context) + return; + + glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(fs->glx_context)); + + /* if available, randomly change some values */ + if (do_fog) + fs->fog = LRAND() & 1; + if (do_shadows) + fs->shadows = LRAND() & 1; + /* reset observer position */ + frame = 0; + vinit(fs->obs, DEF_OBS[0], DEF_OBS[1], DEF_OBS[2]); + fs->v = 0.0; + /* particle randomisation */ + fs->eject_r = 0.1 + NRAND(10) * 0.03; + fs->ridtri = 0.1 + NRAND(10) * 0.005; + + if (MI_IS_DEBUG(mi)) { + (void) fprintf(stderr, + "%s:\n\tnum_part=%d\n\ttrees=%d\n\tfog=%s\n\tshadows=%s\n\teject_r=%.3f\n\tridtri=%.3f\n", + MI_NAME(mi), + fs->np, + fs->num_trees, + fs->fog ? "on" : "off", + fs->shadows ? "on" : "off", + fs->eject_r, fs->ridtri); + } +} +#endif /* MODE_fire */