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, ¢erPos[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