fireworks.c (15423B)
1/* 2 * fireworks.c 3 * written by Holmes Futrell 4 * use however you want 5 */ 6 7#include "SDL.h" 8#include "SDL_opengles.h" 9#include "common.h" 10#include <math.h> 11#include <time.h> 12 13#define MILLESECONDS_PER_FRAME 16 /* about 60 frames per second */ 14#define ACCEL 0.0001f /* acceleration due to gravity, units in pixels per millesecond squared */ 15#define WIND_RESISTANCE 0.00005f /* acceleration per unit velocity due to wind resistance */ 16#define MAX_PARTICLES 2000 /* maximum number of particles displayed at once */ 17 18static GLuint particleTextureID; /* OpenGL particle texture id */ 19static SDL_bool pointSizeExtensionSupported; /* is GL_OES_point_size_array supported ? */ 20/* 21 used to describe what type of particle a given struct particle is. 22 emitter - this particle flies up, shooting off trail particles, then finally explodes into dust particles. 23 trail - shoots off, following emitter particle 24 dust - radiates outwards from emitter explosion 25*/ 26enum particleType 27{ 28 emitter = 0, 29 trail, 30 dust 31}; 32/* 33 struct particle is used to describe each particle displayed on screen 34*/ 35struct particle 36{ 37 GLfloat x; /* x position of particle */ 38 GLfloat y; /* y position of particle */ 39 GLubyte color[4]; /* rgba color of particle */ 40 GLfloat size; /* size of particle in pixels */ 41 GLfloat xvel; /* x velocity of particle in pixels per milesecond */ 42 GLfloat yvel; /* y velocity of particle in pixels per millescond */ 43 int isActive; /* if not active, then particle is overwritten */ 44 enum particleType type; /* see enum particleType */ 45} particles[MAX_PARTICLES]; /* this array holds all our particles */ 46 47static int num_active_particles; /* how many members of the particle array are actually being drawn / animated? */ 48static int screen_w, screen_h; 49 50/* function declarations */ 51void spawnTrailFromEmitter(struct particle *emitter); 52void spawnEmitterParticle(GLfloat x, GLfloat y); 53void explodeEmitter(struct particle *emitter); 54void initializeParticles(void); 55void initializeTexture(); 56int nextPowerOfTwo(int x); 57void drawParticles(); 58void stepParticles(void); 59 60/* helper function (used in texture loading) 61 returns next power of two greater than or equal to x 62*/ 63int 64nextPowerOfTwo(int x) 65{ 66 int val = 1; 67 while (val < x) { 68 val *= 2; 69 } 70 return val; 71} 72 73/* 74 steps each active particle by timestep MILLESECONDS_PER_FRAME 75*/ 76void 77stepParticles(void) 78{ 79 int i; 80 struct particle *slot = particles; 81 struct particle *curr = particles; 82 for (i = 0; i < num_active_particles; i++) { 83 /* is the particle actually active, or is it marked for deletion? */ 84 if (curr->isActive) { 85 /* is the particle off the screen? */ 86 if (curr->y > screen_h) 87 curr->isActive = 0; 88 else if (curr->y < 0) 89 curr->isActive = 0; 90 if (curr->x > screen_w) 91 curr->isActive = 0; 92 else if (curr->x < 0) 93 curr->isActive = 0; 94 95 /* step velocity, then step position */ 96 curr->yvel += ACCEL * MILLESECONDS_PER_FRAME; 97 curr->xvel += 0.0f; 98 curr->y += curr->yvel * MILLESECONDS_PER_FRAME; 99 curr->x += curr->xvel * MILLESECONDS_PER_FRAME; 100 101 /* particle behavior */ 102 if (curr->type == emitter) { 103 /* if we're an emitter, spawn a trail */ 104 spawnTrailFromEmitter(curr); 105 /* if we've reached our peak, explode */ 106 if (curr->yvel > 0.0) { 107 explodeEmitter(curr); 108 } 109 } else { 110 float speed = 111 sqrt(curr->xvel * curr->xvel + curr->yvel * curr->yvel); 112 /* if wind resistance is not powerful enough to stop us completely, 113 then apply winde resistance, otherwise just stop us completely */ 114 if (WIND_RESISTANCE * MILLESECONDS_PER_FRAME < speed) { 115 float normx = curr->xvel / speed; 116 float normy = curr->yvel / speed; 117 curr->xvel -= 118 normx * WIND_RESISTANCE * MILLESECONDS_PER_FRAME; 119 curr->yvel -= 120 normy * WIND_RESISTANCE * MILLESECONDS_PER_FRAME; 121 } else { 122 curr->xvel = curr->yvel = 0; /* stop particle */ 123 } 124 125 if (curr->color[3] <= MILLESECONDS_PER_FRAME * 0.1275f) { 126 /* if this next step will cause us to fade out completely 127 then just mark for deletion */ 128 curr->isActive = 0; 129 } else { 130 /* otherwise, let's fade a bit more */ 131 curr->color[3] -= MILLESECONDS_PER_FRAME * 0.1275f; 132 } 133 134 /* if we're a dust particle, shrink our size */ 135 if (curr->type == dust) 136 curr->size -= MILLESECONDS_PER_FRAME * 0.010f; 137 138 } 139 140 /* if we're still active, pack ourselves in the array next 141 to the last active guy (pack the array tightly) */ 142 if (curr->isActive) 143 *(slot++) = *curr; 144 } /* endif (curr->isActive) */ 145 curr++; 146 } 147 /* the number of active particles is computed as the difference between 148 old number of active particles, where slot points, and the 149 new size of the array, where particles points */ 150 num_active_particles = slot - particles; 151} 152 153/* 154 This draws all the particles shown on screen 155*/ 156void 157drawParticles() 158{ 159 160 /* draw the background */ 161 glClear(GL_COLOR_BUFFER_BIT); 162 163 /* set up the position and color pointers */ 164 glVertexPointer(2, GL_FLOAT, sizeof(struct particle), particles); 165 glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(struct particle), 166 particles[0].color); 167 168 if (pointSizeExtensionSupported) { 169 /* pass in our array of point sizes */ 170 glPointSizePointerOES(GL_FLOAT, sizeof(struct particle), 171 &(particles[0].size)); 172 } 173 174 /* draw our particles! */ 175 glDrawArrays(GL_POINTS, 0, num_active_particles); 176 177} 178 179/* 180 This causes an emitter to explode in a circular bloom of dust particles 181*/ 182void 183explodeEmitter(struct particle *emitter) 184{ 185 /* first off, we're done with this particle, so turn active off */ 186 emitter->isActive = 0; 187 int i; 188 for (i = 0; i < 200; i++) { 189 190 if (num_active_particles >= MAX_PARTICLES) 191 return; 192 193 /* come up with a random angle and speed for new particle */ 194 float theta = randomFloat(0, 2.0f * 3.141592); 195 float exponent = 3.0f; 196 float speed = randomFloat(0.00, powf(0.17, exponent)); 197 speed = powf(speed, 1.0f / exponent); 198 199 /* select the particle at the end of our array */ 200 struct particle *p = &particles[num_active_particles]; 201 202 /* set the particles properties */ 203 p->xvel = speed * cos(theta); 204 p->yvel = speed * sin(theta); 205 p->x = emitter->x + emitter->xvel; 206 p->y = emitter->y + emitter->yvel; 207 p->isActive = 1; 208 p->type = dust; 209 p->size = 15; 210 /* inherit emitter's color */ 211 p->color[0] = emitter->color[0]; 212 p->color[1] = emitter->color[1]; 213 p->color[2] = emitter->color[2]; 214 p->color[3] = 255; 215 /* our array has expanded at the end */ 216 num_active_particles++; 217 } 218 219} 220 221/* 222 This spawns a trail particle from an emitter 223*/ 224void 225spawnTrailFromEmitter(struct particle *emitter) 226{ 227 228 if (num_active_particles >= MAX_PARTICLES) 229 return; 230 231 /* select the particle at the slot at the end of our array */ 232 struct particle *p = &particles[num_active_particles]; 233 234 /* set position and velocity to roughly that of the emitter */ 235 p->x = emitter->x + randomFloat(-3.0, 3.0); 236 p->y = emitter->y + emitter->size / 2.0f; 237 p->xvel = emitter->xvel + randomFloat(-0.005, 0.005); 238 p->yvel = emitter->yvel + 0.1; 239 240 /* set the color to a random-ish orangy type color */ 241 p->color[0] = (0.8f + randomFloat(-0.1, 0.0)) * 255; 242 p->color[1] = (0.4f + randomFloat(-0.1, 0.1)) * 255; 243 p->color[2] = (0.0f + randomFloat(0.0, 0.2)) * 255; 244 p->color[3] = (0.7f) * 255; 245 246 /* set other attributes */ 247 p->size = 10; 248 p->type = trail; 249 p->isActive = 1; 250 251 /* our array has expanded at the end */ 252 num_active_particles++; 253 254} 255 256/* 257 spawns a new emitter particle at the bottom of the screen 258 destined for the point (x,y). 259*/ 260void 261spawnEmitterParticle(GLfloat x, GLfloat y) 262{ 263 264 if (num_active_particles >= MAX_PARTICLES) 265 return; 266 267 /* find particle at endpoint of array */ 268 struct particle *p = &particles[num_active_particles]; 269 270 /* set the color randomly */ 271 switch (rand() % 4) { 272 case 0: 273 p->color[0] = 255; 274 p->color[1] = 100; 275 p->color[2] = 100; 276 break; 277 case 1: 278 p->color[0] = 100; 279 p->color[1] = 255; 280 p->color[2] = 100; 281 break; 282 case 2: 283 p->color[0] = 100; 284 p->color[1] = 100; 285 p->color[2] = 255; 286 break; 287 case 3: 288 p->color[0] = 255; 289 p->color[1] = 150; 290 p->color[2] = 50; 291 break; 292 } 293 p->color[3] = 255; 294 /* set position to (x, screen_h) */ 295 p->x = x; 296 p->y = screen_h; 297 /* set velocity so that terminal point is (x,y) */ 298 p->xvel = 0; 299 p->yvel = -sqrt(2 * ACCEL * (screen_h - y)); 300 /* set other attributes */ 301 p->size = 10; 302 p->type = emitter; 303 p->isActive = 1; 304 /* our array has expanded at the end */ 305 num_active_particles++; 306} 307 308/* just sets the endpoint of the particle array to element zero */ 309void 310initializeParticles(void) 311{ 312 num_active_particles = 0; 313} 314 315/* 316 loads the particle texture 317 */ 318void 319initializeTexture() 320{ 321 322 int bpp; /* texture bits per pixel */ 323 Uint32 Rmask, Gmask, Bmask, Amask; /* masks for pixel format passed into OpenGL */ 324 SDL_Surface *bmp_surface; /* the bmp is loaded here */ 325 SDL_Surface *bmp_surface_rgba8888; /* this serves as a destination to convert the BMP 326 to format passed into OpenGL */ 327 328 bmp_surface = SDL_LoadBMP("stroke.bmp"); 329 if (bmp_surface == NULL) { 330 fatalError("could not load stroke.bmp"); 331 } 332 333 /* Grab info about format that will be passed into OpenGL */ 334 SDL_PixelFormatEnumToMasks(SDL_PIXELFORMAT_ABGR8888, &bpp, &Rmask, &Gmask, 335 &Bmask, &Amask); 336 /* Create surface that will hold pixels passed into OpenGL */ 337 bmp_surface_rgba8888 = 338 SDL_CreateRGBSurface(0, bmp_surface->w, bmp_surface->h, bpp, Rmask, 339 Gmask, Bmask, Amask); 340 /* Blit to this surface, effectively converting the format */ 341 SDL_BlitSurface(bmp_surface, NULL, bmp_surface_rgba8888, NULL); 342 343 glGenTextures(1, &particleTextureID); 344 glBindTexture(GL_TEXTURE_2D, particleTextureID); 345 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 346 nextPowerOfTwo(bmp_surface->w), 347 nextPowerOfTwo(bmp_surface->h), 348 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); 349 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 350 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 351 /* this is where we actually pass in the pixel data */ 352 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, bmp_surface->w, bmp_surface->h, 0, 353 GL_RGBA, GL_UNSIGNED_BYTE, bmp_surface_rgba8888->pixels); 354 355 /* free bmp surface and converted bmp surface */ 356 SDL_FreeSurface(bmp_surface); 357 SDL_FreeSurface(bmp_surface_rgba8888); 358 359} 360 361int 362main(int argc, char *argv[]) 363{ 364 SDL_Window *window; /* main window */ 365 SDL_GLContext context; 366 int w, h; 367 Uint32 startFrame; /* time frame began to process */ 368 Uint32 endFrame; /* time frame ended processing */ 369 Uint32 delay; /* time to pause waiting to draw next frame */ 370 int done; /* should we clean up and exit? */ 371 372 /* initialize SDL */ 373 if (SDL_Init(SDL_INIT_VIDEO) < 0) { 374 fatalError("Could not initialize SDL"); 375 } 376 /* seed the random number generator */ 377 srand(time(NULL)); 378 /* 379 request some OpenGL parameters 380 that may speed drawing 381 */ 382 SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 5); 383 SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 6); 384 SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 5); 385 SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 0); 386 SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 0); 387 SDL_GL_SetAttribute(SDL_GL_RETAINED_BACKING, 0); 388 SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, 1); 389 390 /* create main window and renderer */ 391 window = SDL_CreateWindow(NULL, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 392 SDL_WINDOW_OPENGL | 393 SDL_WINDOW_BORDERLESS); 394 context = SDL_GL_CreateContext(window); 395 396 /* load the particle texture */ 397 initializeTexture(); 398 399 /* check if GL_POINT_SIZE_ARRAY_OES is supported 400 this is used to give each particle its own size 401 */ 402 pointSizeExtensionSupported = 403 SDL_GL_ExtensionSupported("GL_OES_point_size_array"); 404 405 /* set up some OpenGL state */ 406 glDisable(GL_DEPTH_TEST); 407 glDisable(GL_CULL_FACE); 408 409 glMatrixMode(GL_MODELVIEW); 410 glLoadIdentity(); 411 412 SDL_GetWindowSize(window, &screen_w, &screen_h); 413 glViewport(0, 0, screen_w, screen_h); 414 415 glMatrixMode(GL_PROJECTION); 416 glLoadIdentity(); 417 glOrthof((GLfloat) 0, 418 (GLfloat) screen_w, 419 (GLfloat) screen_h, 420 (GLfloat) 0, 0.0, 1.0); 421 422 glEnable(GL_TEXTURE_2D); 423 glEnable(GL_BLEND); 424 glBlendFunc(GL_SRC_ALPHA, GL_ONE); 425 glEnableClientState(GL_VERTEX_ARRAY); 426 glEnableClientState(GL_COLOR_ARRAY); 427 428 glEnable(GL_POINT_SPRITE_OES); 429 glTexEnvi(GL_POINT_SPRITE_OES, GL_COORD_REPLACE_OES, 1); 430 431 if (pointSizeExtensionSupported) { 432 /* we use this to set the sizes of all the particles */ 433 glEnableClientState(GL_POINT_SIZE_ARRAY_OES); 434 } else { 435 /* if extension not available then all particles have size 10 */ 436 glPointSize(10); 437 } 438 439 done = 0; 440 /* enter main loop */ 441 while (!done) { 442 startFrame = SDL_GetTicks(); 443 SDL_Event event; 444 while (SDL_PollEvent(&event)) { 445 if (event.type == SDL_QUIT) { 446 done = 1; 447 } 448 if (event.type == SDL_MOUSEBUTTONDOWN) { 449 int x, y; 450 SDL_GetMouseState(&x, &y); 451 spawnEmitterParticle(x, y); 452 } 453 } 454 stepParticles(); 455 drawParticles(); 456 SDL_GL_SwapWindow(window); 457 endFrame = SDL_GetTicks(); 458 459 /* figure out how much time we have left, and then sleep */ 460 delay = MILLESECONDS_PER_FRAME - (endFrame - startFrame); 461 if (delay > MILLESECONDS_PER_FRAME) { 462 delay = MILLESECONDS_PER_FRAME; 463 } 464 if (delay > 0) { 465 SDL_Delay(delay); 466 } 467 } 468 469 /* delete textures */ 470 glDeleteTextures(1, &particleTextureID); 471 /* shutdown SDL */ 472 SDL_Quit(); 473 474 return 0; 475}