diff options
Diffstat (limited to 'samples/gles2_book/ParticleSystem/ParticleSystem.c')
-rw-r--r-- | samples/gles2_book/ParticleSystem/ParticleSystem.c | 294 |
1 files changed, 294 insertions, 0 deletions
diff --git a/samples/gles2_book/ParticleSystem/ParticleSystem.c b/samples/gles2_book/ParticleSystem/ParticleSystem.c new file mode 100644 index 00000000..22141334 --- /dev/null +++ b/samples/gles2_book/ParticleSystem/ParticleSystem.c @@ -0,0 +1,294 @@ +// +// Book: OpenGL(R) ES 2.0 Programming Guide +// Authors: Aaftab Munshi, Dan Ginsburg, Dave Shreiner +// ISBN-10: 0321502795 +// ISBN-13: 9780321502797 +// Publisher: Addison-Wesley Professional +// URLs: http://safari.informit.com/9780321563835 +// http://www.opengles-book.com +// + +// ParticleSystem.c +// +// This is an example that demonstrates rendering a particle system +// using a vertex shader and point sprites. +// +#include <stdlib.h> +#include <math.h> +#include "esUtil.h" + +#define NUM_PARTICLES 1000 +#define PARTICLE_SIZE 7 + +typedef struct +{ + // Handle to a program object + GLuint programObject; + + // Attribute locations + GLint lifetimeLoc; + GLint startPositionLoc; + GLint endPositionLoc; + + // Uniform location + GLint timeLoc; + GLint colorLoc; + GLint centerPositionLoc; + GLint samplerLoc; + + // Texture handle + GLuint textureId; + + // Particle vertex data + float particleData[ NUM_PARTICLES * PARTICLE_SIZE ]; + + // Current time + float time; + +} UserData; + +/// +// Load texture from disk +// +GLuint LoadTexture ( char *fileName ) +{ + int width, + height; + char *buffer = esLoadTGA ( fileName, &width, &height ); + GLuint texId; + + if ( buffer == NULL ) + { + esLogMessage ( "Error loading (%s) image.\n", fileName ); + return 0; + } + + glGenTextures ( 1, &texId ); + glBindTexture ( GL_TEXTURE_2D, texId ); + + glTexImage2D ( GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, buffer ); + glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); + glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); + glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); + glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); + + free ( buffer ); + + return texId; +} + + +/// +// Initialize the shader and program object +// +int Init ( ESContext *esContext ) +{ + UserData *userData = esContext->userData; + int i; + + GLbyte vShaderStr[] = + "uniform float u_time; \n" + "uniform vec3 u_centerPosition; \n" + "attribute float a_lifetime; \n" + "attribute vec3 a_startPosition; \n" + "attribute vec3 a_endPosition; \n" + "varying float v_lifetime; \n" + "void main() \n" + "{ \n" + " if ( u_time <= a_lifetime ) \n" + " { \n" + " gl_Position.xyz = a_startPosition + \n" + " (u_time * a_endPosition); \n" + " gl_Position.xyz += u_centerPosition; \n" + " gl_Position.w = 1.0; \n" + " } \n" + " else \n" + " gl_Position = vec4( -1000, -1000, 0, 0 ); \n" + " v_lifetime = 1.0 - ( u_time / a_lifetime ); \n" + " v_lifetime = clamp ( v_lifetime, 0.0, 1.0 ); \n" + " gl_PointSize = ( v_lifetime * v_lifetime ) * 40.0; \n" + "}"; + + GLbyte fShaderStr[] = + "precision mediump float; \n" + "uniform vec4 u_color; \n" + "varying float v_lifetime; \n" + "uniform sampler2D s_texture; \n" + "void main() \n" + "{ \n" + " vec4 texColor; \n" + " texColor = texture2D( s_texture, gl_PointCoord ); \n" + " gl_FragColor = vec4( u_color ) * texColor; \n" + " gl_FragColor.a *= v_lifetime; \n" + "} \n"; + + // Load the shaders and get a linked program object + userData->programObject = esLoadProgram ( vShaderStr, fShaderStr ); + + // Get the attribute locations + userData->lifetimeLoc = glGetAttribLocation ( userData->programObject, "a_lifetime" ); + userData->startPositionLoc = glGetAttribLocation ( userData->programObject, "a_startPosition" ); + userData->endPositionLoc = glGetAttribLocation ( userData->programObject, "a_endPosition" ); + + // Get the uniform locations + userData->timeLoc = glGetUniformLocation ( userData->programObject, "u_time" ); + userData->centerPositionLoc = glGetUniformLocation ( userData->programObject, "u_centerPosition" ); + userData->colorLoc = glGetUniformLocation ( userData->programObject, "u_color" ); + userData->samplerLoc = glGetUniformLocation ( userData->programObject, "s_texture" ); + + glClearColor ( 0.0f, 0.0f, 0.0f, 0.0f ); + + // Fill in particle data array + srand ( 0 ); + for ( i = 0; i < NUM_PARTICLES; i++ ) + { + float *particleData = &userData->particleData[i * PARTICLE_SIZE]; + + // Lifetime of particle + (*particleData++) = ( (float)(rand() % 10000) / 10000.0f ); + + // End position of particle + (*particleData++) = ( (float)(rand() % 10000) / 5000.0f ) - 1.0f; + (*particleData++) = ( (float)(rand() % 10000) / 5000.0f ) - 1.0f; + (*particleData++) = ( (float)(rand() % 10000) / 5000.0f ) - 1.0f; + + // Start position of particle + (*particleData++) = ( (float)(rand() % 10000) / 40000.0f ) - 0.125f; + (*particleData++) = ( (float)(rand() % 10000) / 40000.0f ) - 0.125f; + (*particleData++) = ( (float)(rand() % 10000) / 40000.0f ) - 0.125f; + + } + + // Initialize time to cause reset on first update + userData->time = 1.0f; + + userData->textureId = LoadTexture ( "smoke.tga" ); + if ( userData->textureId <= 0 ) + { + return FALSE; + } + + return TRUE; +} + +/// +// Update time-based variables +// +void Update ( ESContext *esContext, float deltaTime ) +{ + UserData *userData = esContext->userData; + + userData->time += deltaTime; + + if ( userData->time >= 1.0f ) + { + float centerPos[3]; + float color[4]; + + userData->time = 0.0f; + + // Pick a new start location and color + centerPos[0] = ( (float)(rand() % 10000) / 10000.0f ) - 0.5f; + centerPos[1] = ( (float)(rand() % 10000) / 10000.0f ) - 0.5f; + centerPos[2] = ( (float)(rand() % 10000) / 10000.0f ) - 0.5f; + + glUniform3fv ( userData->centerPositionLoc, 1, ¢erPos[0] ); + + // Random color + color[0] = ( (float)(rand() % 10000) / 20000.0f ) + 0.5f; + color[1] = ( (float)(rand() % 10000) / 20000.0f ) + 0.5f; + color[2] = ( (float)(rand() % 10000) / 20000.0f ) + 0.5f; + color[3] = 0.5; + + glUniform4fv ( userData->colorLoc, 1, &color[0] ); + } + + // Load uniform time variable + glUniform1f ( userData->timeLoc, userData->time ); +} + +/// +// Draw a triangle using the shader pair created in Init() +// +void Draw ( ESContext *esContext ) +{ + UserData *userData = esContext->userData; + + // Set the viewport + glViewport ( 0, 0, esContext->width, esContext->height ); + + // Clear the color buffer + glClear ( GL_COLOR_BUFFER_BIT ); + + // Use the program object + glUseProgram ( userData->programObject ); + + // Load the vertex attributes + glVertexAttribPointer ( userData->lifetimeLoc, 1, GL_FLOAT, + GL_FALSE, PARTICLE_SIZE * sizeof(GLfloat), + userData->particleData ); + + glVertexAttribPointer ( userData->endPositionLoc, 3, GL_FLOAT, + GL_FALSE, PARTICLE_SIZE * sizeof(GLfloat), + &userData->particleData[1] ); + + glVertexAttribPointer ( userData->startPositionLoc, 3, GL_FLOAT, + GL_FALSE, PARTICLE_SIZE * sizeof(GLfloat), + &userData->particleData[4] ); + + + glEnableVertexAttribArray ( userData->lifetimeLoc ); + glEnableVertexAttribArray ( userData->endPositionLoc ); + glEnableVertexAttribArray ( userData->startPositionLoc ); + // Blend particles + glEnable ( GL_BLEND ); + glBlendFunc ( GL_SRC_ALPHA, GL_ONE ); + + // Bind the texture + glActiveTexture ( GL_TEXTURE0 ); + glBindTexture ( GL_TEXTURE_2D, userData->textureId ); + glEnable ( GL_TEXTURE_2D ); + + // Set the sampler texture unit to 0 + glUniform1i ( userData->samplerLoc, 0 ); + + glDrawArrays( GL_POINTS, 0, NUM_PARTICLES ); + + eglSwapBuffers ( esContext->eglDisplay, esContext->eglSurface ); +} + +/// +// Cleanup +// +void ShutDown ( ESContext *esContext ) +{ + UserData *userData = esContext->userData; + + // Delete texture object + glDeleteTextures ( 1, &userData->textureId ); + + // Delete program object + glDeleteProgram ( userData->programObject ); +} + + +int main ( int argc, char *argv[] ) +{ + ESContext esContext; + UserData userData; + + esInitContext ( &esContext ); + esContext.userData = &userData; + + esCreateWindow ( &esContext, TEXT("ParticleSystem"), 640, 480, ES_WINDOW_RGB ); + + if ( !Init ( &esContext ) ) + return 0; + + esRegisterDrawFunc ( &esContext, Draw ); + esRegisterUpdateFunc ( &esContext, Update ); + + esMainLoop ( &esContext ); + + ShutDown ( &esContext ); +} |