📄 particle.c
字号:
/* particle.c Nate Robins, 1998 An example of a simple particle system. */#include <math.h>#include <stdio.h>#include <stdlib.h>#include <string.h>#include <GL/glut.h>#ifdef _WIN32#define drand48() ((float)rand()/RAND_MAX)#endif/* #define SCREEN_SAVER_MODE */#define PS_GRAVITY -9.8#define PS_WATERFALL 0#define PS_FOUNTAIN 1typedef struct { float x, y, z; float radius;} PSsphere;typedef struct { float position[3]; /* current position */ float previous[3]; /* previous position */ float velocity[3]; /* velocity (magnitude & direction) */ float dampening; /* % of energy lost on collision */ int alive; /* is this particle alive? */} PSparticle;PSparticle* particles = NULL;#define NUM_SPHERES 3PSsphere spheres[NUM_SPHERES] = { /* position of spheres */ { -0.1, 0.6, 0, 0.4 }, { -0.7, 1.4, 0, 0.25 }, { 0.1, 1.5, 0.1, 0.3 },};int num_particles = 10000;int type = PS_WATERFALL;int points = 1;int draw_spheres = 1;int frame_rate = 1;float frame_time = 0;float flow = 500;float slow_down = 1;float spin_x = 0;float spin_y = 0;int point_size = 4;/* timedelta: returns the number of seconds that have elapsed since the previous call to the function. */#if defined(_WIN32)#include <sys/timeb.h>#else#include <limits.h>#include <unistd.h>#include <sys/types.h>#include <sys/times.h>#endif
#ifndef CLK_TCK
#define CLK_TCK 1000
#endiffloattimedelta(void){ static long begin = 0; static long finish, difference;#if defined(_WIN32) static struct timeb tb; ftime(&tb); finish = tb.time*1000+tb.millitm;#else static struct tms tb; finish = times(&tb);#endif difference = finish - begin; begin = finish; return (float)difference/(float)CLK_TCK;}/* text: draws a string of text with an 18 point helvetica bitmap font at position (x,y) in window space (bottom left corner is (0,0). */voidtext(int x, int y, char* s) { int lines; char* p; glDisable(GL_DEPTH_TEST); glDisable(GL_FOG); glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); glOrtho(0, glutGet(GLUT_WINDOW_WIDTH), 0, glutGet(GLUT_WINDOW_HEIGHT), -1, 1); glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity(); glColor3ub(0, 0, 0); glRasterPos2i(x+1, y-1); for(p = s, lines = 0; *p; p++) { if (*p == '\n') { lines++; glRasterPos2i(x+1, y-1-(lines*18)); } glutBitmapCharacter(GLUT_BITMAP_HELVETICA_18, *p); } glColor3ub(128, 0, 255); glRasterPos2i(x, y); for(p = s, lines = 0; *p; p++) { if (*p == '\n') { lines++; glRasterPos2i(x, y-(lines*18)); } glutBitmapCharacter(GLUT_BITMAP_HELVETICA_18, *p); } glMatrixMode(GL_PROJECTION); glPopMatrix(); glMatrixMode(GL_MODELVIEW); glPopMatrix(); glEnable(GL_FOG); glEnable(GL_DEPTH_TEST);}intfequal(float a, float b){ float epsilon = 0.1; float f = a - b; if (f < epsilon && f > -epsilon) return 1; else return 0;}voidpsTimeStep(PSparticle* p, float dt){ if (p->alive == 0) return; p->velocity[0] += 0; p->velocity[1] += PS_GRAVITY*dt; p->velocity[2] += 0; p->previous[0] = p->position[0]; p->previous[1] = p->position[1]; p->previous[2] = p->position[2]; p->position[0] += p->velocity[0]*dt; p->position[1] += p->velocity[1]*dt; p->position[2] += p->velocity[2]*dt;}voidpsNewParticle(PSparticle* p, float dt){ if (type == PS_WATERFALL) { p->velocity[0] = -2*(drand48()-0.0); p->velocity[1] = 0; p->velocity[2] = 0.5*(drand48()-0.0); p->position[0] = 0; p->position[1] = 2; p->position[2] = 0; p->previous[0] = p->position[0]; p->previous[1] = p->position[1]; p->previous[2] = p->position[2]; p->dampening = 0.45*drand48(); p->alive = 1; } else if (type == PS_FOUNTAIN) { p->velocity[0] = 2*(drand48()-0.5); p->velocity[1] = 5; p->velocity[2] = 2*(drand48()-0.5); p->position[0] = -0.1; p->position[1] = 0.9; p->position[2] = 0; p->previous[0] = p->position[0]; p->previous[1] = p->position[1]; p->previous[2] = p->position[2]; p->dampening = 0.35*drand48(); p->alive = 1; } psTimeStep(p, 2*dt*drand48());}/* psBounce: the particle has gone past (or exactly hit) the ground plane, so calculate the time at which the particle actually intersected the ground plane (s). essentially, this just rolls back time to when the particle hit the ground plane, then starts time again from then. - - o A (previous position) | | \ | s \ o (position it _should_ be at) - t | \ / | t - s | - ------X-------- - | \ - o B (new position) A + V*s = 0 or s = -A/V to calculate where the particle should be: A + V*t + V*(t-s)*d where d is a damping factor which accounts for the loss of energy due to the bounce. */voidpsBounce(PSparticle* p, float dt){ float s; if (p->alive == 0) return; /* since we know it is the ground plane, we only need to calculate s for a single dimension. */ s = -p->previous[1]/p->velocity[1]; p->position[0] = (p->previous[0] + p->velocity[0] * s + p->velocity[0] * (dt-s) * p->dampening); p->position[1] = -p->velocity[1] * (dt-s) * p->dampening; /* reflect */ p->position[2] = (p->previous[2] + p->velocity[2] * s + p->velocity[2] * (dt-s) * p->dampening); /* damp the reflected velocity (since the particle hit something, it lost some energy) */ p->velocity[0] *= p->dampening; p->velocity[1] *= -p->dampening; /* reflect */ p->velocity[2] *= p->dampening;}voidpsCollideSphere(PSparticle* p, PSsphere* s){ float vx = p->position[0] - s->x; float vy = p->position[1] - s->y; float vz = p->position[2] - s->z; float distance; if (p->alive == 0) return; distance = sqrt(vx*vx + vy*vy + vz*vz); if (distance < s->radius) {#if 0 vx /= distance; vy /= distance; vz /= distance; d = 2*(-vx*p->velocity[0] + -vy*p->velocity[1] + -vz*p->velocity[2]); p->velocity[0] += vx*d*2; p->velocity[1] += vy*d*2; p->velocity[2] += vz*d*2; d = sqrt(p->velocity[0]*p->velocity[0] + p->velocity[1]*p->velocity[1] + p->velocity[2]*p->velocity[2]); p->velocity[0] /= d; p->velocity[1] /= d; p->velocity[2] /= d;#else p->position[0] = s->x+(vx/distance)*s->radius; p->position[1] = s->y+(vy/distance)*s->radius; p->position[2] = s->z+(vz/distance)*s->radius; p->previous[0] = p->position[0]; p->previous[1] = p->position[1]; p->previous[2] = p->position[2]; p->velocity[0] = vx/distance; p->velocity[1] = vy/distance; p->velocity[2] = vz/distance;#endif }}voidreshape(int width, int height){ float black[] = { 0, 0, 0, 0 }; glViewport(0, 0, width, height); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(60, (float)width/height, 0.1, 1000); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); gluLookAt(0, 1, 3, 0, 1, 0, 0, 1, 0); glFogfv(GL_FOG_COLOR, black); glFogf(GL_FOG_START, 2.5); glFogf(GL_FOG_END, 4); glEnable(GL_FOG); glFogi(GL_FOG_MODE, GL_LINEAR); glPointSize(point_size); glEnable(GL_POINT_SMOOTH); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnable(GL_COLOR_MATERIAL); glEnable(GL_DEPTH_TEST); glEnable(GL_LIGHT0); timedelta();}voiddisplay(void){ static int i; static float c; static int j = 0; static char s[32]; static int frames = 0; glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); glPushMatrix(); glRotatef(spin_y, 1, 0, 0); glRotatef(spin_x, 0, 1, 0); glEnable(GL_LIGHTING); if (draw_spheres) { for (i = 0; i < draw_spheres; i++) { glPushMatrix(); glTranslatef(spheres[i].x, spheres[i].y, spheres[i].z); glColor3ub(0, 255, 128); glutSolidSphere(spheres[i].radius, 16, 16); glPopMatrix(); } } glDisable(GL_LIGHTING); glBegin(GL_QUADS); glColor3ub(0, 128, 255); glVertex3f(-2, 0, -2); glVertex3f(-2, 0, 2); glVertex3f(2, 0, 2); glVertex3f(2, 0, -2); glEnd(); if (points) { glBegin(GL_POINTS); for (i = 0; i < num_particles; i++) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -