view XCodeiPhoneOS/Demos/src/fireworks.c @ 2417:ac26bd83db1f gsoc2008_iphone

Cleaned up code, added comments, added randomized colors, added check for extension GL_POINT_SIZE_ARRAY_OES, which not all OpenGL ES systems have.
author Holmes Futrell <hfutrell@umail.ucsb.edu>
date Wed, 13 Aug 2008 23:10:51 +0000
parents 36bcf13ccb48
children
line wrap: on
line source

/*
 *	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;
}