/* Robert Himelberg cs580 advanced computer graphics Assigment 2: Procedural Textures of fire smoke + a particle system too citation: base code of window and pixel stuff taken from image.c from opengl redbook noise functions: based upon perlin's noise functions as interpreted from several books procedural texturing, advanced rendering, and the online site http://freespace.virgin.net/hugo.elias/models/m_perlin.htm especially the sections on clouds also shout out to Scott Arthur for his idea about the rgb values and making them look red and such also see: image.c, envphong.c, checker.c, and light.c I used various setups from each in terms of drawing the scene and various functions. Basically, I kept moving my code from one, file to the next, hoping nothing would break along the way. */ /* * light.c * This program demonstrates the use of the OpenGL lighting * model. A sphere is drawn using a grey material characteristic. * A single light source illuminates the object. */ #include #include #include #include #include #include #include #include #ifndef M_PI #define M_PI 3.14159265 #endif /* Create checkerboard texture */ /* Create checkerboard image */ #define checkImageWidth 256 #define checkImageHeight 256 #define checkDepth 30 GLubyte checkImage[checkImageHeight][checkImageWidth][4]; GLubyte checkImage2[checkImageHeight][checkImageWidth][4]; float map32[32 * 32]; //primary texture float map256[256 * 256]; //expaned to 256*256 float map32_2[32 * 32]; //used again for interpolation between frames float Animap[32 * 32]; //this is where we put our result interp b4 we blow it up to 256 float map256_2[256 * 256]; //didn't really use it but keep around in case for debugg purposes float timecount = 0; //used in terms of the time step between fire/smoke textures float timestep = 0.8; float gravity = -0.8; float vdrag = -0.001; static GLdouble zoomFactor = 1.0; static GLint height; #ifdef GL_VERSION_1_1 static GLuint texName; #endif /*/////////////////////////////////////////////////////////////////////// Particle Code Start ///////////////////////////////////////////////////////////////////////*/ #define MAX_PARTICLES 150 float particleTimer = 0; //time relevant to the particles, don't want to confuse with texture time float particleTimeStep = 0.5; typedef struct { //position of it float x; float y; float z; float mass; //not really relevant consider each has 1 mass problem solved :) //velocity float xveloc; float yveloc; float zveloc; //used for force accumulator float xforces; float yforces; float zforces; bool alive; //how much has the particle faded float fade; //how much time do we let it live float life; }particle; particle particles[MAX_PARTICLES]; float partTimestep=2.0f; float yspeed = 100.0f; float wind_veloc = 1.0f; float wind_Horz; float wind_Vert; GLuint texture[1]; ////////////////////////////////////////Texture Code//////////////////////////////// AUX_RGBImageRec *LoadBMP(char *Filename) // Loads A Bitmap Image { FILE *File=NULL; // File Handle if (!Filename) // Make Sure A Filename Was Given { return NULL; // If Not Return NULL } File=fopen(Filename,"r"); // Check To See If The File Exists if (File) // Does The File Exist? { fclose(File); // Close The Handle return auxDIBImageLoad(Filename); // Load The Bitmap And Return A Pointer } return NULL; // If Load Failed Return NULL } int LoadGLTextures() // Load Bitmap And Convert To A Texture { int Status=FALSE; // Status Indicator AUX_RGBImageRec *TextureImage[1]; // Create Storage Space For The Textures memset(TextureImage,0,sizeof(void *)*1); // Set The Pointer To NULL if (TextureImage[0]=LoadBMP("Data/Particle.bmp")) // Load Particle Texture { Status=TRUE; // Set The Status To TRUE glGenTextures(1, &texture[0]); // Create One Texture glBindTexture(GL_TEXTURE_2D, texture[0]); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR); glTexImage2D(GL_TEXTURE_2D, 0, 3, TextureImage[0]->sizeX, TextureImage[0]->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, TextureImage[0]->data); } if (TextureImage[0]) // If Texture Exists { if (TextureImage[0]->data) // If Texture Image Exists { free(TextureImage[0]->data); // Free The Texture Image Memory } free(TextureImage[0]); // Free The Image Structure } return Status; // Return The Status } ////////////////////////////////End Texture Code/////////////////////////////////////// void initParticles() { for(int i = 0; i < MAX_PARTICLES; i++) { particles[i].alive = TRUE; particles[i].life = 1.0; particles[i].fade = float(rand()%100)/1000.0f+0.003f; particles[i].mass = 1.0; particles[i].x = 0.0; particles[i].y = 0.0; particles[i].z = 0.0; particles[i].xveloc = 0; particles[i].yveloc = 10; particles[i].zveloc =0; wind_Horz = ((wind_veloc-particles[i].xveloc) * 0.01f); wind_Vert = ((0-particles[i].yveloc) * 0.01f); particles[i].xforces = wind_Horz + vdrag; particles[i].yforces = gravity + wind_Vert + vdrag; particles[i].zforces = vdrag; } } void drawParticles() { //glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); LoadGLTextures(); glEnable(GL_TEXTURE_2D); // Enable Texture Mapping glBindTexture(GL_TEXTURE_2D,texture[0]); // Select Our Texture //glPointSize(5); //glDisable(GL_DEPTH_TEST); for(int i = 0; i < MAX_PARTICLES; i++) { if(particles[i].alive == TRUE) { /* glBegin(GL_POINTS); glColor4f(1.0,0.0,0.0,particles[i].life); glVertex3f(particles[i].x, particles[i].y,particles[i].z); glEnd();*/ glPushMatrix(); glColor4f(1.0,0.0,0.0,particles[i].life); glBegin(GL_TRIANGLE_STRIP); // Build Quad From A Triangle Strip glTexCoord2d(1,1); glVertex3f(particles[i].x+0.05f,particles[i].y+0.05f,particles[i].z); // Top Right glTexCoord2d(0,1); glVertex3f(particles[i].x-0.05f,particles[i].y+0.05f,particles[i].z); // Top Left glTexCoord2d(1,0); glVertex3f(particles[i].x+0.05f,particles[i].y-0.05f,particles[i].z); // Bottom Right glTexCoord2d(0,0); glVertex3f(particles[i].x-0.05f,particles[i].y-0.05f,particles[i].z); // Bottom Left glEnd(); // Done Building Triangle Strip glPopMatrix(); particles[i].x+= particles[i].xveloc/ (partTimestep*1000); particles[i].y+= particles[i].yveloc/ (partTimestep*1000); particles[i].z+= particles[i].zveloc/ (partTimestep*1000); //wind_Horz = ((0-particles[i].xveloc) * 0.01f); wind_Vert = ((wind_veloc-particles[i].yveloc) * 0.001f); particles[i].xveloc+= (particles[i].xveloc * vdrag); particles[i].yveloc+= gravity + wind_Vert + (particles[i].yveloc * vdrag); particles[i].zveloc+= 0; particles[i].life-= particles[i].fade; } if(particles[i].life <= 0.0f) { particles[i].life = 1.0f; particles[i].fade = float(rand()%100)/1000.0f+0.003f; particles[i].x=0.0f; particles[i].y=0.0f; particles[i].z=0.0f; particles[i].xveloc= 1+ float((rand()%60)-32.0f); particles[i].yveloc=yspeed + float((rand()%60)-30.0f); particles[i].zveloc=float((rand()%60)-30.0f); } } glDisable(GL_TEXTURE_2D); } /*////////////////////////////////////////////////////////////////////////////////////// Noise Code //////////////////////////////////////////////////////////////////////////////////////*/ //a noise function found from perlin's site, works pretty well float Noise(int x, int y, int random) { int n = x + y * 57 + random * 131; n = (n<<13) ^ n; return (1.0f - ( (n * (n * n * 15731 + 789221) + 1376312589)&0x7fffffff)* 0.000000000931322574615478515625f); } //sets the noise in a 32*32 map, also takes care of mirroring problems at edges void SetNoise(float *map) { srand(time(NULL)); int x,y; float temp[34][34]; int random=rand() % 5000; for (int y=1; y<33; y++) { for (int x=1; x<33; x++) { temp[x][y] = 128.0f + Noise(x, y, random)*128.0f; } } for (int x=1; x<33; x++) { temp[0][x] = temp[32][x]; temp[33][x] = temp[1][x]; temp[x][0] = temp[x][32]; temp[x][33] = temp[x][1]; } temp[0][0] = temp[32][32]; temp[33][33] = temp[1][1]; temp[0][33] = temp[32][1]; temp[33][0] = temp[1][32]; for (int y=1; y<33; y++) { for (int x=1; x<33; x++) { float center = temp[x][y]/4.0f; float sides = (temp[x+1][y] + temp[x-1][y] + temp[x][y+1] + temp[x][y-1])/8.0f; float corners = (temp[x+1][y+1] + temp[x+1][y-1] + temp[x-1][y+1] + temp[x-1][y-1])/16.0f; map32[((x-1)*32) + (y-1)] = center + sides + corners; } } } //basic liner interpolation float Interpolate(float x, float y, float *map) { int Xint = (int)x; int Yint = (int)y; float Xfrac = x - Xint; float Yfrac = y - Yint; int X0 = Xint % 32; int Y0 = Yint % 32; int X1 = (Xint + 1) % 32; int Y1 = (Yint + 1) % 32; float bot = map[X0*32 + Y0] + Xfrac * (map[X1*32 + Y0] - map[X0*32 + Y0]); float top = map[X0*32 + Y1] + Xfrac * (map[X1*32 + Y1] - map[X0*32 + Y1]); return (bot + Yfrac * (top - bot)); } //use octaves to overlap the samples void OverlapOctaves(float *map32, float *map256) { for (int x=0; x<256*256; x++) { map256[x] = 0; } for (int octave=0; octave<4; octave++) for (int x=0; x<256; x++) for (int y=0; y<256; y++) { float scale = 1 / pow(2, 3-octave); float noise = Interpolate(x*scale, y*scale , map32); map256[(y*256) + x] += noise / pow(2, octave); } } float cover = 20.0f; float sharpness = 0.95f; //makes the maps actually look pretty void ExpFilter(float *map) { for (int x=0; x<256*256; x++) { float c = map[x] - (255.0f-cover); if (c<0) c = 0; map[x] = 255.0f - ((float)(pow(sharpness, c))*255.0f); } } void finishNoise(float *map32, float *map256) { OverlapOctaves(map32, map256); ExpFilter(map256); } void makeCheckImage(float *map256) { //yo check it! when you set checkImage[1] and [2] to 1 you get red; //you leave them all as (GLubyte) you get white! tada smoke and fire //word int i, j; float c; //int random=rand() % 5000; for (i = 0; i < checkImageHeight; i++) { for (j = 0; j < checkImageWidth; j++) { //c = ((((i&0x8)==0)^((j&0x8))==0))*255; c = map256[i*256+j]; checkImage[i][j][0] = 255; checkImage[i][j][1] = 255-c; checkImage[i][j][2] = 0; checkImage[i][j][3] = (GLubyte) 255; } } /* for (i = 0; i < checkImageHeight; i++) { for (j = 0; j < checkImageWidth; j++) { //c = ((((i&0x8)==0)^((j&0x8))==0))*255; c = map256[i*256+j]; checkImage[i][j][0] = (GLubyte)c; checkImage[i][j][1] = (GLubyte)c; checkImage[i][j][2] = (GLubyte)c; checkImage[i][j][3] = (GLubyte) 255; } } */ } void makeCheckImageSmoke(float *map256) { //yo check it! when you set checkImage[1] and [2] to 1 you get red; //you leave them all as (GLubyte) you get white! tada smoke and fire //word int i, j; float c; //int random=rand() % 5000; for (i = 0; i < checkImageHeight; i++) { for (j = 0; j < checkImageWidth; j++) { //c = ((((i&0x8)==0)^((j&0x8))==0))*255; c = map256[i*256+j]; checkImage2[i][j][0] = (GLubyte)c; checkImage2[i][j][1] = (GLubyte)c; checkImage2[i][j][2] = (GLubyte)c; checkImage2[i][j][3] = (GLubyte) 255; } } } /*////////////////////////////////////////////////////////////////////////////////////// Noise Code End ////////////////////////////////////////////////////////////////////////////////////*/ void drawSphere(int numMajor, int numMinor, float radius) { double majorStep = (M_PI / numMajor); double minorStep = (2.0 * M_PI / numMinor); int i, j; for (i = 0; i < numMajor; ++i) { double a = i * majorStep; double b = a + majorStep; double r0 = radius * sin(a); double r1 = radius * sin(b); GLfloat z0 = radius * cos(a); GLfloat z1 = radius * cos(b); glBegin(GL_TRIANGLE_STRIP); for (j = 0; j <= numMinor; ++j) { double c = j * minorStep; GLfloat x = cos(c); GLfloat y = sin(c); glNormal3f((x * r0) / radius, (y * r0) / radius, z0 / radius); glTexCoord2f(j / (GLfloat) numMinor, i / (GLfloat) numMajor); glVertex3f(x * r0, y * r0, z0); glNormal3f((x * r1) / radius, (y * r1) / radius, z1 / radius); glTexCoord2f(j / (GLfloat) numMinor, (i + 1) / (GLfloat) numMajor); glVertex3f(x * r1, y * r1, z1); } glEnd(); } } /* Initialize material property, light source, lighting model, * and depth buffer. */ void init(void) { GLfloat mat_specular[] = { 1.0, 1.0, 1.0, 1.0 }; GLfloat mat_shininess[] = { 50.0 }; GLfloat light_position[] = { 1.0, 1.0, 1.0, 0.0 }; glClearColor (0.0, 0.0, 0.0, 0.0); glShadeModel (GL_SMOOTH); glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular); glMaterialfv(GL_FRONT, GL_SHININESS, mat_shininess); glLightfv(GL_LIGHT0, GL_POSITION, light_position); //glEnable(GL_LIGHTING); glEnable(GL_LIGHT0); glEnable(GL_DEPTH_TEST); glClearDepth(1.0f); // Depth Buffer Setup glDisable(GL_DEPTH_TEST); // Disable Depth Testing glEnable(GL_BLEND); // Enable Blending glBlendFunc(GL_SRC_ALPHA,GL_ONE); // Type Of Blending To Perform glHint(GL_PERSPECTIVE_CORRECTION_HINT,GL_NICEST); // Really Nice Perspective Calculations glHint(GL_POINT_SMOOTH_HINT,GL_NICEST); // Really Nice Point Smoothing SetNoise(map32); finishNoise(map32,map256); SetNoise(map32_2); finishNoise(map32_2,map256_2); makeCheckImage(map256); makeCheckImageSmoke(map256_2); initParticles(); glPixelStorei(GL_UNPACK_ALIGNMENT, 1); } void display(void) { glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); drawParticles(); float p1,p2; //finishNoise(map32,map256); //printf("time cout is %4.4f\n", timecount); /* if(timecount >= 1.0) { timecount = timecount -1; *map32 = *map32_2; SetNoise(map32_2); } p2 = timecount; p1 = 1 - timecount; //printf(" p2 is: %4.4f p1 is %4.4f \n", p2,p1); for(int i = 1; i < 33; i++) { for(int j = 1; j < 33; j++) { Animap[((i-1)*32) + (j-1)] = (map32[((i-1)*32) + (j-1)] * p1) + (map32_2[((i-1)*32) + (j-1)] * p2); } } timecount = timecount + timestep; */ SetNoise(map32); finishNoise(map32,map256); makeCheckImage(map256); makeCheckImageSmoke(map256); glEnable(GL_DEPTH_TEST); //finishNoise(map32,map256); //finishNoise(Animap,map256); #ifdef GL_VERSION_1_1 glGenTextures(1, &texName); glBindTexture(GL_TEXTURE_2D, texName); #endif glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); #ifdef GL_VERSION_1_1 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, checkImageWidth, checkImageHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, checkImage); #else glTexImage2D(GL_TEXTURE_2D, 0, 4, checkImageWidth, checkImageHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, checkImage); #endif //glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glEnable(GL_TEXTURE_2D); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL); #ifdef GL_VERSION_1_1 glBindTexture(GL_TEXTURE_2D, texName); #endif glEnable(GL_TEXTURE_2D); glPushMatrix(); //glTranslatef(1,0,0); glRotatef(90,0,1,0); drawSphere(256,256,0.20); glPopMatrix(); glDisable(GL_TEXTURE_2D); //glFlush(); #ifdef GL_VERSION_1_1 glGenTextures(1, &texName); glBindTexture(GL_TEXTURE_2D, texName); #endif glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); #ifdef GL_VERSION_1_1 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, checkImageWidth, checkImageHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, checkImage2); #else glTexImage2D(GL_TEXTURE_2D, 0, 4, checkImageWidth, checkImageHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, checkImage2); #endif //glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glEnable(GL_TEXTURE_2D); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL); #ifdef GL_VERSION_1_1 glBindTexture(GL_TEXTURE_2D, texName); #endif glEnable(GL_TEXTURE_2D); //glEnable(GL_BLEND); //glBlendFunc(GL_SRC_ALPHA, GL_ONE); glPushMatrix(); glScalef(.4,.4,0.5); glTranslatef(1,1,-1); //glRasterPos2i(0,0); glBegin(GL_QUADS); glTexCoord2f(0.0, 0.0); glVertex3f(-1.5, -1.0, 0.0); glTexCoord2f(0.0, 1.0); glVertex3f(-3.0, 2.0, 0.0); glTexCoord2f(1.0, 1.0); glVertex3f(1.0, 2.0, 0.0); glTexCoord2f(1.0, 0.0); glVertex3f(-0.5, -1.0, 0.0); glEnd(); glPopMatrix(); glFlush (); glutSwapBuffers(); } void reshape (int w, int h) { glViewport (0, 0, (GLsizei) w, (GLsizei) h); glMatrixMode (GL_PROJECTION); glLoadIdentity(); if (w <= h) glOrtho (-1.5, 1.5, -1.5*(GLfloat)h/(GLfloat)w, 1.5*(GLfloat)h/(GLfloat)w, -10.0, 10.0); else glOrtho (-1.5*(GLfloat)w/(GLfloat)h, 1.5*(GLfloat)w/(GLfloat)h, -1.5, 1.5, -10.0, 10.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); } void keyboard(unsigned char key, int x, int y) { switch (key) { case 'C': cover+= 10.0f; printf("Cover in proc texture is now: %4.4f\n", cover); break; case 'c': cover-= 10.0f; printf("Cover in proc texture is now: %4.4f\n", cover); break; case 'P': sharpness+= 0.05f; printf("Sharpness in proc texture is now: %4.4f\n", sharpness); break; case 'p': sharpness-= 0.05f; printf("Sharpness in proc texture is now: %4.4f\n", sharpness); break; case 'h': printf("+/- for all variables\nT/t animation timestep\nS/s particle timestep \nF/f particle birthrate\nW/w Vertical Wind Speed\nG/g gravity\nP/p Proc Texture Sharpness\nC/c Cover ~ Turbulence\nV/v Viscous Drag\n"); break; case 'V': vdrag+= 0.01; printf("The viscous drag is: %4.4f\n", vdrag); break; case 'v': vdrag-= 0.01; printf("The viscous drag is: %4.4f\n", vdrag); break; case 'G': gravity-= 1.00; printf("The gravity is: %4.4f\n", gravity); break; case 'g': gravity+= 1.00; printf("The gravity is: %4.4f\n", gravity); break; case 'T': timestep+= 0.05; printf("The animation time step is: %4.4f\n", timestep); break; case 't': timestep-= 0.05; printf("The animation time step is: %4.4f\n", timestep); break; case 'S': partTimestep-= 0.01f; printf("Decrement The particle time step is: %4.4f\n", partTimestep); break; case 's': partTimestep+= 0.01f; printf("Increment by 0.01f: The particle time step is: %4.4f\n", partTimestep); break; case 'f': yspeed -= 1.00f; printf("The yspeed is: %4.4f\n", yspeed); break; case 'F': yspeed += 1.00f; printf("The yspeed is: %4.4f\n", yspeed); break; case 'W': wind_veloc+= 1.00f; printf("The Vertical wind veloc is now: %4.4f increases past 4 tends to be bad\n",wind_veloc); break; case 'w': wind_veloc-= 1.00f; printf("The Vertical wind veloc is now: %4.4f\n",wind_veloc); break; case 27: exit(0); break; } } void timedUpdate(int value) { glutPostRedisplay(); glutTimerFunc(200, timedUpdate, 0); } int main(int argc, char** argv) { glutInit(&argc, argv); glutInitDisplayMode (GLUT_SINGLE | GLUT_RGB | GLUT_DEPTH); glutInitWindowSize (500, 500); glutInitWindowPosition (100, 100); glutCreateWindow (argv[0]); init (); glutDisplayFunc(display); glutReshapeFunc(reshape); glutTimerFunc(200, timedUpdate, 0); glutKeyboardFunc(keyboard); glutMainLoop(); return 0; }