summaryrefslogtreecommitdiff
path: root/samples/gles2_book/ParticleSystem/ParticleSystem.c
diff options
context:
space:
mode:
Diffstat (limited to 'samples/gles2_book/ParticleSystem/ParticleSystem.c')
-rw-r--r--samples/gles2_book/ParticleSystem/ParticleSystem.c294
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, &centerPos[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 );
+}