Home » Intermediate » Lesson 4: Starfield Particle Engine OpenGL

Lesson 4: Starfield Particle Engine OpenGL

 

We introduce a simple particle engine. A particle engine can be used for many purposes in 3d graphics such as fire, explosions, lasers, stars and many other fun things! :-)

To make a particle engine, we need to have a particle set.  The set consists of N elements where each elements represents a star. A star itself can be defined by a set of variables including vertex position, size, colour, speed and others. Stars could be defined in a class or struct, but we keep things very basic in this tutorial.

First we define a set of stars:

int NR_STARS = 1024;
float starx[1024],stary[1024],starz[1024];

In this example, every star is actually a flat square with a transparent texture. To make textures transparent we need to enable blending.

void drawStar() {

    glBegin(GL_QUADS);
      glTexCoord2f( 0.0f, 1.0f ); glVertex3f( -1.0f, -1.0f, 1.0f );
      glTexCoord2f( 1.0f, 1.0f ); glVertex3f(  1.0f, -1.0f, 1.0f );
      glTexCoord2f( 1.0f, 0.0f ); glVertex3f(  1.0f,  1.0f, 1.0f );
      glTexCoord2f( 0.0f, 0.0f ); glVertex3f( -1.0f,  1.0f, 1.0f );
    glEnd( );
}

And turn on blending, as mentioned :-)

// turn on alpha blending for transparency
glEnable(GL_BLEND);     // Turn Blending On
glDisable(GL_DEPTH_TEST);   // Turn Depth Testing Off
glBlendFunc(GL_SRC_ALPHA,GL_ONE);

Finally we need to draw and update the star position.
We have put them in one loop to increase speed:

    int i = 0;
    for (i = 0; i 
              

Full code:

/*
*  OpenGL Textured Cube
*  https://talkera.org.cp-in-1.webhostbox.net/opengl/  
*  http://openglsamples.sf.net
*
*  Compilation trough:
*     gcc cube2.c -lglut -lGL -lGLU -lSDL -lSDL_image
*
* Note lSDL_image is added for PNG support!
*
*/


#include 
#include 
#include 
#include 
#include "SDL/SDL.h"
#include 

/* screen width, height, and bit depth */
#define SCREEN_WIDTH  640
#define SCREEN_HEIGHT 480
#define SCREEN_BPP     16

/* Setup useful booleans */
#define TRUE  1
#define FALSE 0

/* This is our SDL surface */
SDL_Surface *surface;

/* Used for rotating the cube */
GLfloat rotationXaxis; 
GLfloat rotationYaxis; 
GLfloat rotationZaxis; 

/* Storage For Textures */
GLuint texture[3]; 

/* Release resources and quit  */
void Quit( int returnCode ) {
    SDL_Quit( );
    exit( returnCode );
}

int NR_STARS = 1024;
float starx[1024],stary[1024],starz[1024];


/* Loads in a bitmap as a GL texture */
int LoadGLTextures( ) {
    int Status = FALSE;

    /* Create storage space for the texture */
    SDL_Surface *TextureImage[1]; 

    /* Load The Bitmap into Memory */
    if ((TextureImage[0] = IMG_Load("data/fire2.jpg"))) {
            Status = TRUE;
            glGenTextures( 1, &texture[0] );
            glBindTexture( GL_TEXTURE_2D, texture[0] );
            glTexImage2D( GL_TEXTURE_2D, 0, 3, TextureImage[0]->w,
                          TextureImage[0]->h, 0, GL_RGB,
                          GL_UNSIGNED_BYTE, TextureImage[0]->pixels );
            glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
            glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
        }

    /* Free up some memory */
    if ( TextureImage[0] )
            SDL_FreeSurface( TextureImage[0] );


    // second texture
    /* Load The Bitmap into Memory */
    if ((TextureImage[0] = IMG_Load("data/fire.jpg"))) {
            Status = TRUE;
            glGenTextures( 1, &texture[1] );
            glBindTexture( GL_TEXTURE_2D, texture[1] );
            glTexImage2D( GL_TEXTURE_2D, 0, 3, TextureImage[0]->w,
                          TextureImage[0]->h, 0, GL_RGB,
                          GL_UNSIGNED_BYTE, TextureImage[0]->pixels );
            glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
            glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
        }

    /* Free up some memory */
    if ( TextureImage[0] )
            SDL_FreeSurface( TextureImage[0] );

    return Status;
}

/* function to reset our viewport after a window resize */
int resizeWindow( int width, int height ) {
    /* Height / width ration */
    GLfloat ratio;
 
    /* Protect against a divide by zero */
    if ( height == 0 )
        height = 1;

    ratio = ( GLfloat )width / ( GLfloat )height;

    /* Setup our viewport. */
    glViewport( 0, 0, ( GLint )width, ( GLint )height );

    /*
     * change to the projection matrix and set
     * our viewing volume.
     */
    glMatrixMode( GL_PROJECTION );
    glLoadIdentity( );

    /* Set our perspective */
    gluPerspective( 45.0f, ratio, 0.1f, 100.0f );

    /* Make sure we're chaning the model view and not the projection */
    glMatrixMode( GL_MODELVIEW );

    /* Reset The View */
    glLoadIdentity( );

    return( TRUE );
}

/* function to handle key press events */
void handleKeyPress( SDL_keysym *keysym )
{
    switch ( keysym->sym )
        {
        case SDLK_ESCAPE:
            /* ESC key was pressed */
            Quit( 0 );
            break;
        case SDLK_F1:
            /* F1 key was pressed
             * this toggles fullscreen mode
             */
            SDL_WM_ToggleFullScreen( surface );
            break;
        default:
            break;
        }

    return;
}

/* OpenGL initialization function */
int initGL( GLvoid )
{

    /* Load in the texture */
    if ( !LoadGLTextures( ) )
        return FALSE;

    /* Enable Texture Mapping ( NEW ) */
    glEnable( GL_TEXTURE_2D );

    /* Enable smooth shading */
    glShadeModel( GL_SMOOTH );

    /* Set the background black */
    glClearColor( 0.0f, 0.0f, 0.0f, 0.5f );

    /* Depth buffer setup */
    glClearDepth( 1.0f );

    /* Enables Depth Testing */
    glEnable( GL_DEPTH_TEST );

    /* The Type Of Depth Test To Do */
    glDepthFunc( GL_LEQUAL );

    /* Really Nice Perspective Calculations */
    glHint( GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST );

    return( TRUE );
}

void initStars() {
  int i = 0;
  for (i = 0; i hw_available )
        videoFlags |= SDL_HWSURFACE;
    else
        videoFlags |= SDL_SWSURFACE;

    /* This checks if hardware blits can be done */
    if ( videoInfo->blit_hw )
        videoFlags |= SDL_HWACCEL;

    /* Sets up OpenGL double buffering */
    SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 );

    /* get a SDL surface */
    surface = SDL_SetVideoMode( SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_BPP,
                                videoFlags );

    /* Verify there is a surface */
    if ( !surface )
        {
            fprintf( stderr,  "Video mode set failed: %s\n", SDL_GetError( ) );
            Quit( 1 );
        }

    /* initialize OpenGL */
    initGL( );

    initStars();

    /* resize the initial window */
    resizeWindow( SCREEN_WIDTH, SCREEN_HEIGHT );

    /* wait for events */
    while ( !done )
        {
            /* handle the events in the queue */

            while ( SDL_PollEvent( &event ) )
                {
                    switch( event.type )
                        {
                        case SDL_ACTIVEEVENT:
                            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 get here */
    return( 0 );
}

We have used this texture: