Xcode-iPhoneOS/Demos/src/fireworks.c
author Sam Lantinga <slouken@libsdl.org>
Sun, 13 Mar 2011 11:17:11 -0700
changeset 5483 ccec742c9b7d
parent 5365 db3c6ebcc954
permissions -rw-r--r--
Removed unreferenced variable
     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 
    18 static GLuint particleTextureID;        /* OpenGL particle texture id */
    19 static 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 */
    26 enum particleType
    27 {
    28     emitter = 0,
    29     trail,
    30     dust
    31 };
    32 /*
    33 	struct particle is used to describe each particle displayed on screen
    34 */
    35 struct 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 
    47 static int num_active_particles;        /* how many members of the particle array are actually being drawn / animated? */
    48 static int screen_w, screen_h;
    49 
    50 /* function declarations */
    51 void spawnTrailFromEmitter(struct particle *emitter);
    52 void spawnEmitterParticle(GLfloat x, GLfloat y);
    53 void explodeEmitter(struct particle *emitter);
    54 void initializeParticles(void);
    55 void initializeTexture();
    56 int nextPowerOfTwo(int x);
    57 void drawParticles();
    58 void stepParticles(void);
    59 
    60 /*	helper function (used in texture loading)
    61 	returns next power of two greater than or equal to x
    62 */
    63 int
    64 nextPowerOfTwo(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 */
    76 void
    77 stepParticles(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 */
   156 void
   157 drawParticles()
   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 */
   182 void
   183 explodeEmitter(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 */
   224 void
   225 spawnTrailFromEmitter(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 */
   260 void
   261 spawnEmitterParticle(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 */
   309 void
   310 initializeParticles(void)
   311 {
   312     num_active_particles = 0;
   313 }
   314 
   315 /*
   316 	loads the particle texture
   317  */
   318 void
   319 initializeTexture()
   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 
   361 int
   362 main(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 | SDL_WINDOW_SHOWN |
   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 }