Home | History | Annotate | Download | only in ParticleSystem
      1 //
      2 // Book:      OpenGL(R) ES 2.0 Programming Guide
      3 // Authors:   Aaftab Munshi, Dan Ginsburg, Dave Shreiner
      4 // ISBN-10:   0321502795
      5 // ISBN-13:   9780321502797
      6 // Publisher: Addison-Wesley Professional
      7 // URLs:      http://safari.informit.com/9780321563835
      8 //            http://www.opengles-book.com
      9 //
     10 
     11 // ParticleSystem.c
     12 //
     13 //    This is an example that demonstrates rendering a particle system
     14 //    using a vertex shader and point sprites.
     15 //
     16 #include <stdlib.h>
     17 #include <math.h>
     18 #include "esUtil.h"
     19 
     20 #define NUM_PARTICLES	1000
     21 #define PARTICLE_SIZE   7
     22 
     23 typedef struct
     24 {
     25    // Handle to a program object
     26    GLuint programObject;
     27 
     28    // Attribute locations
     29    GLint  lifetimeLoc;
     30    GLint  startPositionLoc;
     31    GLint  endPositionLoc;
     32 
     33    // Uniform location
     34    GLint timeLoc;
     35    GLint colorLoc;
     36    GLint centerPositionLoc;
     37    GLint samplerLoc;
     38 
     39    // Texture handle
     40    GLuint textureId;
     41 
     42    // Particle vertex data
     43    float particleData[ NUM_PARTICLES * PARTICLE_SIZE ];
     44 
     45    // Current time
     46    float time;
     47 
     48 } UserData;
     49 
     50 ///
     51 // Load texture from disk
     52 //
     53 GLuint LoadTexture ( char *fileName )
     54 {
     55    int width,
     56        height;
     57    char *buffer = esLoadTGA ( fileName, &width, &height );
     58    GLuint texId;
     59 
     60    if ( buffer == NULL )
     61    {
     62       esLogMessage ( "Error loading (%s) image.\n", fileName );
     63       return 0;
     64    }
     65 
     66    glGenTextures ( 1, &texId );
     67    glBindTexture ( GL_TEXTURE_2D, texId );
     68 
     69    glTexImage2D ( GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, buffer );
     70    glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
     71    glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
     72    glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
     73    glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
     74 
     75    free ( buffer );
     76 
     77    return texId;
     78 }
     79 
     80 
     81 ///
     82 // Initialize the shader and program object
     83 //
     84 int Init ( ESContext *esContext )
     85 {
     86    UserData *userData = esContext->userData;
     87    int i;
     88 
     89    GLbyte vShaderStr[] =
     90       "uniform float u_time;		                           \n"
     91       "uniform vec3 u_centerPosition;                       \n"
     92       "attribute float a_lifetime;                          \n"
     93       "attribute vec3 a_startPosition;                      \n"
     94       "attribute vec3 a_endPosition;                        \n"
     95       "varying float v_lifetime;                            \n"
     96       "void main()                                          \n"
     97       "{                                                    \n"
     98       "  if ( u_time <= a_lifetime )                        \n"
     99       "  {                                                  \n"
    100       "    gl_Position.xyz = a_startPosition +              \n"
    101       "                      (u_time * a_endPosition);      \n"
    102       "    gl_Position.xyz += u_centerPosition;             \n"
    103       "    gl_Position.w = 1.0;                             \n"
    104       "  }                                                  \n"
    105       "  else                                               \n"
    106       "     gl_Position = vec4( -1000, -1000, 0, 0 );       \n"
    107       "  v_lifetime = 1.0 - ( u_time / a_lifetime );        \n"
    108       "  v_lifetime = clamp ( v_lifetime, 0.0, 1.0 );       \n"
    109       "  gl_PointSize = ( v_lifetime * v_lifetime ) * 40.0; \n"
    110       "}";
    111 
    112    GLbyte fShaderStr[] =
    113       "precision mediump float;                             \n"
    114       "uniform vec4 u_color;		                           \n"
    115       "varying float v_lifetime;                            \n"
    116       "uniform sampler2D s_texture;                         \n"
    117       "void main()                                          \n"
    118       "{                                                    \n"
    119       "  vec4 texColor;                                     \n"
    120       "  texColor = texture2D( s_texture, gl_PointCoord );  \n"
    121       "  gl_FragColor = vec4( u_color ) * texColor;         \n"
    122       "  gl_FragColor.a *= v_lifetime;                      \n"
    123       "}                                                    \n";
    124 
    125    // Load the shaders and get a linked program object
    126    userData->programObject = esLoadProgram ( vShaderStr, fShaderStr );
    127 
    128    // Get the attribute locations
    129    userData->lifetimeLoc = glGetAttribLocation ( userData->programObject, "a_lifetime" );
    130    userData->startPositionLoc = glGetAttribLocation ( userData->programObject, "a_startPosition" );
    131    userData->endPositionLoc = glGetAttribLocation ( userData->programObject, "a_endPosition" );
    132 
    133    // Get the uniform locations
    134    userData->timeLoc = glGetUniformLocation ( userData->programObject, "u_time" );
    135    userData->centerPositionLoc = glGetUniformLocation ( userData->programObject, "u_centerPosition" );
    136    userData->colorLoc = glGetUniformLocation ( userData->programObject, "u_color" );
    137    userData->samplerLoc = glGetUniformLocation ( userData->programObject, "s_texture" );
    138 
    139    glClearColor ( 0.0f, 0.0f, 0.0f, 0.0f );
    140 
    141    // Fill in particle data array
    142    srand ( 0 );
    143    for ( i = 0; i < NUM_PARTICLES; i++ )
    144    {
    145       float *particleData = &userData->particleData[i * PARTICLE_SIZE];
    146 
    147       // Lifetime of particle
    148       (*particleData++) = ( (float)(rand() % 10000) / 10000.0f );
    149 
    150       // End position of particle
    151       (*particleData++) = ( (float)(rand() % 10000) / 5000.0f ) - 1.0f;
    152       (*particleData++) = ( (float)(rand() % 10000) / 5000.0f ) - 1.0f;
    153       (*particleData++) = ( (float)(rand() % 10000) / 5000.0f ) - 1.0f;
    154 
    155       // Start position of particle
    156       (*particleData++) = ( (float)(rand() % 10000) / 40000.0f ) - 0.125f;
    157       (*particleData++) = ( (float)(rand() % 10000) / 40000.0f ) - 0.125f;
    158       (*particleData++) = ( (float)(rand() % 10000) / 40000.0f ) - 0.125f;
    159 
    160    }
    161 
    162    // Initialize time to cause reset on first update
    163    userData->time = 1.0f;
    164 
    165    userData->textureId = LoadTexture ( "smoke.tga" );
    166    if ( userData->textureId <= 0 )
    167    {
    168       return FALSE;
    169    }
    170 
    171    return TRUE;
    172 }
    173 
    174 ///
    175 //  Update time-based variables
    176 //
    177 void Update ( ESContext *esContext, float deltaTime )
    178 {
    179    UserData *userData = esContext->userData;
    180 
    181    userData->time += deltaTime;
    182 
    183    if ( userData->time >= 1.0f )
    184    {
    185       float centerPos[3];
    186       float color[4];
    187 
    188       userData->time = 0.0f;
    189 
    190       // Pick a new start location and color
    191       centerPos[0] = ( (float)(rand() % 10000) / 10000.0f ) - 0.5f;
    192       centerPos[1] = ( (float)(rand() % 10000) / 10000.0f ) - 0.5f;
    193       centerPos[2] = ( (float)(rand() % 10000) / 10000.0f ) - 0.5f;
    194 
    195       glUniform3fv ( userData->centerPositionLoc, 1, &centerPos[0] );
    196 
    197       // Random color
    198       color[0] = ( (float)(rand() % 10000) / 20000.0f ) + 0.5f;
    199       color[1] = ( (float)(rand() % 10000) / 20000.0f ) + 0.5f;
    200       color[2] = ( (float)(rand() % 10000) / 20000.0f ) + 0.5f;
    201       color[3] = 0.5;
    202 
    203       glUniform4fv ( userData->colorLoc, 1, &color[0] );
    204    }
    205 
    206    // Load uniform time variable
    207    glUniform1f ( userData->timeLoc, userData->time );
    208 }
    209 
    210 ///
    211 // Draw a triangle using the shader pair created in Init()
    212 //
    213 void Draw ( ESContext *esContext )
    214 {
    215    UserData *userData = esContext->userData;
    216 
    217    // Set the viewport
    218    glViewport ( 0, 0, esContext->width, esContext->height );
    219 
    220    // Clear the color buffer
    221    glClear ( GL_COLOR_BUFFER_BIT );
    222 
    223    // Use the program object
    224    glUseProgram ( userData->programObject );
    225 
    226    // Load the vertex attributes
    227    glVertexAttribPointer ( userData->lifetimeLoc, 1, GL_FLOAT,
    228                            GL_FALSE, PARTICLE_SIZE * sizeof(GLfloat),
    229                            userData->particleData );
    230 
    231    glVertexAttribPointer ( userData->endPositionLoc, 3, GL_FLOAT,
    232                            GL_FALSE, PARTICLE_SIZE * sizeof(GLfloat),
    233                            &userData->particleData[1] );
    234 
    235    glVertexAttribPointer ( userData->startPositionLoc, 3, GL_FLOAT,
    236                            GL_FALSE, PARTICLE_SIZE * sizeof(GLfloat),
    237                            &userData->particleData[4] );
    238 
    239 
    240    glEnableVertexAttribArray ( userData->lifetimeLoc );
    241    glEnableVertexAttribArray ( userData->endPositionLoc );
    242    glEnableVertexAttribArray ( userData->startPositionLoc );
    243    // Blend particles
    244    glEnable ( GL_BLEND );
    245    glBlendFunc ( GL_SRC_ALPHA, GL_ONE );
    246 
    247    // Bind the texture
    248    glActiveTexture ( GL_TEXTURE0 );
    249    glBindTexture ( GL_TEXTURE_2D, userData->textureId );
    250    glEnable ( GL_TEXTURE_2D );
    251 
    252    // Set the sampler texture unit to 0
    253    glUniform1i ( userData->samplerLoc, 0 );
    254 
    255    glDrawArrays( GL_POINTS, 0, NUM_PARTICLES );
    256 
    257    eglSwapBuffers ( esContext->eglDisplay, esContext->eglSurface );
    258 }
    259 
    260 ///
    261 // Cleanup
    262 //
    263 void ShutDown ( ESContext *esContext )
    264 {
    265    UserData *userData = esContext->userData;
    266 
    267    // Delete texture object
    268    glDeleteTextures ( 1, &userData->textureId );
    269 
    270    // Delete program object
    271    glDeleteProgram ( userData->programObject );
    272 }
    273 
    274 
    275 int main ( int argc, char *argv[] )
    276 {
    277    ESContext esContext;
    278    UserData  userData;
    279 
    280    esInitContext ( &esContext );
    281    esContext.userData = &userData;
    282 
    283    esCreateWindow ( &esContext, TEXT("ParticleSystem"), 640, 480, ES_WINDOW_RGB );
    284 
    285    if ( !Init ( &esContext ) )
    286       return 0;
    287 
    288    esRegisterDrawFunc ( &esContext, Draw );
    289    esRegisterUpdateFunc ( &esContext, Update );
    290 
    291    esMainLoop ( &esContext );
    292 
    293    ShutDown ( &esContext );
    294 }
    295