// Today we are going to be working with particle engines. The one here // is pretty basic but extremely easy to use and expand on. I have even // added texture masking for weirdly shaped particles. But unfortunately // I am yet to change the color of these on-the-fly without disrupting the // masking so for the moment, you cannot change the color to a number such as // 0.1, 0.2, 0.3... it must be a whole number. Eg: 1 or 0. // Anyway, lets get on with the tutorial. // ------------------------------------------------------------------------ // Section 1: Variables // First off, we need to declare our header files. For this project we need // #include // #include // #include // #include // #include // This first variable I am going to use to hold the number of particles // we will display on the screen at any one time. // const ParticleCount = 500 // Of course we also need something to hold our textures. // GLfloat texture[10]; // Now here, I am creating a Type called PARTICLES which will hold all // our information for our particles. // typedef struct // { // The X position // double Xpos; // The Y position // double Ypos; // The Z position // double Zpos; // The movement on the X axis while being displayed // double Xmov; // The movement on the Z axis while being displayed // double Zmov; // The amount of Red within the object // double Red; // The amount of Green within the object // double Green; // The amount of Blue within the object // double Blue; // The angle of rotation // double Direction; // How fast it accelerates upwards // double Acceleration; // How fast it decelerates downwards // double Deceleration; // How much we wish to scale it // double Scalez; // }PARTICLES; // And now we will create a variable that will hold each of the PARTICLES // information for each actual Particle // PARTICLES Particle[ParticleCount]; // ------------------------------------------------------------------------ // Section 2: Creation // To create the particles I am going to call the function glCreateParticles // during the initialization section of the code with: // glCreateParticles // Now for the creation function: // Public Function glCreateParticles() // This will be a temporary variable to cycle through our particles // int i; // We are going to loop through our particles until we reach the end // for (i = 1; i < ParticleCount; i++) // { // Set the inital X position to 0 // Particle[i].Xpos = 0; // Set the inital Y position to -5 // Particle[i].Ypos = -5; // Set the inital Z position to -5 // Particle[i].Zpos = -5; // Set the amount of movement on the X axis to a random number, we dont want // all our particles doing the same thing :P // Particle[i].Xmov = (((((((2 - 1 + 1) * rand()%11) + 1) - 1 + 1) * rand()%11) + 1) * 0.005) - (((((((2 - 1 + 1) * rand()%11) + 1) - 1 + 1) * rand()%11) + 1) * 0.005); // Set the amount of movement on the Z axis to a random number, as above, we dont // want all our particles doing the same thing :P // Particle[i].Zmov = (((((((2 - 1 + 1) * rand()%11) + 1) - 1 + 1) * rand()%11) + 1) * 0.005) - (((((((2 - 1 + 1) * rand()%11) + 1) - 1 + 1) * rand()%11) + 1) * 0.005); // Set the amount of Red to 1 // Particle[i].Red = 1; // Set the amount of Green to 1 // Particle[i].Green = 1; // Set the amount of Blue to 1 // Particle[i].Blue = 1; // Scale the particle to 1 quarter of its original size // Particle[i].Scalez = 0.25; // Set the initial rotation angle to 0 // Particle[i].Direction = 0; // Set the amount of acceleration to a random number so they climb to different // heights // Particle[i].Acceleration = ((((((8 - 5 + 2) * rand()%11) + 5) - 1 + 1) * rand()%11) + 1) * 0.02; // Decrease their acceleration by 0.0025. They will slow down at a constant // rate but you will not see a difference // Particle[i].Deceleration = 0.0025; // } // } // ---------------------------------------------------------------------------- // Section 3: Updating the Particles // The particles would not be effective, if they stayed at their inital state, so // we want them to change and acctually move. I am doing this by calling: // glUpdateParticles in my Display function. // Public Function glUpdateParticles() // Once again, another temporary variable to handle the cycle through the particles // int i; // Loop through all of the particles // for (i = 1; i < ParticleCount; i++) // { // Set the color of the current particle // glColor3f (Particle[i].Red, Particle[i].Green, Particle[i].Blue); // Move the particle on the Y axes, adding on the amount of acceleration // and then subtracting the rate of deceleration // Particle[i].Ypos = Particle[i].Ypos + Particle[i].Acceleration - Particle[i].Deceleration; // Increase the deceleration rate so the particle falls gaining speed // Particle[i].Deceleration = Particle[i].Deceleration + 0.0025; // Move the particle on the X axis // Particle[i].Xpos = Particle[i].Xpos + Particle[i].Xmov; // Move the particle on the Z axis // Particle[i].Zpos = Particle[i].Zpos + Particle[i].Zmov; // Rotate the particle // Particle[i].Direction = Particle[i].Direction + ((((((int)(0.5 - 0.1 + 0.1) * rand()%11) + 1) - 1 + 1) * rand()%11) + 1); // Now here I am saying that if the particle goes beneath its initial height // which I set earlier to -5, then it will restart the particle changing some // of the variables. // if (Particle[i].Ypos < -5) // { // Set the X position // Particle[i].Xpos = 0; // Set the Y position // Particle[i].Ypos = -5; // Set the Z position // Particle[i].Zpos = -5; // Set the amount of Red // Particle[i].Red = 1; // Set the amount of Green // Particle[i].Green = 1; // Set the amount of Blue // Particle[i].Blue = 1; // Set the angle of rotation // Particle[i].Direction = 0; // Adjust the Acceleration rate to another random number // Particle[i].Acceleration = ((((((8 - 5 + 2) * rand()%11) + 5) - 1 + 1) * rand()%11) + 1) * 0.02; // Reset the Deceleration rate // Particle[i].Deceleration = 0.0025; // } // } // } // ---------------------------------------------------------------------------- // Section 4: Drawing the Particles // To draw the particles, I am calling the following function: glDrawParticle // from within the Display function, after the updating function. // Public Function glDrawParticle() // Yet another temporary variable for our cycles // int i; // Looping through our particles again // for (i = 1; i < ParticleCount; i++) // { // Distinguish the start of our current particle, we do not wish for them // all to be affected by the ones prior // glPushMatrix(); // Translate the particle on the X, Y and Z axis accordingly // glTranslatef (Particle[i].Xpos, Particle[i].Ypos, Particle[i].Zpos); // Rotate the particle // glRotatef (Particle[i].Direction - 90, 0, 0, 1); // Sclale the particle // glScalef (Particle[i].Scalez, Particle[i].Scalez, Particle[i].Scalez); // Disable Depth Testing so our masking appears as one // glDisable (GL_DEPTH_TEST); // Enable blending // glEnable (GL_BLEND); // Set the blending function to Take our Destination Colour and combine it with // Zero which is Black // glBlendFunc (GL_DST_COLOR, GL_ZERO); // Bind our mask // glBindTexture (GL_TEXTURE_2D, texture[0]); // Draw our shape // glBegin (GL_QUADS); // glTexCoord2d (0, 0); // glVertex3f (-1, -1, 0); // glTexCoord2d (1, 0); // glVertex3f (1, -1, 0); // glTexCoord2d (1, 1); // glVertex3f (1, 1, 0); // glTexCoord2d (0, 1); // glVertex3f (-1, 1, 0); // glEnd(); // Then set out blending function to combine White with White // glBlendFunc (GL_ONE, GL_ONE); // Bind our texture // glBindTexture (GL_TEXTURE_2D, texture[1]); // Draw the shape // glBegin (GL_QUADS); // glTexCoord2d (0, 0); // glVertex3f (-1, -1, 0); // glTexCoord2d (1, 0); // glVertex3f (1, -1, 0); // glTexCoord2d (1, 1); // glVertex3f (1, 1, 0); // glTexCoord2d (0, 1); // glVertex3f (-1, 1, 0); // glEnd(); // Re-enable Depth Testing // glEnable(GL_DEPTH_TEST); // End the changes to the current object // glPopMatrix(); // } // } // } // ------------------------------------------------------------------------- // And there we have it, a simple particle engine that pumps out 500 particles // at a steady framerate of 80 frames per second. // If you have any questions, feel free to email me at swiftless@gmail.com #include #include #include #include #include GLfloat texture[10]; const ParticleCount = 500; typedef struct { double Xpos; double Ypos; double Zpos; double Xmov; double Zmov; double Red; double Green; double Blue; double Direction; double Acceleration; double Deceleration; double Scalez; bool Visible; }PARTICLES; PARTICLES Particle[ParticleCount]; GLuint LoadTextureRAW( const char * filename, int width, int height); void FreeTexture( GLuint texturez ); void square (void) { glBindTexture( GL_TEXTURE_2D, texture[0] ); glBegin (GL_QUADS); glTexCoord2d(0.0,0.0); glVertex2d(-1.0,-1.0); glTexCoord2d(1.0,0.0); glVertex2d(1.0,-1.0); glTexCoord2d(1.0,1.0); glVertex2d(1.0,1.0); glTexCoord2d(0.0,1.0); glVertex2d(-1.0,1.0); glEnd(); } void glCreateParticles (void) { int i; for (i = 1; i < ParticleCount; i++) { Particle[i].Xpos = 0; Particle[i].Ypos = -5; Particle[i].Zpos = -5; Particle[i].Xmov = (((((((2 - 1 + 1) * rand()%11) + 1) - 1 + 1) * rand()%11) + 1) * 0.005) - (((((((2 - 1 + 1) * rand()%11) + 1) - 1 + 1) * rand()%11) + 1) * 0.005); Particle[i].Zmov = (((((((2 - 1 + 1) * rand()%11) + 1) - 1 + 1) * rand()%11) + 1) * 0.005) - (((((((2 - 1 + 1) * rand()%11) + 1) - 1 + 1) * rand()%11) + 1) * 0.005); Particle[i].Red = 1; Particle[i].Green = 1; Particle[i].Blue = 1; Particle[i].Scalez = 0.25; Particle[i].Direction = 0; Particle[i].Acceleration = ((((((8 - 5 + 2) * rand()%11) + 5) - 1 + 1) * rand()%11) + 1) * 0.02; Particle[i].Deceleration = 0.0025; } } void glUpdateParticles (void) { int i; for (i = 1; i < ParticleCount; i++) { glColor3f (Particle[i].Red, Particle[i].Green, Particle[i].Blue); Particle[i].Ypos = Particle[i].Ypos + Particle[i].Acceleration - Particle[i].Deceleration; Particle[i].Deceleration = Particle[i].Deceleration + 0.0025; Particle[i].Xpos = Particle[i].Xpos + Particle[i].Xmov; Particle[i].Zpos = Particle[i].Zpos + Particle[i].Zmov; Particle[i].Direction = Particle[i].Direction + ((((((int)(0.5 - 0.1 + 0.1) * rand()%11) + 1) - 1 + 1) * rand()%11) + 1); if (Particle[i].Ypos < -5) { Particle[i].Xpos = 0; Particle[i].Ypos = -5; Particle[i].Zpos = -5; Particle[i].Red = 1; Particle[i].Green = 1; Particle[i].Blue = 1; Particle[i].Direction = 0; Particle[i].Acceleration = ((((((8 - 5 + 2) * rand()%11) + 5) - 1 + 1) * rand()%11) + 1) * 0.02; Particle[i].Deceleration = 0.0025; } } } void glDrawParticles (void) { int i; for (i = 1; i < ParticleCount; i++) { glPushMatrix(); glTranslatef (Particle[i].Xpos, Particle[i].Ypos, Particle[i].Zpos); glRotatef (Particle[i].Direction - 90, 0, 0, 1); glScalef (Particle[i].Scalez, Particle[i].Scalez, Particle[i].Scalez); glDisable (GL_DEPTH_TEST); glEnable (GL_BLEND); glBlendFunc (GL_DST_COLOR, GL_ZERO); glBindTexture (GL_TEXTURE_2D, texture[0]); glBegin (GL_QUADS); glTexCoord2d (0, 0); glVertex3f (-1, -1, 0); glTexCoord2d (1, 0); glVertex3f (1, -1, 0); glTexCoord2d (1, 1); glVertex3f (1, 1, 0); glTexCoord2d (0, 1); glVertex3f (-1, 1, 0); glEnd(); glBlendFunc (GL_ONE, GL_ONE); glBindTexture (GL_TEXTURE_2D, texture[1]); glBegin (GL_QUADS); glTexCoord2d (0, 0); glVertex3f (-1, -1, 0); glTexCoord2d (1, 0); glVertex3f (1, -1, 0); glTexCoord2d (1, 1); glVertex3f (1, 1, 0); glTexCoord2d (0, 1); glVertex3f (-1, 1, 0); glEnd(); glEnable(GL_DEPTH_TEST); glPopMatrix(); } } void display (void) { glClearDepth (1); glClearColor (0.0,0.0,0.0,1.0); glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glLoadIdentity(); glTranslatef (0,0,-10); glUpdateParticles(); glDrawParticles(); glutSwapBuffers(); } void init (void) { glEnable( GL_TEXTURE_2D ); glEnable(GL_DEPTH_TEST); glCreateParticles(); texture[0] = LoadTextureRAW( "particle_mask.raw",256,256); //load our texture texture[1] = LoadTextureRAW( "particle.raw",256,256); //load our texture } void reshape (int w, int h) { glViewport (0, 0, (GLsizei)w, (GLsizei)h); glMatrixMode (GL_PROJECTION); glLoadIdentity (); gluPerspective (60, (GLfloat)w / (GLfloat)h, 1.0, 100.0); glMatrixMode (GL_MODELVIEW); } int main (int argc, char **argv) { glutInit (&argc, argv); glutInitDisplayMode (GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH); glutInitWindowSize (500, 500); glutInitWindowPosition (100, 100); glutCreateWindow ("A basic OpenGL Window"); init(); glutDisplayFunc (display); glutIdleFunc (display); glutReshapeFunc (reshape); glutMainLoop (); return 0; } //function to load the RAW file GLuint LoadTextureRAW( const char * filename, int width, int height ) { GLuint texture; unsigned char * data; FILE * file; file = fopen( filename, "rb" ); if ( file == NULL ) return 0; data = (unsigned char *)malloc( width * height * 3 ); fread( data, width * height * 3, 1, file ); fclose( file ); glGenTextures(1, &texture ); glBindTexture(GL_TEXTURE_2D, texture); glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE ); glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST ); glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); gluBuild2DMipmaps(GL_TEXTURE_2D, 3, width, height, GL_RGB, GL_UNSIGNED_BYTE, data); free( data ); return texture; } void FreeTexture( GLuint texture ) { glDeleteTextures( 1, &texture ); }