commit 8334c53fc245a125bce936daf289457a54f95e41 Author: Andrew Kesterson Date: Mon May 18 12:33:52 2026 -0400 Commit code circa 2006 diff --git a/Makefile b/Makefile new file mode 100755 index 0000000..5c6bab4 --- /dev/null +++ b/Makefile @@ -0,0 +1,22 @@ +SRC = main.cpp +OBJ = main.o + +CC = gcc +CXX = g++ +LD = $(CXX) + +CFLAGS = -g -g2 -p -pg -ggdb -c `sdl-config --cflags` +LDFLAGS = -g -g2 -pg -ggdb -lSDL -lX11 -lGL -lGLU `sdl-config --libs` + +TARGET = glparticletest + +all : $(OBJ) + $(LD) $(LDFLAGS) -o $(TARGET) $(OBJ) + +%.o : %.cpp + $(CXX) $(CFLAGS) -o $@ $< + +.PHONY : clean +clean: + rm *.o + rm $(TARGET) diff --git a/common.h b/common.h new file mode 100755 index 0000000..1682b4a --- /dev/null +++ b/common.h @@ -0,0 +1,55 @@ +#ifndef __COMMON_H__ +#define __COMMON_H__ + +#include +#include +#include +#include + + +/* global defines */ +#define SCREEN_WIDTH 640 +#define SCREEN_HEIGHT 480 +#define SCREEN_BPP 16 +#define TRUE 1 +#define FALSE 0 + +/* main screen surface */ +SDL_Surface *surface; + +typedef struct +{ + GLfloat x; + GLfloat y; + GLfloat z; +} Vector; + +Vector cameraPosition = {0.0F, 0.0F, 0.0F}; +Vector cameraLook = {0.0F, 0.0F, -1.0F}; + +char *pTextures[] = {"pBlue.bmp", "pGreen.pcx", "pRed.pcx"}; + +/* copy v2 to v1 */ +void vectorCopy(Vector *v1, Vector *v2) +{ + v1->x = v2->x; + v1->y = v2->y; + v1->z = v2->y; +} + +/* returns a floating point value between -ceiling and ceiling */ +GLfloat frand(GLfloat ceiling) +{ + if ( ((rand() / ((RAND_MAX+1)/2) )) < 0) + return (rand() / ( ((float) RAND_MAX+1) / -ceiling)); + else + return (rand() / ( ((float) RAND_MAX+1) / ceiling)); +} + +#include "particle.h" +#include "draw.h" +#include "init.h" + +PEmitter theEmitter[2]; + +#endif // __COMMON_H__ 1 diff --git a/draw.h b/draw.h new file mode 100755 index 0000000..48f01fb --- /dev/null +++ b/draw.h @@ -0,0 +1,94 @@ + +/* anything related to drawing is in here + * including texture loading and setting */ + +#ifndef __DRAW_H__ +#define __DRAW_H__ + +#include "common.h" + +extern PEmitter theEmitter[2]; + +int drawGLScene( GLvoid ) +{ + /* These are to calculate our fps */ + static GLint T0 = 0; + GLint t = 0; + static GLint Frames = 0; + + /* Clear The Screen And The Depth Buffer */ + glPointSize(3.0f); + + glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity( ); + glTranslatef(0.0f, 1.0f, -20.0f); + + + DrawEmitter(&theEmitter[0]); + DrawEmitter(&theEmitter[1]); + + /* Draw it to the screen */ + SDL_GL_SwapBuffers( ); + + Frames++; + t = SDL_GetTicks(); + if (t - T0 >= 1000) { + GLfloat seconds = (t - T0) / 1000.0; + GLfloat fps = Frames / seconds; + printf("%d frames in %g seconds = %g FPS (new time %g)\n", Frames, seconds, fps, (float)time(NULL)); + T0 = t; + Frames = 0; + } + + UpdateEmitter(&theEmitter[0], t - T0); + UpdateEmitter(&theEmitter[1], t - T0); + + T0 = t; + + return( TRUE ); +} + +/* function to reset our viewport after a window resize */ +int resizeWindow( int width, int height ) +{ + GLfloat ratio; + + /* Protect against a divide by zero */ + if ( height == 0 ) height = 1; + + ratio = ( GLfloat )width / ( GLfloat )height; + + /* set the viewport, viewing volume, perspective, etc etc */ + + glViewport( 0, 0, ( GLint )width, ( GLint )height ); + glMatrixMode( GL_PROJECTION ); + glLoadIdentity( ); + gluPerspective( 45.0f, ratio, 0.1f, 100.0f ); + glMatrixMode( GL_MODELVIEW ); + + /* Reset The Modelview matrix */ + glLoadIdentity( ); + + return( TRUE ); +} + +/* general OpenGL initialization function */ +int initGL( GLvoid ) +{ + + /* Setup shading, clearing, depth testing, and rendering hints */ + glShadeModel( GL_SMOOTH ); + glClearColor( 0.0f, 0.0f, 0.0f, 0.0f ); + glClearDepth( 1.0f ); + glEnable( GL_DEPTH_TEST ); + glDepthFunc( GL_LEQUAL ); + glHint( GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST ); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE); + + return( TRUE ); +} + +#endif // __DRAW_H__ diff --git a/init.h b/init.h new file mode 100755 index 0000000..82ebb49 --- /dev/null +++ b/init.h @@ -0,0 +1,34 @@ +#ifndef __INIT_H__ +#define __INIT_H__ + +#include "common.h" + +/* function to release/destroy our resources and restore the old desktop + * (if we've clobbered it) */ +void Quit( int returnCode ) +{ + SDL_Quit( ); + exit( returnCode ); +} + +/* function to handle key press events */ +void handleKeyPress( SDL_keysym *keysym ) +{ + switch ( keysym->sym ) + { + case SDLK_ESCAPE: + Quit( 0 ); + break; + case SDLK_F1: + /* this toggles fullscreen mode + */ + SDL_WM_ToggleFullScreen( surface ); + break; + default: + break; + } + + return; +} + +#endif // __INIT_H__ diff --git a/main.cpp b/main.cpp new file mode 100755 index 0000000..4ff64e2 --- /dev/null +++ b/main.cpp @@ -0,0 +1,205 @@ +/* + * A particle system using triangles and textured particles + * you can also switch between textured triangles and colored points + * and you can change the emitter's position/direction. + * + * The actual action goes on in particle.h, but I figured main.cpp is where + * most folks would start reading. This isn't a complete system; I had intended + * to implement textured particles, but I didn't get that implemented by my 6 am deadline. + * I'm going to go ahead and leave in the code for textured polys/quads (which + * does nothing right now) because I intend to implement that in the future. + * + * I also haven't profiled this code, so I have no idea how efficient it is. However, + * since this is my *first ever* attempt at a particle system, I don't think it's + * too bad considering I churned it out in 6 hours without anyone else's code to guide me. + * So take this as you will. :) + * + * Written with openGL and SDL (http://www.libsdl.org). + * + * Andrew Kesterson, Feb. 2005. + * + * NOTE: Textured polys are currently not implemented, but now that I have the particles + * distributing around the emitter properly and fading correctly, it is *definitely* next + * on the list. + */ + + +#include +#include + +#include "common.h" + +int main( int argc, char **argv ) +{ + /* Flags and stuff for the state machine and SDL */ + int videoFlags; + int done = FALSE; + SDL_Event event; + const SDL_VideoInfo *videoInfo; + int isActive = TRUE; + GLfloat emitterColors[2][3] = { + //{1.0, 1.0, 1.0}, + {0.0, 0.0, 0.0}, + {0.0, 0.5, 1.0}}; + Vector emitterSpeeds[2] = { + {0.00f, 0.0001f, 0.0f}, + {0.00f, 0.005f, 0.0f}}; + Vector emitterGravities[2] = { + {0.002f, -0.0f, 0.0f}, + {0.00f, -0.00001f, 0.00f}}; + Vector emitterPositions[2] = { + {0.0f, 0.0f, -50.0f}, + {0.0f, -2.0f, -50.0f}}; + Vector randPositions[2] = { + {5.7f, 5.7f, 0.0f}, + {.5f, 3.5f, .5f} + }; + Vector randSpeeds[2] = { + {0.001f, 0.001f, 0.0f}, + {0.000f, 0.00f, 0.000f} + }; + Vector randGravities[2] = { + {0.0f, 0.0f, 0.0f}, + {0.000005f, 0.0f, 0.000005f}, + }; + + srand((long)time(NULL)); + + PopulateEmitter(&theEmitter[0], + GL_TRUE, + emitterColors[0], + 0.0001F, + 1000, + &emitterGravities[0], + &emitterPositions[0], + &emitterSpeeds[0], + &randGravities[0], + &randPositions[0], + &randSpeeds[0], + 500, + GL_POINTS, + 0); + PopulateEmitter(&theEmitter[1], + GL_TRUE, + emitterColors[1], + 0.0002F, + 5000, + &emitterGravities[1], + &emitterPositions[1], + &emitterSpeeds[1], + &randGravities[1], + &randPositions[1], + &randSpeeds[1], + 1400, + GL_POINTS, + 0); + + + /* initialize SDL */ + if ( SDL_Init( SDL_INIT_VIDEO ) < 0 ) + { + fprintf( stderr, "Video initialization failed: %s\n", + SDL_GetError( ) ); + Quit( 1 ); + } + + /* Fetch the video info */ + videoInfo = SDL_GetVideoInfo( ); + + if ( !videoInfo ) + { + fprintf( stderr, "Video query failed: %s\n", + SDL_GetError( ) ); + Quit( 1 ); + } + + /* Ask SDL for a OpenGL double buffered context with various flags. */ + videoFlags = SDL_OPENGL; + videoFlags |= SDL_GL_DOUBLEBUFFER; + videoFlags |= SDL_HWPALETTE; + videoFlags |= SDL_RESIZABLE; + + /* Can we have hardware surfaces? */ + if ( videoInfo->hw_available ) + videoFlags |= SDL_HWSURFACE; + else + videoFlags |= SDL_SWSURFACE; + + /* Hardware blits? */ + if ( videoInfo->blit_hw ) + videoFlags |= SDL_HWACCEL; + + SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 ); + + /* get the main SDL surface */ + surface = SDL_SetVideoMode( SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_BPP, + videoFlags ); + + if ( !surface ) + { + fprintf( stderr, "Video mode set failed: %s\n", SDL_GetError( ) ); + Quit( 1 ); + } + + if ( initGL( ) == FALSE ) + { + fprintf( stderr, "Could not initialize OpenGL.\n" ); + Quit( 1 ); + } + + resizeWindow( SCREEN_WIDTH, SCREEN_HEIGHT ); + + /* wait for events */ + while ( !done ) + { + /* handle the events in the queue, pumped by the SDL event machine */ + + while ( SDL_PollEvent( &event ) ) + { + switch( event.type ) + { + case SDL_ACTIVEEVENT: + /* Gained or lost window focus. */ + if ( event.active.gain == 0 ) + isActive = FALSE; + else + isActive = TRUE; + break; + case SDL_VIDEORESIZE: + /* handle resize event */ + surface = SDL_SetVideoMode( event.resize.w, + event.resize.h, + 16, videoFlags ); + if ( !surface ) + { + fprintf( stderr, "Could not get a surface after resize: %s\n", SDL_GetError( ) ); + Quit( 1 ); + } + resizeWindow( event.resize.w, event.resize.h ); + break; + case SDL_KEYDOWN: + /* handle key presses */ + handleKeyPress( &event.key.keysym ); + break; + case SDL_QUIT: + /* handle quit requests */ + done = TRUE; + break; + default: + break; + } + } + + /* draw the scene */ + if ( isActive ) { + drawGLScene( ); + } + } + + /* clean ourselves up and exit */ + Quit( 0 ); + + /* Should never even get here */ + return( 0 ); +} + diff --git a/particle.h b/particle.h new file mode 100755 index 0000000..b42f433 --- /dev/null +++ b/particle.h @@ -0,0 +1,280 @@ +#ifndef __PARTICLE_H__ +#define __PARTICLE_H__ + +#include "common.h" + +/* max particles per emitter */ +#define MAX_PARTICLES 256 + +typedef struct { + GLint aliveTime; /* time the particle's been alive */ + GLint lifetime; /* how long this particle should stay alive (in ms) */ + GLfloat alpha; + Vector speed; + Vector pos; + Vector iGrav; +} Particle; + +typedef struct { + GLboolean active; /* emitter active? */ + GLint lifetime; /* base lifetime of particles from this emitter */ + GLint lifeEdge; /* the random adjustment to each particle's lifetime + (between lifeEdge and lifeEdge*2) */ + GLfloat alphaFade; /* a floating point value between 0.0 and 1.0 that tells + how much transparency should be added to the particle each frame */ + + Vector pos; /* base position of particles from this emitter */ + Vector speed; /* base speed (direction) of particles from this emitter */ + Vector pGrav; /* the base x, y, and z gravity acting on + particles from this emitter*/ + + Vector rSpeed; /* the random speed adjustment applied to particles */ + Vector rGrav; /* the random gravity adjustment applied to particles */ + Vector rPos; /* the random position adjustment applied to particles */ + + Particle particles[MAX_PARTICLES]; /* the actual particles */ + GLfloat texCoords[4][2]; // the texture coordinates for all quad or triangle particles + GLuint texHandle; /* the texture handle for all particles from this emitter*/ + GLuint pType; /* GL_POINTS, GL_TRIANGLES, or GL_QUADS.*/ + GLfloat color[3]; /* color of particles in this emitter */ + + GLfloat size[2]; /* the size in X and Y of all particles from this emitter */ + +} PEmitter; + + +void DrawParticle(Particle *particle, PEmitter *emitter) +{ + + /* set up the texturing if we're not doing points */ + if ( emitter->pType != GL_POINTS ) { + glBindTexture(GL_TEXTURE_2D, emitter->texHandle); + glEnable(GL_TEXTURE_2D); + } + + glBegin(emitter->pType); + + if ( emitter->pType == GL_TRIANGLES ) { + glColor3f(emitter->color[0], + emitter->color[1], + emitter->color[2]); + glTexCoord2f(emitter->texCoords[0][0], + emitter->texCoords[0][1]); + glVertex3f(particle->pos.x - ( emitter->size[0] / 2), + particle->pos.y - ( emitter->size[1] / 2), + particle->pos.z); + + glColor3f(emitter->color[0], + emitter->color[1], + emitter->color[2]); + glTexCoord2f(emitter->texCoords[1][0], + emitter->texCoords[1][1]); + glVertex3f(particle->pos.x + ( emitter->size[0] / 2), + particle->pos.y - ( emitter->size[1] / 2), + particle->pos.z); + + glColor3f(emitter->color[0], + emitter->color[1], + emitter->color[2]); + glTexCoord2f(emitter->texCoords[2][0], + emitter->texCoords[2][1]); + glVertex3f(particle->pos.x, + particle->pos.y + ( emitter->size[1] / 2), + particle->pos.z); + } + + else if ( emitter->pType == GL_QUADS ) { + glColor3f(emitter->color[0], + emitter->color[1], + emitter->color[2]); + glTexCoord2f(emitter->texCoords[0][0], + emitter->texCoords[0][1]); + glVertex3f(particle->pos.x - ( emitter->size[0] / 2), + particle->pos.y + ( emitter->size[1] / 2), + particle->pos.z); + + glColor3f(emitter->color[0], + emitter->color[1], + emitter->color[2]); + glTexCoord2f(emitter->texCoords[1][0], + emitter->texCoords[1][1]); + glVertex3f(particle->pos.x + ( emitter->size[0] / 2), + particle->pos.y - ( emitter->size[1] / 2), + particle->pos.z); + + glColor3f(emitter->color[0], + emitter->color[1], + emitter->color[2]); + glTexCoord2f(emitter->texCoords[2][0], + emitter->texCoords[2][1]); + glVertex3f(particle->pos.x + ( emitter->size[0] / 2), + particle->pos.y - ( emitter->size[1] / 2), + particle->pos.z); + + glColor3f(emitter->color[0], + emitter->color[1], + emitter->color[2]); + glTexCoord2f(emitter->texCoords[3][0], + emitter->texCoords[3][1]); + glVertex3f(particle->pos.x + ( emitter->size[0] / 2), + particle->pos.y + ( emitter->size[1] / 2), + particle->pos.z); + } + else { + glColor4f(emitter->color[0], emitter->color[1], emitter->color[2], particle->alpha); + glVertex3f(particle->pos.x, particle->pos.y, particle->pos.z); + + } + + glEnd(); + + glDisable(GL_TEXTURE_2D); +} + +GLuint DrawEmitter(PEmitter *emitter) +{ + /* this function assumes the viewport and etc has already been setup + * (e.g., call this from within your main GL drawing function) */ + + int i = 0; + Particle *currParticle; + + if ( !emitter ) { + return 1; + } + + if ( emitter->active != GL_TRUE ) { + return 0; // emitter not active, but this isn't an error + } + + for ( i = 0 ; i < MAX_PARTICLES ; i++ ) { + currParticle = &emitter->particles[i]; + + /* draw the particle given its type */ + DrawParticle(currParticle, emitter); + } + return 0; +} + +void ResetParticle(Particle *particle, PEmitter *emitter) +{ + particle->aliveTime = 0; + + /* get a new lifetime for the particle based on the base from the emitter + random adj */ + particle->lifetime = emitter->lifetime + (rand() % (emitter->lifeEdge*2)) - emitter->lifeEdge; + particle->alpha = 1.0F; + + /* reset the particle's x, y, z position from the emitter base */ + vectorCopy(&particle->pos, &emitter->pos); + if ( emitter->rPos.x != 0.0f) + particle->pos.x += frand( emitter->rPos.x); + if ( emitter->rPos.y != 0.0f ) + particle->pos.y += frand( emitter->rPos.y ); + if ( emitter->rPos.z != 0.0f ) + particle->pos.z += frand( emitter->rPos.z ); + + /* reset the particle's gravity from the base gravity of the emitter + rand adjust */ + vectorCopy(&particle->iGrav, &emitter->pGrav); + if ( emitter->rGrav.x != 0.0f ) + particle->iGrav.x += frand( emitter->rGrav.x ); + if ( emitter->rGrav.y != 0.0f ) + particle->iGrav.y += frand( emitter->rGrav.y ); + if ( emitter->rGrav.z != 0.0f ) + particle->iGrav.z += frand( emitter->rGrav.z ); + + /* reset the particle's speed from the base gravity of the emitter + rand adjust */ + vectorCopy(&particle->speed, &emitter->speed); + if ( emitter->rSpeed.x != 0.0f ) + particle->speed.x += frand( emitter->rSpeed.x); + if ( emitter->rSpeed.y != 0.0f ) + particle->speed.y += frand( emitter->rSpeed.y); + if ( emitter->rSpeed.z != 0.0f ) + particle->speed.z += frand( emitter->rSpeed.z); +} + +void UpdateEmitter(PEmitter *emitter, GLuint timePassed) +{ + /* here's where we do the emitter's logic. It's assumed + * we're being called once per frame, so we don't do any + * time calculations, other than updating the lifetimes + * ... timePassed is the number of ms passed since the last call */ + + int i = 0; + Particle *currParticle; + + for ( i = 0 ; i < MAX_PARTICLES ; i++ ) { + currParticle = &emitter->particles[i]; + + currParticle->aliveTime += timePassed; + + if ( currParticle->aliveTime >= currParticle->lifetime ) { + /* this particle has died; set it to inactive and reset its lifetime/pos/dir */ + ResetParticle(currParticle,emitter); + continue; + } + + /* apply gravity and such to the particle */ + + currParticle->speed.x += currParticle->iGrav.x; + currParticle->speed.y += currParticle->iGrav.y; + currParticle->speed.z += currParticle->iGrav.z; + + currParticle->pos.x += (currParticle->speed.x); + currParticle->pos.y += (currParticle->speed.y); + currParticle->pos.z += (currParticle->speed.z); + + /* the alpha is a constant drop over the lifetime of the particle */ + currParticle->alpha -= emitter->alphaFade; + } +} + +GLuint PopulateEmitter(PEmitter *emitter, + GLboolean active, + GLfloat color[], + GLfloat alphaFade, + GLint lifetime, + Vector *pGrav, + Vector *pos, + Vector *speed, + Vector *rgrav, + Vector *rpos, + Vector *rspeed, + GLint lifeEdge, + GLuint pType, + GLuint texHandle) +{ + int i = 0; + + if ( !emitter ) { + return 1; + } + + emitter->active = active; + + emitter->color[0] = color[0]; + emitter->color[1] = color[1]; + emitter->color[2] = color[2]; + + emitter->lifetime = lifetime; + emitter->lifeEdge = lifeEdge; + emitter->alphaFade = alphaFade; + + vectorCopy(&emitter->pGrav, pGrav); + vectorCopy(&emitter->pos, pos); + vectorCopy(&emitter->speed, speed); + vectorCopy(&emitter->rSpeed, rspeed); + vectorCopy(&emitter->rPos, rpos); + vectorCopy(&emitter->rGrav, rgrav); + + + emitter->pType = pType; + emitter->texHandle = texHandle; + + for ( i = 0; i < MAX_PARTICLES; i++ ) { + ResetParticle(&emitter->particles[i],emitter); + } + + return 0; +} + +#endif // __PARTICLE_H__