sdl

FORK: Simple Directmedia Layer
git clone https://git.neptards.moe/neptards/sdl.git
Log | Files | Refs

fireworks.c (15454B)


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