diff XCodeiPhoneOS/Demos/src/fireworks.c @ 2765:f55c87ae336b

Final merge of Google Summer of Code 2008 work... Bring SDL to iPhone and iPod Touch by Holmes Futrell, mentored by Sam Lantinga
author Sam Lantinga <slouken@libsdl.org>
date Sat, 04 Oct 2008 06:46:59 +0000
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/XCodeiPhoneOS/Demos/src/fireworks.c	Sat Oct 04 06:46:59 2008 +0000
@@ -0,0 +1,461 @@
+/*
+ *	fireworks.c
+ *	written by Holmes Futrell
+ *	use however you want
+ */
+
+#include "SDL.h"
+#include "SDL_opengles.h"
+#include "common.h"
+#include <math.h>
+#include <time.h>
+
+#define MILLESECONDS_PER_FRAME 16       /* about 60 frames per second */
+#define ACCEL 0.0001f           /* acceleration due to gravity, units in pixels per millesecond squared */
+#define WIND_RESISTANCE 0.00005f        /* acceleration per unit velocity due to wind resistance */
+#define MAX_PARTICLES 2000      /* maximum number of particles displayed at once */
+
+static GLuint particleTextureID;        /* OpenGL particle texture id */
+static SDL_bool pointSizeExtensionSupported;    /* is GL_OES_point_size_array supported ? */
+/* 
+	used to describe what type of particle a given struct particle is.
+	emitter - this particle flies up, shooting off trail particles, then finally explodes into dust particles.
+	trail	- shoots off, following emitter particle
+	dust	- radiates outwards from emitter explosion
+*/
+enum particleType
+{
+    emitter = 0,
+    trail,
+    dust
+};
+/*
+	struct particle is used to describe each particle displayed on screen
+*/
+struct particle
+{
+    GLfloat x;                  /* x position of particle */
+    GLfloat y;                  /* y position of particle */
+    GLubyte color[4];           /* rgba color of particle */
+    GLfloat size;               /* size of particle in pixels */
+    GLfloat xvel;               /* x velocity of particle in pixels per milesecond */
+    GLfloat yvel;               /* y velocity of particle in pixels per millescond */
+    int isActive;               /* if not active, then particle is overwritten */
+    enum particleType type;     /* see enum particleType */
+} particles[MAX_PARTICLES];     /* this array holds all our particles */
+
+static int num_active_particles;        /* how many members of the particle array are actually being drawn / animated? */
+
+/* function declarations */
+void spawnTrailFromEmitter(struct particle *emitter);
+void spawnEmitterParticle(GLfloat x, GLfloat y);
+void explodeEmitter(struct particle *emitter);
+void initializeParticles(void);
+void initializeTexture();
+int nextPowerOfTwo(int x);
+void drawParticles();
+void stepParticles(void);
+
+/*	helper function (used in texture loading)
+	returns next power of two greater than or equal to x
+*/
+int
+nextPowerOfTwo(int x)
+{
+    int val = 1;
+    while (val < x) {
+        val *= 2;
+    }
+    return val;
+}
+
+/*	
+	steps each active particle by timestep MILLESECONDS_PER_FRAME
+*/
+void
+stepParticles(void)
+{
+    int i;
+    struct particle *slot = particles;
+    struct particle *curr = particles;
+    for (i = 0; i < num_active_particles; i++) {
+        /* is the particle actually active, or is it marked for deletion? */
+        if (curr->isActive) {
+            /* is the particle off the screen? */
+            if (curr->y > SCREEN_HEIGHT)
+                curr->isActive = 0;
+            else if (curr->y < 0)
+                curr->isActive = 0;
+            if (curr->x > SCREEN_WIDTH)
+                curr->isActive = 0;
+            else if (curr->x < 0)
+                curr->isActive = 0;
+
+            /* step velocity, then step position */
+            curr->yvel += ACCEL * MILLESECONDS_PER_FRAME;
+            curr->xvel += 0.0f;
+            curr->y += curr->yvel * MILLESECONDS_PER_FRAME;
+            curr->x += curr->xvel * MILLESECONDS_PER_FRAME;
+
+            /* particle behavior */
+            if (curr->type == emitter) {
+                /* if we're an emitter, spawn a trail */
+                spawnTrailFromEmitter(curr);
+                /* if we've reached our peak, explode */
+                if (curr->yvel > 0.0) {
+                    explodeEmitter(curr);
+                }
+            } else {
+                float speed =
+                    sqrt(curr->xvel * curr->xvel + curr->yvel * curr->yvel);
+                /*      if wind resistance is not powerful enough to stop us completely,
+                   then apply winde resistance, otherwise just stop us completely */
+                if (WIND_RESISTANCE * MILLESECONDS_PER_FRAME < speed) {
+                    float normx = curr->xvel / speed;
+                    float normy = curr->yvel / speed;
+                    curr->xvel -=
+                        normx * WIND_RESISTANCE * MILLESECONDS_PER_FRAME;
+                    curr->yvel -=
+                        normy * WIND_RESISTANCE * MILLESECONDS_PER_FRAME;
+                } else {
+                    curr->xvel = curr->yvel = 0;        /* stop particle */
+                }
+
+                if (curr->color[3] <= MILLESECONDS_PER_FRAME * 0.1275f) {
+                    /* if this next step will cause us to fade out completely
+                       then just mark for deletion */
+                    curr->isActive = 0;
+                } else {
+                    /* otherwise, let's fade a bit more */
+                    curr->color[3] -= MILLESECONDS_PER_FRAME * 0.1275f;
+                }
+
+                /* if we're a dust particle, shrink our size */
+                if (curr->type == dust)
+                    curr->size -= MILLESECONDS_PER_FRAME * 0.010f;
+
+            }
+
+            /* if we're still active, pack ourselves in the array next
+               to the last active guy (pack the array tightly) */
+            if (curr->isActive)
+                *(slot++) = *curr;
+        }                       /* endif (curr->isActive) */
+        curr++;
+    }
+    /* the number of active particles is computed as the difference between
+       old number of active particles, where slot points, and the 
+       new size of the array, where particles points */
+    num_active_particles = slot - particles;
+}
+
+/*
+	This draws all the particles shown on screen
+*/
+void
+drawParticles()
+{
+
+    /* draw the background */
+    glClear(GL_COLOR_BUFFER_BIT);
+
+    /* set up the position and color pointers */
+    glVertexPointer(2, GL_FLOAT, sizeof(struct particle), particles);
+    glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(struct particle),
+                   particles[0].color);
+
+    if (pointSizeExtensionSupported) {
+        /* pass in our array of point sizes */
+        glPointSizePointerOES(GL_FLOAT, sizeof(struct particle),
+                              &(particles[0].size));
+    }
+
+    /* draw our particles! */
+    glDrawArrays(GL_POINTS, 0, num_active_particles);
+
+    /* update screen */
+    SDL_RenderPresent();
+
+}
+
+/*
+	This causes an emitter to explode in a circular bloom of dust particles
+*/
+void
+explodeEmitter(struct particle *emitter)
+{
+    /* first off, we're done with this particle, so turn active off */
+    emitter->isActive = 0;
+    int i;
+    for (i = 0; i < 200; i++) {
+
+        if (num_active_particles >= MAX_PARTICLES)
+            return;
+
+        /* come up with a random angle and speed for new particle */
+        float theta = randomFloat(0, 2.0f * 3.141592);
+        float exponent = 3.0f;
+        float speed = randomFloat(0.00, powf(0.17, exponent));
+        speed = powf(speed, 1.0f / exponent);
+
+        /*select the particle at the end of our array */
+        struct particle *p = &particles[num_active_particles];
+
+        /* set the particles properties */
+        p->xvel = speed * cos(theta);
+        p->yvel = speed * sin(theta);
+        p->x = emitter->x + emitter->xvel;
+        p->y = emitter->y + emitter->yvel;
+        p->isActive = 1;
+        p->type = dust;
+        p->size = 15;
+        /* inherit emitter's color */
+        p->color[0] = emitter->color[0];
+        p->color[1] = emitter->color[1];
+        p->color[2] = emitter->color[2];
+        p->color[3] = 255;
+        /* our array has expanded at the end */
+        num_active_particles++;
+    }
+
+}
+
+/*
+	This spawns a trail particle from an emitter
+*/
+void
+spawnTrailFromEmitter(struct particle *emitter)
+{
+
+    if (num_active_particles >= MAX_PARTICLES)
+        return;
+
+    /* select the particle at the slot at the end of our array */
+    struct particle *p = &particles[num_active_particles];
+
+    /* set position and velocity to roughly that of the emitter */
+    p->x = emitter->x + randomFloat(-3.0, 3.0);
+    p->y = emitter->y + emitter->size / 2.0f;
+    p->xvel = emitter->xvel + randomFloat(-0.005, 0.005);
+    p->yvel = emitter->yvel + 0.1;
+
+    /* set the color to a random-ish orangy type color */
+    p->color[0] = (0.8f + randomFloat(-0.1, 0.0)) * 255;
+    p->color[1] = (0.4f + randomFloat(-0.1, 0.1)) * 255;
+    p->color[2] = (0.0f + randomFloat(0.0, 0.2)) * 255;
+    p->color[3] = (0.7f) * 255;
+
+    /* set other attributes */
+    p->size = 10;
+    p->type = trail;
+    p->isActive = 1;
+
+    /* our array has expanded at the end */
+    num_active_particles++;
+
+}
+
+/*
+	spawns a new emitter particle at the bottom of the screen
+    destined for the point (x,y).
+*/
+void
+spawnEmitterParticle(GLfloat x, GLfloat y)
+{
+
+    if (num_active_particles >= MAX_PARTICLES)
+        return;
+
+    /* find particle at endpoint of array */
+    struct particle *p = &particles[num_active_particles];
+
+    /* set the color randomly */
+    switch (rand() % 4) {
+    case 0:
+        p->color[0] = 255;
+        p->color[1] = 100;
+        p->color[2] = 100;
+        break;
+    case 1:
+        p->color[0] = 100;
+        p->color[1] = 255;
+        p->color[2] = 100;
+        break;
+    case 2:
+        p->color[0] = 100;
+        p->color[1] = 100;
+        p->color[2] = 255;
+        break;
+    case 3:
+        p->color[0] = 255;
+        p->color[1] = 150;
+        p->color[2] = 50;
+        break;
+    }
+    p->color[3] = 255;
+    /* set position to (x, SCREEN_HEIGHT) */
+    p->x = x;
+    p->y = SCREEN_HEIGHT;
+    /* set velocity so that terminal point is (x,y) */
+    p->xvel = 0;
+    p->yvel = -sqrt(2 * ACCEL * (SCREEN_HEIGHT - y));
+    /* set other attributes */
+    p->size = 10;
+    p->type = emitter;
+    p->isActive = 1;
+    /* our array has expanded at the end */
+    num_active_particles++;
+}
+
+/* just sets the endpoint of the particle array to element zero */
+void
+initializeParticles(void)
+{
+    num_active_particles = 0;
+}
+
+/*
+	loads the particle texture
+ */
+void
+initializeTexture()
+{
+
+    int bpp;                    /* texture bits per pixel */
+    Uint32 Rmask, Gmask, Bmask, Amask;  /* masks for pixel format passed into OpenGL */
+    SDL_Surface *bmp_surface;   /* the bmp is loaded here */
+    SDL_Surface *bmp_surface_rgba8888;  /* this serves as a destination to convert the BMP
+                                           to format passed into OpenGL */
+
+    bmp_surface = SDL_LoadBMP("stroke.bmp");
+    if (bmp_surface == NULL) {
+        fatalError("could not load stroke.bmp");
+    }
+
+    /* Grab info about format that will be passed into OpenGL */
+    SDL_PixelFormatEnumToMasks(SDL_PIXELFORMAT_ABGR8888, &bpp, &Rmask, &Gmask,
+                               &Bmask, &Amask);
+    /* Create surface that will hold pixels passed into OpenGL */
+    bmp_surface_rgba8888 =
+        SDL_CreateRGBSurface(0, bmp_surface->w, bmp_surface->h, bpp, Rmask,
+                             Gmask, Bmask, Amask);
+    /* Blit to this surface, effectively converting the format */
+    SDL_BlitSurface(bmp_surface, NULL, bmp_surface_rgba8888, NULL);
+
+    glGenTextures(1, &particleTextureID);
+    glBindTexture(GL_TEXTURE_2D, particleTextureID);
+    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
+                 nextPowerOfTwo(bmp_surface->w),
+                 nextPowerOfTwo(bmp_surface->h),
+                 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+    /* this is where we actually pass in the pixel data */
+    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, bmp_surface->w, bmp_surface->h, 0,
+                 GL_RGBA, GL_UNSIGNED_BYTE, bmp_surface_rgba8888->pixels);
+
+    /* free bmp surface and converted bmp surface */
+    SDL_FreeSurface(bmp_surface);
+    SDL_FreeSurface(bmp_surface_rgba8888);
+
+}
+
+int
+main(int argc, char *argv[])
+{
+
+    SDL_WindowID windowID;      /* ID of main window */
+    Uint32 startFrame;          /* time frame began to process */
+    Uint32 endFrame;            /* time frame ended processing */
+    Uint32 delay;               /* time to pause waiting to draw next frame */
+    int done;                   /* should we clean up and exit? */
+
+    /* initialize SDL */
+    if (SDL_Init(SDL_INIT_VIDEO) < 0) {
+        fatalError("Could not initialize SDL");
+    }
+    /* seed the random number generator */
+    srand(time(NULL));
+    /*      
+       request some OpenGL parameters
+       that may speed drawing
+     */
+    SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 5);
+    SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 6);
+    SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 5);
+    SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 0);
+    SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 0);
+    SDL_GL_SetAttribute(SDL_GL_RETAINED_BACKING, 0);
+    SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, 1);
+
+    /* create main window and renderer */
+    windowID = SDL_CreateWindow(NULL, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT,
+                                SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN |
+                                SDL_WINDOW_BORDERLESS);
+    SDL_CreateRenderer(windowID, 0, 0);
+
+    /* load the particle texture */
+    initializeTexture();
+
+    /*      check if GL_POINT_SIZE_ARRAY_OES is supported
+       this is used to give each particle its own size
+     */
+    pointSizeExtensionSupported =
+        SDL_GL_ExtensionSupported("GL_OES_point_size_array");
+
+    /* set up some OpenGL state */
+    glEnable(GL_TEXTURE_2D);
+    glEnable(GL_BLEND);
+    glBlendFunc(GL_SRC_ALPHA, GL_ONE);
+    glEnableClientState(GL_VERTEX_ARRAY);
+    glEnableClientState(GL_COLOR_ARRAY);
+
+    glEnable(GL_POINT_SPRITE_OES);
+    glTexEnvi(GL_POINT_SPRITE_OES, GL_COORD_REPLACE_OES, 1);
+
+    if (pointSizeExtensionSupported) {
+        /* we use this to set the sizes of all the particles */
+        glEnableClientState(GL_POINT_SIZE_ARRAY_OES);
+    } else {
+        /* if extension not available then all particles have size 10 */
+        glPointSize(10);
+    }
+
+    done = 0;
+    /* enter main loop */
+    while (!done) {
+        startFrame = SDL_GetTicks();
+        SDL_Event event;
+        while (SDL_PollEvent(&event)) {
+            if (event.type == SDL_QUIT) {
+                done = 1;
+            }
+            if (event.type == SDL_MOUSEBUTTONDOWN) {
+                int which = event.button.which;
+                int x, y;
+                SDL_SelectMouse(which);
+                SDL_GetMouseState(&x, &y);
+                spawnEmitterParticle(x, y);
+            }
+        }
+        stepParticles();
+        drawParticles();
+        endFrame = SDL_GetTicks();
+
+        /* figure out how much time we have left, and then sleep */
+        delay = MILLESECONDS_PER_FRAME - (endFrame - startFrame);
+        if (delay > MILLESECONDS_PER_FRAME) {
+            delay = MILLESECONDS_PER_FRAME;
+        }
+        if (delay > 0) {
+            SDL_Delay(delay);
+        }
+    }
+
+    /* delete textures */
+    glDeleteTextures(1, &particleTextureID);
+    /* shutdown SDL */
+    SDL_Quit();
+
+    return 0;
+}