/* bg.c - Don Yang (uguu.org) XXX: Need to figure out what to do when aspect ratio is way out of proportion. XXX: Objects don't have shadows. 05/19/03 */ /*@ -evalorder -realcompare @*/ #include"global.h" #include"bg.h" #include"city.h" #include"core.h" #include"forest.h" #include"rocks.h" #include"tilegrid.h" #include"util.h" /* Background grid states. All background functions use this same grid to save memory. Bit fields. Note that rand() doesn't give too many bits of randomness, do not use more than 15 bits without using an alternate pseudo random number generator. bits 0-1: tile size (00 = 1x1, 01 = 2x2, 10 = 3x3, 11 = 4x4) bits 0-9: type bit 10: enable scale (1 = enabled) bit 11: enable shear (1 = enabled) bit 12: scale phase (0 = sin, 1 = cos) bit 13: shear phase (0 = sin, 1 = cos) */ static unsigned int StaticGrid[GRID_XSIZE][GRID_ZSIZE]; static unsigned int *Grid[GRID_SIZE]; static unsigned char StaticGridCoverage[GRID_XSIZE][GRID_ZSIZE]; static unsigned char *GridCoverage[GRID_SIZE]; /* Rain state */ static struct { double sx, sy, sz, st; } Rain[RAINDROP_COUNT]; /* Transform states */ static double TransformCurve, TransformCurveA; /* Display lists */ enum { ObjDaySky, ObjDaySkyG, ObjSun, ObjSunRays, ObjNightSky, ObjNightSkyG, ObjStars0, ObjStars1, ObjStars2, ObjStars3, ObjDarkSky, ObjCount }; static GLuint Obj[ObjCount] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* Background functions */ typedef void (*BGFUNC)(int, int, int, int); static void BG_Day(int width, int height, int left, int right, int g); static void BG_Night(int width, int height, int left, int right, int g); static void BG_Dark(int width, int height, int left, int right); static void BlankBackground(int width, int height, int left, int right); static void CityBackgroundD(int width, int height, int left, int right); static void CityBackgroundN(int width, int height, int left, int right); static void ForestBackgroundD(int width, int height, int left, int right); static void ForestBackgroundN(int width, int height, int left, int right); static void RainBackground(int width, int height, int left, int right); static void TileBackgroundD(int width, int height, int left, int right); static void TileBackgroundN(int width, int height, int left, int right); static BGFUNC BGFunc[VERSE_COUNT] = { /*0*/ BlankBackground, /*1*/ ForestBackgroundD, /*2*/ ForestBackgroundN, /*3*/ TileBackgroundN, /*4*/ CityBackgroundD, /*5*/ CityBackgroundN, /*6*/ RainBackground, /*7*/ ForestBackgroundD, /*8*/ CityBackgroundD, /*9*/ TileBackgroundD }; /* Helper functions */ static void DrawRain(void); static void DrawTileSet(void (*tilefunc)(unsigned int), void (*outlinefunc)(unsigned int), int transform); static void TransformTile(unsigned int cell, int xi, int zi, int zd, int t); static void UpdateGrid(void); /****************************************************************** DrawBG */ void DrawBG(int width, int height, int left, int right, int verse) { double t; /* Create transform curve (almost like sine function) */ t = fmod(CurrentTime, 1.0); TransformCurve = (t < 0.6) ? (t < 0.4) ? (t < 0.1) ? (10.0 * t) : 1.0 : (-10.0 * t + 5.0) : (t > 0.9) ? (10.0 * t - 10.0) : -1.0; TransformCurveA = fabs(TransformCurve); UpdateGrid(); BGFunc[verse](width, height, left, right); } /* DrawBG() */ /****************************************************************** InitBG */ void InitBG(void) { int xi, zi, zdirection; unsigned int c, d, cellsize; /* Initialize grid */ for(xi = 0; xi < GRID_XSIZE; xi++) for(zi=0; zi < GRID_ZSIZE; StaticGrid[xi][zi++] = (unsigned int)rand()); /* Update grid coverage */ memset(StaticGridCoverage, 0, GRID_XSIZE * GRID_ZSIZE); for(xi = 0; xi < GRID_XSIZE; xi++) { for(zdirection = -1; zdirection <= 1; zdirection += 2) { zi = (zdirection == -1) ? (GRID_ZSIZE / 2 - 2) : (GRID_ZSIZE / 2 + 2); for(; zi >= 0 && zi < GRID_ZSIZE; zi += zdirection) { /* Check for collisions at current cell */ if( StaticGridCoverage[xi][zi] != CELL_EMPTY ) continue; cellsize = StaticGrid[xi][zi] & 3; for(c = 1; c <= cellsize; c++) { /* Check for boundary condition */ if( (zi + c * zdirection) < 0 || (zi + c * zdirection) >= GRID_ZSIZE || (xi + c) >= GRID_XSIZE ) break; /* Check for collisions with other oversized tiles */ if( StaticGridCoverage[xi][zi + c * zdirection] != CELL_EMPTY ) break; } /* Update grid cell size */ StaticGrid[xi][zi] = (StaticGrid[xi][zi] & ~3) | (cellsize = c - 1); /* Mark cell coverage (1 = cell used, 2 = original cell) */ for(c = 0; c <= cellsize; c++) { for(d = 0; d <= cellsize; d++) { StaticGridCoverage[xi + c][zi + zdirection * d] = CELL_COVERED; } } StaticGridCoverage[xi][zi] = CELL_ORIG; } } } /* Initialize tilesets */ InitCityTiles(); /* (city.c) */ InitForestTiles(); /* (forest.c) */ InitRockTiles(); /* (rocks.c) */ InitTileGrid(); /* (tilegrid.c) */ /* Initialize rain */ for(d = 0; d < RAINDROP_COUNT; d++) { Rain[d].sx = Random(-SCENE_3D_XSIZE, SCENE_3D_XSIZE); Rain[d].sy = Random(0.0, SCENE_3D_YSIZE); Rain[d].sz = Random(-SCENE_3D_ZSIZE, SCENE_3D_ZSIZE); Rain[d].st = CurrentTime; } } /* InitBG() */ /**************************************************************** UninitBG */ void UninitBG(void) { int i; UninitCityTiles(); /* (city.c) */ UninitForestTiles(); /* (forest.c) */ UninitRockTiles(); /* (rocks.c) */ UninitTileGrid(); /* (tilegrid.c) */ for(i = 0; i < ObjCount; i++) { if( Obj[i] != 0 ) glDeleteLists(Obj[i], 1); } } /* UninitBG() */ /****************************************************************** BG_Day */ static void BG_Day(int width, int height, int left, int right, int g) { static const GLfloat skycolor0[4] = {1.0f, 1.0f, 1.0f, 1.0f}; static const GLfloat skycolor1[4] = {0.2f, 0.2f, 1.0f, 1.0f}; static const GLfloat skycolor_g0[4] = {0.9f, 1.0f, 0.9f, 1.0f}; static const GLfloat skycolor_g1[4] = {0.0f, 0.7f, 0.2f, 1.0f}; static const GLfloat suncolor0[4] = {1.0f, 1.0f, 0.4f, 1.0f}; static const GLfloat suncolor1[4] = {0.9f, 0.9f, 0.8f, 1.0f}; static const GLfloat sunoutline[4] = {0.0f, 0.0f, 0.0f, 0.0f}; static const double sunradius = 0.1; double a; int i, x; SetViewport2D(width, height, left, right); if( g == 0 ) { /* Sky gradient */ if( Obj[ObjDaySky] != 0 ) { glCallList(Obj[ObjDaySky]); } else { glNewList(Obj[ObjDaySky] = glGenLists(1), GL_COMPILE_AND_EXECUTE); glBegin(GL_QUAD_STRIP); glColor4fv(skycolor0); glVertex2d(-SCENE_2D_XSIZE,-1.0); glVertex2d( SCENE_2D_XSIZE,-1.0); glVertex2d(-SCENE_2D_XSIZE, 0.1); glVertex2d( SCENE_2D_XSIZE, 0.1); glColor4fv(skycolor1); glVertex2d(-SCENE_2D_XSIZE, 1.0); glVertex2d( SCENE_2D_XSIZE, 1.0); glEnd(); glEndList(); } } else /* g != 0 */ { /* Sky gradient + ground */ if( Obj[ObjDaySkyG] != 0 ) { glCallList(Obj[ObjDaySkyG]); } else { glNewList(Obj[ObjDaySkyG] = glGenLists(1), GL_COMPILE_AND_EXECUTE); glBegin(GL_QUAD_STRIP); glColor4fv(skycolor_g1); glVertex2d(-SCENE_2D_XSIZE,-1.0); glVertex2d( SCENE_2D_XSIZE,-1.0); glColor4fv(skycolor_g0); glVertex2d(-SCENE_2D_XSIZE, 0.1); glVertex2d( SCENE_2D_XSIZE, 0.1); glEnd(); glBegin(GL_QUAD_STRIP); glColor4fv(skycolor0); glVertex2d(-SCENE_2D_XSIZE, 0.1); glVertex2d( SCENE_2D_XSIZE, 0.1); glColor4fv(skycolor1); glVertex2d(-SCENE_2D_XSIZE, 1.0); glVertex2d( SCENE_2D_XSIZE, 1.0); glEnd(); glEndList(); } } /* Sun */ glTranslated(0.0, 0.7, 0.0); if( Obj[ObjSun] != 0 ) { glCallList(Obj[ObjSun]); } else { glNewList(Obj[ObjSun] = glGenLists(1), GL_COMPILE_AND_EXECUTE); /* Outline */ glShadeModel(GL_FLAT); glBegin(GL_TRIANGLE_FAN); glColor4fv(sunoutline); glVertex2d(0.0, 0.0); for(i = 0; i < 16; i++) { a = (double)i * PI / 8.0; glVertex2d((sunradius + 0.01) * cos(a), (sunradius + 0.01) * sin(a)); } glVertex2d(sunradius + 0.01, 0.0); glEnd(); /* Object */ glShadeModel(GL_SMOOTH); glBegin(GL_TRIANGLE_FAN); glColor4fv(suncolor0); glVertex2d(0.0, 0.0); for(i = 0; i < 16; i++) { a = (double)i * PI / 8.0; if( i == 1 ) glColor4fv(suncolor1); if( i == 6 ) glColor4fv(suncolor0); glVertex2d(sunradius * cos(a), sunradius * sin(a)); } glVertex2d(sunradius, 0.0); glEnd(); glEndList(); } /* Rays */ glRotated(30.0 + 36.0 * (double)(((int)(CurrentTime * 6.0)) % 2), 0.0, 0.0, 1.0); if( Obj[ObjSunRays] != 0 ) { glCallList(Obj[ObjSunRays]); } else { glNewList(Obj[ObjSunRays] = glGenLists(1), GL_COMPILE_AND_EXECUTE); /* Outline */ glShadeModel(GL_FLAT); glBegin(GL_QUADS); glColor4fv(sunoutline); for(i = x = 0; i < 10; i++) { a = (double)i * PI / 5.0; if( (i & 1) == 0 ) { glVertex2d((sunradius + 0.02) * cos(a - 0.21), (sunradius + 0.02) * sin(a - 0.21)); glVertex2d((sunradius + 0.02) * cos(a + 0.21), (sunradius + 0.02) * sin(a + 0.21)); glVertex2d((sunradius + 0.07) * cos(a + 0.21), (sunradius + 0.07) * sin(a + 0.21)); glVertex2d((sunradius + 0.07) * cos(a - 0.21), (sunradius + 0.07) * sin(a - 0.21)); } else { glVertex2d((sunradius + 0.02) * cos(a - 0.14), (sunradius + 0.02) * sin(a - 0.14)); glVertex2d((sunradius + 0.02) * cos(a + 0.14), (sunradius + 0.02) * sin(a + 0.14)); glVertex2d((sunradius + 0.12) * cos(a + 0.14), (sunradius + 0.12) * sin(a + 0.14)); glVertex2d((sunradius + 0.12) * cos(a - 0.14), (sunradius + 0.12) * sin(a - 0.14)); } } glEnd(); /* Object */ glBegin(GL_QUADS); glColor4fv(suncolor0); for(i = 0; i < 10; i++) { a = (double)i * PI / 5.0; if( (i & 1) == 0 ) { glVertex2d((sunradius + 0.03) * cos(a - 0.175), (sunradius + 0.03) * sin(a - 0.175)); glVertex2d((sunradius + 0.03) * cos(a + 0.175), (sunradius + 0.03) * sin(a + 0.175)); glVertex2d((sunradius + 0.06) * cos(a + 0.175), (sunradius + 0.06) * sin(a + 0.175)); glVertex2d((sunradius + 0.06) * cos(a - 0.175), (sunradius + 0.06) * sin(a - 0.175)); } else { glVertex2d((sunradius + 0.03) * cos(a - 0.1), (sunradius + 0.03) * sin(a - 0.1)); glVertex2d((sunradius + 0.03) * cos(a + 0.1), (sunradius + 0.03) * sin(a + 0.1)); glVertex2d((sunradius + 0.11) * cos(a + 0.1), (sunradius + 0.11) * sin(a + 0.1)); glVertex2d((sunradius + 0.11) * cos(a - 0.1), (sunradius + 0.11) * sin(a - 0.1)); } } glEnd(); glShadeModel(GL_SMOOTH); glEndList(); } } /* BG_Day() */ /**************************************************************** BG_Night */ static void BG_Night(int width, int height, int left, int right, int g) { static const GLfloat skycolor0[4] = {0.0f, 0.0f, 0.3f, 1.0f}; static const GLfloat skycolor1[4] = {0.0f, 0.0f, 0.0f, 1.0f}; static const GLfloat skycolor_g0[4] = {0.0f, 0.0f, 0.0f, 1.0f}; static const GLfloat skycolor_g1[4] = {0.0f, 0.2f, 0.0f, 1.0f}; static const GLfloat starcolor[4][4] = { {1.0f, 1.0f, 1.0f, 1.0f}, {0.0f, 1.0f, 0.5f, 1.0f}, {1.0f, 1.0f, 1.0f, 1.0f}, {0.0f, 0.0f, 1.0f, 1.0f} }; static const int starcount = 128; static int starcolorindex = 0; int i, s; SetViewport2D(width, height, left, right); if( g == 0 ) { /* Dark blue sky */ if( Obj[ObjNightSky] != 0 ) { glCallList(Obj[ObjNightSky]); } else { glNewList(Obj[ObjNightSky] = glGenLists(1), GL_COMPILE_AND_EXECUTE); glShadeModel(GL_FLAT); glBegin(GL_QUAD_STRIP); glColor4fv(skycolor1); glVertex2d(-SCENE_2D_XSIZE,-1.0); glVertex2d( SCENE_2D_XSIZE,-1.0); glVertex2d(-SCENE_2D_XSIZE, 0.1); glVertex2d( SCENE_2D_XSIZE, 0.1); glEnd(); glShadeModel(GL_SMOOTH); glBegin(GL_QUAD_STRIP); glColor4fv(skycolor0); glVertex2d(-SCENE_2D_XSIZE, 0.1); glVertex2d( SCENE_2D_XSIZE, 0.1); glColor4fv(skycolor1); glVertex2d(-SCENE_2D_XSIZE, 1.0); glVertex2d( SCENE_2D_XSIZE, 1.0); glEnd(); glEndList(); } } else /* g != 0 */ { /* Dark blue sky + ground */ if( Obj[ObjNightSkyG] != 0 ) { glCallList(Obj[ObjNightSkyG]); } else { glNewList(Obj[ObjNightSkyG] = glGenLists(1), GL_COMPILE_AND_EXECUTE); glBegin(GL_QUAD_STRIP); glColor4fv(skycolor_g1); glVertex2d(-SCENE_2D_XSIZE,-1.0); glVertex2d( SCENE_2D_XSIZE,-1.0); glColor4fv(skycolor_g0); glVertex2d(-SCENE_2D_XSIZE, 0.1); glVertex2d( SCENE_2D_XSIZE, 0.1); glEnd(); glBegin(GL_QUAD_STRIP); glColor4fv(skycolor0); glVertex2d(-SCENE_2D_XSIZE, 0.1); glVertex2d( SCENE_2D_XSIZE, 0.1); glColor4fv(skycolor1); glVertex2d(-SCENE_2D_XSIZE, 1.0); glVertex2d( SCENE_2D_XSIZE, 1.0); glEnd(); glEndList(); } } /* Stars (2 sets for independent glow) */ glShadeModel(GL_FLAT); if( Obj[ObjStars1] != 0 ) { for(s = 0; s < 4; s++) { glColor4fv(starcolor[(starcolorindex + s) & 3]); glCallList(Obj[ObjStars0 + s]); } } else { Obj[ObjStars3] = 1 + (Obj[ObjStars2] = 1 + (Obj[ObjStars1] = 1 + (Obj[ObjStars0] = glGenLists(4)))); for(s = 0; s < 4; s++) { glColor4fv(starcolor[(starcolorindex + s) & 3]); glNewList(Obj[ObjStars0 + s], GL_COMPILE_AND_EXECUTE); glBegin(GL_POINTS); for(i = 0; i < starcount; i++) glVertex2d(Random(0.0, SCENE_2D_XSIZE), Random(0.3, 1.0)); glEnd(); glEndList(); } } glTranslated(-SCENE_2D_XSIZE, 0.0, 0.0); for(s = 0; s < 4; s++) { glColor4fv(starcolor[(starcolorindex + s) & 3]); glCallList(Obj[ObjStars0 + s]); } starcolorindex++; glShadeModel(GL_SMOOTH); /*XXX moon here? ;) */ } /* BG_Night() */ /***************************************************************** BG_Dark */ static void BG_Dark(int width, int height, int left, int right) { static const GLfloat skycolor0[4] = {0.4f, 0.4f, 0.4f, 1.0f}; static const GLfloat skycolor1[4] = {0.1f, 0.1f, 0.1f, 1.0f}; static const GLfloat skycolor2[4] = {0.6f, 0.6f, 0.7f, 1.0f}; static const GLfloat skycolor3[4] = {0.1f, 0.1f, 0.13f, 1.0f}; SetViewport2D(width, height, left, right); if( Obj[ObjDarkSky] != 0 ) { glCallList(Obj[ObjDarkSky]); } else { glNewList(Obj[ObjDarkSky] = glGenLists(1), GL_COMPILE_AND_EXECUTE); glBegin(GL_QUAD_STRIP); glColor4fv(skycolor0); glVertex2d(-SCENE_2D_XSIZE,-1.0); glVertex2d( SCENE_2D_XSIZE,-1.0); glColor4fv(skycolor1); glVertex2d(-SCENE_2D_XSIZE, 0.1); glVertex2d( SCENE_2D_XSIZE, 0.1); glEnd(); glBegin(GL_QUAD_STRIP); glColor4fv(skycolor2); glVertex2d(-SCENE_2D_XSIZE, 0.1); glVertex2d( SCENE_2D_XSIZE, 0.1); glColor4fv(skycolor3); glVertex2d(-SCENE_2D_XSIZE, 1.0); glVertex2d( SCENE_2D_XSIZE, 1.0); glEnd(); glEndList(); } } /* BG_Dark() */ /********************************************************* BlankBackground */ static void BlankBackground(int width, int height, int left, int right) { SetViewport2D(width, height, left, right); glShadeModel(GL_FLAT); glBegin(GL_QUAD_STRIP); glColor3f(1.0f, 1.0f, 1.0f); glVertex2d(-SCENE_2D_XSIZE,-1.0); glVertex2d( SCENE_2D_XSIZE,-1.0); glVertex2d(-SCENE_2D_XSIZE, 1.0); glVertex2d( SCENE_2D_XSIZE, 1.0); glEnd(); glShadeModel(GL_SMOOTH); } /* BlankBackground() */ /********************************************************* CityBackgroundD */ static void CityBackgroundD(int width, int height, int left, int right) { BG_Day(width, height, left, right, 0); SetViewport3D(width, height, left, right, 1); SetupCityTileD(); /* (city.c) */ DrawTileSet(DrawCityTileD, DrawCityOutline, 1); /* (city.c) */ } /* CityBackgroundD() */ /********************************************************* CityBackgroundN */ static void CityBackgroundN(int width, int height, int left, int right) { BG_Night(width, height, left, right, 0); SetViewport3D(width, height, left, right, 2); SetupCityTileN(); /* (city.c) */ DrawTileSet(DrawCityTileN, DrawCityOutline, 1); /* (city.c) */ } /* CityBackgroundN() */ /******************************************************* ForestBackgroundD */ static void ForestBackgroundD(int width, int height, int left, int right) { BG_Day(width, height, left, right, 1); SetViewport3D(width, height, left, right, 1); DrawTileSet(DrawForestTileD, DrawForestOutline, 1); /* (forest.c) */ } /* ForestBackgroundD() */ /******************************************************* ForestBackgroundN */ static void ForestBackgroundN(int width, int height, int left, int right) { BG_Night(width, height, left, right, 1); SetViewport3D(width, height, left, right, 2); DrawTileSet(DrawForestTileN, DrawForestOutline, 1); /* (forest.c) */ } /* ForestBackgroundN() */ /********************************************************** RainBackground */ static void RainBackground(int width, int height, int left, int right) { BG_Dark(width, height, left, right); SetViewport3D(width, height, left, right, 2); DrawRain(); SetupRockTiles(); /* (rocks.c) */ DrawTileSet(DrawRockTile, DrawRockOutline, 0); /* (rocks.c) */ } /* RainBackground() */ /********************************************************* TileBackgroundD */ static void TileBackgroundD(int width, int height, int left, int right) { BG_Day(width, height, left, right, 0); SetViewport3D(width, height, left, right, 1); DrawTileGridD(); /* (tilegrid.c) */ } /* TileBackgroundD() */ /********************************************************* TileBackgroundN */ static void TileBackgroundN(int width, int height, int left, int right) { BG_Night(width, height, left, right, 0); SetViewport3D(width, height, left, right, 2); DrawTileGridN(); /* (tilegrid.c) */ } /* TileBackgroundN() */ /**************************************************************** DrawRain */ static void DrawRain(void) { static const GLfloat raincolor[4][4] = { {0.3f, 0.3f, 0.8f, 1.0f}, {0.3f, 0.35f, 0.8f, 1.0f}, {0.3f, 0.3f, 0.6f, 1.0f}, {0.3f, 0.3f, 0.7f, 1.0f} }; static const double dx[4] = {-0.1,-0.2,-0.3,-0.4}; static const double dy[4] = {-1.0,-1.1,-1.2,-0.9}; static const double dz[4] = { 0.0, 0.0, 0.01,-0.01}; double x, y, z, dt; int i; glDisable(GL_CULL_FACE); glDisable(GL_FOG); glDisable(GL_LIGHTING); glShadeModel(GL_FLAT); glBegin(GL_LINES); /* Slower raindrops */ for(i = 0; i < RAINDROP_COUNT / 2; i++) { /* Change color 4 times */ if( (i % (RAINDROP_COUNT/8)) == 0 ) glColor4fv(raincolor[i / (RAINDROP_COUNT/8)]); /* Move each raindrop */ dt = (CurrentTime - Rain[i].st) * 90.0; x = Rain[i].sx + dx[i & 3] * dt - CurrentTime * DRIFT_DISTANCE; y = Rain[i].sy + dy[i & 3] * dt; z = Rain[i].sz + dz[i & 3] * dt; if( x < -SCENE_3D_XSIZE ) { /* Wrap around -X. No need to wrap around +X or Z because rain moves mostly in -X direction. */ x = SCENE_3D_XSIZE - fmod(-SCENE_3D_XSIZE - x, SCENE_3D_XSIZE * 2.0); } if( y >= 0.0 ) { glVertex3d(x, y, z); glVertex3d(x + dx[i & 3], y + dy[i & 3], z + dz[i & 3]); } else { /* Regenerate out of bounds rain */ Rain[i].sx = Random(-SCENE_3D_XSIZE, SCENE_3D_XSIZE); Rain[i].sy = Random(0.0, SCENE_3D_YSIZE); Rain[i].sz = Random(-SCENE_3D_ZSIZE, SCENE_3D_ZSIZE); Rain[i].st = CurrentTime; glVertex3d(Rain[i].sx, Rain[i].sy, Rain[i].sz); glVertex3d(Rain[i].sx + dx[i & 3], Rain[i].sy + dy[i & 3], Rain[i].sz + dz[i & 3]); } } /* Faster raindrops */ for(; i < RAINDROP_COUNT; i++) { if( (i % (RAINDROP_COUNT/8)) == 0 ) glColor4fv(raincolor[(i - RAINDROP_COUNT/2) / (RAINDROP_COUNT/8)]); dt = (CurrentTime - Rain[i].st) * 120.0; x = Rain[i].sx + dx[i & 3] * dt - CurrentTime * DRIFT_DISTANCE; y = Rain[i].sy + dy[i & 3] * dt; z = Rain[i].sz + dz[i & 3] * dt; if( x < -SCENE_3D_XSIZE ) { x = SCENE_3D_XSIZE - fmod(-SCENE_3D_XSIZE - x, SCENE_3D_XSIZE * 2.0); } if( y >= 0.0 ) { glVertex3d(x, y, z); glVertex3d(x + dx[i & 3], y + dy[i & 3], z + dz[i & 3]); } else { Rain[i].sx = Random(-SCENE_3D_XSIZE, SCENE_3D_XSIZE); Rain[i].sy = Random(0.0, SCENE_3D_YSIZE); Rain[i].sz = Random(-SCENE_3D_ZSIZE, SCENE_3D_ZSIZE); Rain[i].st = CurrentTime; glVertex3d(Rain[i].sx, Rain[i].sy, Rain[i].sz); glVertex3d(Rain[i].sx + dx[i & 3], Rain[i].sy + dy[i & 3], Rain[i].sz + dz[i & 3]); } } glEnd(); glEnable(GL_CULL_FACE); glEnable(GL_FOG); glEnable(GL_LIGHTING); glShadeModel(GL_SMOOTH); } /* DrawRain() */ /************************************************************* DrawTileSet */ static void DrawTileSet(void (*tilefunc)(unsigned int), void (*outlinefunc)(unsigned int), int transform) { static const GLfloat outline[4] = {0.0f, 0.0f, 0.0f, 1.0f}; unsigned int cell; int xi, zi, zdirection, phase; /* Translate grid */ glTranslated(-fmod(CurrentTime * DRIFT_DISTANCE, CELL_SIZE), 0.0, 0.0); /* Draw cells */ for(phase = 0; phase < 2; phase++) { for(xi = 0; xi < GRID_SIZE; xi++) { for(zdirection = -1; zdirection <= 1; zdirection += 2) { if( zdirection == -1 && ManualClip == ClipNegativeZ ) continue; if( zdirection == +1 && ManualClip == ClipPositiveZ ) continue; zi = (zdirection == -1) ? (GRID_SIZE / 2 - 2) : (GRID_SIZE / 2 + 2); for(; zi >= 0 && zi < GRID_SIZE; zi += zdirection) { /* Do not draw if cell is already covered by another tile */ assert( GridCoverage[xi][zi] != CELL_EMPTY ); if( GridCoverage[xi][zi] != CELL_ORIG ) continue; /* Draw tile */ glPushMatrix(); TransformTile(cell = Grid[xi][zi], xi, zi, zdirection, transform); ((phase == 0) ? tilefunc : outlinefunc)(cell); glPopMatrix(); } } } glCullFace(GL_FRONT); glDisable(GL_LIGHTING); glShadeModel(GL_FLAT); glColor4fv(outline); } glCullFace(GL_BACK); glEnable(GL_LIGHTING); glShadeModel(GL_SMOOTH); } /* DrawTileSet() */ /*********************************************************** TransformTile */ static void TransformTile(unsigned int cell, int xi, int zi, int zd, int t) { static const double center[4] = {1.0, 2.0, 3.0, 4.0}; GLdouble m[16]; glTranslated((double)(xi * 2) - 32.0 + center[cell & CELL_BITS_SIZE], 0.0, (double)(zi * 2) - 32.0 + center[cell & CELL_BITS_SIZE] * zd); if( t == 0 ) return; /* Scale */ if( (cell & CELL_BITS_SCALE) != 0 ) { if( (cell & CELL_BITS_SCALE_PHASE) != 0 ) { glScaled(0.9 + 0.2 * TransformCurve, 0.92 - 0.16 * TransformCurve, 0.9 + 0.2 * TransformCurve); } else { glScaled(0.9 - 0.2 * TransformCurve, 0.92 + 0.16 * TransformCurve, 0.9 - 0.2 * TransformCurve); } } /* Shear */ m[ 1] = m[ 2] = m[ 3] = m[ 4] = m[ 6] = m[ 7] = m[ 8] = m[ 9] = m[11] = m[12] = m[13] = m[14] = 0.0; m[0] = m[5] = m[10] = m[15] = 1.0; if( (cell & CELL_BITS_SHEAR) != 0 ) { if( (cell & CELL_BITS_SHEAR_PHASE) != 0 ) m[4] = 0.16 * TransformCurve; else m[4] = -0.16 * TransformCurve; m[5] = 1.0 - 0.16 * TransformCurveA; } glMultMatrixd(m); } /* TransformTile() */ /************************************************************** UpdateGrid */ static void UpdateGrid(void) { static int lastx = 0; double currentx; int cx, i; /* Generate cells - Increment lastx until it matches the current rotate distance. As long as each frame is drawn in less than 64 seconds, the cells will be generated properly, assuming time changes in positive direction. */ currentx = CurrentTime * DRIFT_DISTANCE; cx = (int)fmod(currentx / CELL_SIZE, GRID_XSIZE); assert(cx >= 0 && cx < GRID_XSIZE); while( lastx != cx ) { lastx = (lastx + 1) % GRID_XSIZE; for(i = 0; i < GRID_ZSIZE; i++) { /* Only update if cell is an original cell. Also keep same cell size. */ if( StaticGridCoverage[lastx][i] != CELL_ORIG ) continue; StaticGrid[lastx][i] = (StaticGrid[lastx][i] & 3) | (rand() & ~3); } } /* Rotate cells */ for(i = 0; i < GRID_SIZE; i++) { Grid[i] = StaticGrid[(i + cx) % GRID_XSIZE]; GridCoverage[i] = StaticGridCoverage[(i + cx) % GRID_XSIZE]; } } /* UpdateGrid() */ #if 0 /************************************************************* TestTileSet */ static void TestTileSet(unsigned int type) { static const GLfloat ambient[4] = {0.2f, 0.2f, 0.5f, 1.0f}; static const GLfloat specular[4] = {0.2f, 0.2f, 0.22f, 1.0f}; static const GLfloat emission[4] = {0.1f, 0.1f, 0.1f, 1.0f}; static const GLfloat diffuse_r[4] = {1.0f, 0.0f, 0.0f, 1.0f}; static const GLfloat diffuse_g[4] = {0.0f, 1.0f, 0.0f, 1.0f}; static const GLfloat diffuse_b[4] = {0.0f, 0.0f, 1.0f, 1.0f}; static const GLfloat diffuse_y[4] = {1.0f, 1.0f, 0.0f, 1.0f}; static const GLfloat diffuse_x[4] = {0.0f, 0.2f, 0.0f, 1.0f}; glMaterialfv(GL_FRONT, GL_AMBIENT, ambient); glMaterialfv(GL_FRONT, GL_SPECULAR, specular); glMaterialfv(GL_FRONT, GL_EMISSION, emission); glBegin(GL_QUADS); glNormal3f(0.0f, 1.0f, 0.0f); switch( type & 3 ) { case 0: glMaterialfv(GL_FRONT, GL_DIFFUSE, diffuse_r); glVertex3f(-1.0f, 0.0f,-1.0f); glVertex3f(-1.0f, 0.0f, 1.0f); glVertex3f( 1.0f, 0.0f, 1.0f); glVertex3f( 1.0f, 0.0f,-1.0f); glMaterialfv(GL_FRONT, GL_DIFFUSE, diffuse_x); glVertex3f(-0.7f, 0.2f,-0.7f); glVertex3f(-0.7f, 0.2f, 0.7f); glVertex3f( 0.7f, 0.2f, 0.7f); glVertex3f( 0.7f, 0.2f,-0.7f); break; case 1: glMaterialfv(GL_FRONT, GL_DIFFUSE, diffuse_g); glVertex3f(-2.0f, 0.0f,-2.0f); glVertex3f(-2.0f, 0.0f, 2.0f); glVertex3f( 2.0f, 0.0f, 2.0f); glVertex3f( 2.0f, 0.0f,-2.0f); glMaterialfv(GL_FRONT, GL_DIFFUSE, diffuse_x); glVertex3f(-1.7f, 0.2f,-1.7f); glVertex3f(-1.7f, 0.2f, 1.7f); glVertex3f( 1.7f, 0.2f, 1.7f); glVertex3f( 1.7f, 0.2f,-1.7f); break; case 2: glMaterialfv(GL_FRONT, GL_DIFFUSE, diffuse_b); glVertex3f(-3.0f, 0.0f,-3.0f); glVertex3f(-3.0f, 0.0f, 3.0f); glVertex3f( 3.0f, 0.0f, 3.0f); glVertex3f( 3.0f, 0.0f,-3.0f); glMaterialfv(GL_FRONT, GL_DIFFUSE, diffuse_x); glVertex3f(-2.7f, 0.2f,-2.7f); glVertex3f(-2.7f, 0.2f, 2.7f); glVertex3f( 2.7f, 0.2f, 2.7f); glVertex3f( 2.7f, 0.2f,-2.7f); break; default: glMaterialfv(GL_FRONT, GL_DIFFUSE, diffuse_y); glVertex3f(-4.0f, 0.0f,-4.0f); glVertex3f(-4.0f, 0.0f, 4.0f); glVertex3f( 4.0f, 0.0f, 4.0f); glVertex3f( 4.0f, 0.0f,-4.0f); glMaterialfv(GL_FRONT, GL_DIFFUSE, diffuse_x); glVertex3f(-3.7f, 0.2f,-3.7f); glVertex3f(-3.7f, 0.2f, 3.7f); glVertex3f( 3.7f, 0.2f, 3.7f); glVertex3f( 3.7f, 0.2f,-3.7f); break; } glEnd(); } /* TestTileSet() */ #endif