/* schierke.c - Don Yang (uguu.org) 10/30/11 */ #include #include #include"SDL.h" #define WIDTH 800 #define HEIGHT 600 #define TRAIL_SIZE 64 #define ELEMENT_COUNT 4 #define SWARM_SIZE 256 #define FRAME_DELAY_MS (1000/60) /* All particles, mapping from cycle to element to swarm of points to (x, y). At each frame, we update points in the next cycle, and raw particles from the previous cycle at a slightly dimmer color. Each value is a fixed point value, with higher 16 bits being the screen coordinates, and the lower 16 bits to be the fractional part. All swarm particles with point index > 0 follows the 0th point. */ static Sint32 Position[TRAIL_SIZE][ELEMENT_COUNT][SWARM_SIZE][2]; static Sint32 Direction[ELEMENT_COUNT][SWARM_SIZE][2]; static int Cycle = 0; /* Targets for swarm point 0, same fixed point format as above */ static Sint32 Target[ELEMENT_COUNT][2]; /* Color palette for each elemental */ static Uint8 Palette[ELEMENT_COUNT][3] = { {255, 16, 16}, {0, 255, 0}, {255, 255, 64}, {64, 128, 255} }; /* Return a random number in the range of [min, max) */ static Sint32 Rand(Sint32 min, Sint32 max) { return (max - min) * (double)(rand()) / (double)RAND_MAX + min; } /* Clamp value to a particular range */ static Sint32 Clamp(Sint32 min, Sint32 max, Sint32 value) { return value < min ? min : (value > max ? max : value); } /* Update velocity for a single point to approach a target point */ static void Approach(Sint32 x, Sint32 y, Sint32 tx, Sint32 ty, Sint32 *dx, Sint32 *dy, Sint32 minv, Sint32 maxv) { if( x < tx ) *dx += Rand(minv, maxv); else if( x > tx ) *dx -= Rand(minv, maxv); if( y < ty ) *dy += Rand(minv, maxv); else if( y > ty ) *dy -= Rand(minv, maxv); } /* Initialize particles */ static void Init(int element) { int i, j, k; for(i = 0; i < SWARM_SIZE; i++) { Position[0][element][i][0] = Rand(0, WIDTH * 0x10000); Position[0][element][i][1] = Rand(0, HEIGHT * 0x10000); Direction[element][i][0] = Direction[element][i][1] = 0; } for(i = 1; i < TRAIL_SIZE; i++) { for(j = 0; j < SWARM_SIZE; j++) { for(k = 0; k < 2; k++) Position[i][element][j][k] = Position[0][element][j][k]; } } Target[element][0] = Rand(0, WIDTH * 0x10000); Target[element][1] = Rand(0, HEIGHT * 0x10000); } /* Animate particles */ static void Animate(int element) { int i, j, next_cycle; /* Update target for leader about once per second */ if( Rand(0, 60) == 0 ) { Target[element][0] = Rand(0x10000 * (WIDTH / 10), 0x10000 * (WIDTH - (WIDTH / 10))); Target[element][1] = Rand(0x10000 * (HEIGHT / 10), 0x10000 * (HEIGHT - (HEIGHT / 10))); } Approach(Position[Cycle][element][0][0], Position[Cycle][element][0][1], Target[element][0], Target[element][1], &Direction[element][0][0], &Direction[element][0][1], 0x10, 0x1000); for(j = 0; j < 2; j++) Direction[element][0][j] = Clamp(-0x20000, 0x20000, Direction[element][0][j]); /* Update velocities for followers */ for(i = 1; i < SWARM_SIZE; i++) { Approach(Position[Cycle][element][i][0], Position[Cycle][element][i][1], Position[Cycle][element][0][0], Position[Cycle][element][0][1], &Direction[element][i][0], &Direction[element][i][1], 0x20, 0x1800); for(j = 0; j < 2; j++) Direction[element][i][j] = Clamp(-0x40000, 0x40000, Direction[element][i][j]); } /* Update point positions */ next_cycle = (Cycle + 1) % TRAIL_SIZE; for(i = 0; i < SWARM_SIZE; i++) { for(j = 0; j < 2; j++) Position[next_cycle][element][i][j] = Position[Cycle][element][i][j] + Direction[element][i][j]; } } /* Render a single frame */ #define RENDER(type) \ { \ int i, j, k, c; \ Uint32 x, y, pixel; \ \ SDL_FillRect(screen, NULL, SDL_MapRGB(screen->format, 0, 0, 0)); \ \ /* At each cycle, we will update points such that cycle points at the \ latest set of points. Since we want to draw points from oldest \ cycle to newest cycle, we will start at cycle+1. */ \ for(i = 1; i <= TRAIL_SIZE; i++) \ { \ c = (Cycle + i) % TRAIL_SIZE; \ for(j = 0; j < ELEMENT_COUNT; j++) \ { \ /* The leader point will have shades of white */ \ pixel = SDL_MapRGB(screen->format, \ i * 255 / TRAIL_SIZE, \ i * 255 / TRAIL_SIZE, \ i * 255 / TRAIL_SIZE); \ for(k = 0; k < SWARM_SIZE; k++) \ { \ /* After drawing the leader point, set color to draw \ follower points. */ \ if( k == 1 ) \ { \ pixel = SDL_MapRGB(screen->format, \ Palette[j][0] * i / TRAIL_SIZE, \ Palette[j][1] * i / TRAIL_SIZE, \ Palette[j][2] * i / TRAIL_SIZE); \ } \ \ /* Scale coordinates and clip */ \ x = Position[c][j][k][0]; \ if( x < 0 || x >= WIDTH * 0x10000 ) \ continue; \ x >>= 16; \ y = Position[c][j][k][1]; \ if( y < 0 || y >= HEIGHT * 0x10000 ) \ continue; \ y >>= 16; \ \ /* Draw pixel */ \ *(type*)((Uint8*)(screen->pixels) + \ y * screen->pitch + \ x * screen->format->BytesPerPixel) \ = (type)pixel; \ } \ } \ } \ } static void Render16(SDL_Surface *screen) RENDER(Uint16) static void Render32(SDL_Surface *screen) RENDER(Uint32) /* Run renderer until keystroke */ static void RenderLoop(SDL_Surface *screen) { SDL_Event event; int run_loop = 1, i; Uint32 current_time, next_time; /* Initialize swarms */ for(i = 0; i < ELEMENT_COUNT; i++) Init(i); next_time = SDL_GetTicks() + FRAME_DELAY_MS; while( run_loop ) { /* Render points */ SDL_LockSurface(screen); if( screen->format->BytesPerPixel == 4 ) Render32(screen); else Render16(screen); SDL_UnlockSurface(screen); SDL_Flip(screen); /* Update animation state */ for(i = 0; i < ELEMENT_COUNT; i++) Animate(i); Cycle = (Cycle + 1) % TRAIL_SIZE; /* Check for keyboard events */ while( SDL_PollEvent(&event) ) { if( event.type == SDL_KEYDOWN || event.type == SDL_QUIT ) run_loop = 0; } /* Sleep until next frame */ current_time = SDL_GetTicks(); if( current_time < next_time ) SDL_Delay(next_time - current_time); next_time += FRAME_DELAY_MS; } } int main(int argc, char **argv) { const SDL_VideoInfo *video_info; SDL_Surface *screen; Uint32 flags = SDL_HWSURFACE|SDL_DOUBLEBUF; int bpp; /* Initialize SDL */ if( SDL_Init(SDL_INIT_VIDEO) < 0 ) { printf("Unable to init SDL: %s\n", SDL_GetError()); return 1; } atexit(SDL_Quit); /* Initialize surface */ video_info = SDL_GetVideoInfo(); bpp = video_info->vfmt->BytesPerPixel; if( bpp != 2 && bpp != 4 ) { puts("Unsupported pixel format"); SDL_Quit(); return 1; } if( argc > 1 ) flags |= SDL_FULLSCREEN; screen = SDL_SetVideoMode(WIDTH, HEIGHT, bpp * 8, flags); if( screen == NULL ) { printf("Error initializing screen: %s\n", SDL_GetError()); SDL_Quit(); return 1; } SDL_WM_SetCaption("Schierke", NULL); SDL_ShowCursor(SDL_DISABLE); /* Initialize random seed */ srand(time(NULL)); /* Render loop */ RenderLoop(screen); /* Cleanup */ SDL_FreeSurface(screen); SDL_Quit(); return 0; }