cscg22-gearboy

CSCG 2022 Challenge 'Gearboy'
git clone https://git.sinitax.com/sinitax/cscg22-gearboy
Log | Files | Refs | sfeed.txt

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}