// 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 <GL/gl.h>
// #include <GL/glut.h>
// #include <windows.h>
// #include <stdio.h>
// #include <math.h>

// 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 <GL/gl.h>
#include <GL/glut.h>
#include <windows.h>
#include <stdio.h>
#include <math.h>

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 );

}