A collection of visual particle simulators in various languages. Check the different branches. :)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

442 lines
12 KiB

#include <vector>
#include <GL/glew.h>
#include <GL/freeglut.h>
#include <GL/glu.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
using namespace std;
#define PCOUNT 200
#define TRAIL_NODES 10
#define TRAIL_EDGE_LEN 3
#define randpos ((float)(rand() % 2000) / 1000.0 - 1)
//#define USE_SPACE_MOUSE 1
#define PRINT_FPS 1
#define FORCE_CONSTANT 0.00005
#define PULLING_FORCE_CONSTANT -0.00025
#define PULLBACK_THRESHOLD 5.4142
//#define ANAGLYPH 1
//#define PULLBACK_THRESHOLD 1.4142
bool show_trail = 1;
bool show_wire = 1;
bool auto_rotate = 0;
bool slow_down = 0;
bool keys_pressed[256];
bool pause = 0;
bool rotL = 0, rotR = 0, rotU = 0, rotD = 0;
float rotationX = 0.0f, rotationY = 0.0f, rotationZ = 0.0f;
float zoom = -8.5f, zoom_min = -30.0f, zoom_max = -1.0f;
#ifdef PRINT_FPS
int fps_counter = 0, next_fps_print_time = 0;
#endif
#ifdef USE_SPACE_MOUSE
float pointerX, pointerY, pointerZ;
bool sm_apply_force = 0;
bool sm_rotate = 0;
#define POINTER_FORCE_CONSTANT 4000000.0
#endif
#ifdef ANAGLYPH
#include "stereo.cpp"
#endif
void calculate_next_frame();
void place_elements();
class Particle {
public:
float charge, mass;
float x, y, z;
float vx, vy, vz;
unsigned int trail_tick, trail_pos;
vector<vector<float> > trail;
Particle() { };
~Particle() { };
void init(float x, float y, float z, unsigned int i) {
this->x = x;
this->y = y;
this->z = z;
this->trail_tick = 0;
this->trail_pos = 0;
this->trail.resize(TRAIL_NODES);
for (unsigned int i = 0; i < this->trail.size(); i++) {
this->trail[i].resize(3);
this->trail[i][0] = this->x;
this->trail[i][1] = this->y;
this->trail[i][2] = this->z;
}
// this->charge = (float)((rand() % 3) - 1) * 1.0;
this->charge = (float)((rand() % 2) * 2 - 1);
this->mass = 0.002;
if (charge == 0) {
this->vx = (float)(rand() % 100) / 10000;
this->vy = (float)(rand() % 100) / 10000;
this->vz = (float)(rand() % 100) / 10000;
}
else {
this->vx = 0;
this->vy = 0;
this->vz = 0;
}
};
void vadd(float factor, float dx, float dy, float dz) {
this->vx += factor * dx;
this->vy += factor * dy;
this->vz += factor * dz;
}
void posadd() {
this->x += this->vx;
this->y += this->vy;
this->z += this->vz;
//////////////
// store the last position in the trail ringbuffer
if (TRAIL_NODES) {
this->trail_tick ++;
if (this->trail_tick >= TRAIL_EDGE_LEN) {
this->trail_tick = 0;
this->trail[this->trail_pos][0] = this->x;
this->trail[this->trail_pos][1] = this->y;
this->trail[this->trail_pos][2] = this->z;
this->trail_pos = (this->trail_pos + 1) % TRAIL_NODES;
}
}
}
};
vector<Particle> particles;
void idle_callback() {
#ifdef USE_SPACE_MOUSE
FILE *f = fopen("/tmp/spacemouse_pos", "r");
if (f != NULL) {
fscanf(f, "%f %f %f", &pointerX, &pointerY, &pointerZ);
pointerX /= 4096 / 20;
pointerY /= -4096 / 20;
pointerZ /= 4096 / 5;
// pointerX /= 4096;
// pointerY /= -4096;
// pointerZ /= 4096;
// pointerZ /= 8192;
fclose(f);
}
#endif
#ifdef PRINT_FPS
////////////////////
// print FPS
int currentTime = glutGet(GLUT_ELAPSED_TIME);
fps_counter++;
if (next_fps_print_time < currentTime) {
next_fps_print_time = currentTime + 1000;
printf("fps: %d\n", fps_counter);
fps_counter = 0;
}
#endif
glMatrixMode(GL_MODELVIEW);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
glTranslatef(0.0f, 0.0f, zoom);
// glRotatef(30.0f, 0.0f, 0.0f, 1.0f);
// glRotatef(-30.0f, 0.0f, 0.0f, 1.0f);
rotationX += 2.0f * (rotL - rotR) + 0.2f * auto_rotate;
rotationY += 2.0f * (rotD - rotU);
#ifdef USE_SPACE_MOUSE
if (sm_rotate) {
rotationX += pointerX * 10.0f;
// printf("%f\n", rotationX);
rotationY -= pointerY * 10.0f * cos(rotationX / 180 * M_PI);
rotationZ -= pointerY * 10.0f * sin(rotationX / 180 * M_PI);
zoom += pointerZ;
if (zoom < zoom_min) zoom = zoom_min;
if (zoom > zoom_max) zoom = zoom_max;
// rotationZ += pointerY * 10.0f * cos(rotationX);
// rotationZ += pointerZ * 10.0f;
}
#else
zoom += (keys_pressed['='] - keys_pressed['-']) * 0.5;
#endif
#ifdef ANAGLYPH
StereoCamera cam(
10.0f, // Convergence
35.0f, // Eye Separation
(GLfloat)200/(GLfloat)150, // Aspect Ratio
35.0f, // FOV along Y in degrees
0.0001f, // Near Clipping Distance
20000.0f); // Far Clipping Distance
// cam.ApplyLeftFrustum();
glRotatef(20.0f, 0.0f, 1.0f, 0.0f);
glColorMask(true, false, false, false);
//
place_elements();
//
glClear(GL_DEPTH_BUFFER_BIT) ;
// cam.ApplyRightFrustum();
// glRotatef(-20.0f, 0.0f, 1.0f, 0.0f);
glColorMask(false, true, true, false);
place_elements();
glColorMask(true, true, true, true);
#else
place_elements();
#endif
if (pause) {
glutSwapBuffers();
return;
}
calculate_next_frame();
glutSwapBuffers();
}
void place_elements() {
glRotatef(rotationX, 0.0f, 1.0f, 0.0f);
glRotatef(rotationY, 1.0f, 0.0f, 0.0f);
glRotatef(rotationZ, 0.0f, 0.0f, 1.0f);
glTranslatef(0.0f, 0.0f, 0.5f);
GLfloat position[] = { 0.0f, 0.0f, -3.0f, 100.0f};
glLightfv(GL_LIGHT0, GL_POSITION, position);
glTranslatef(0.0f, 0.0f, -0.5);
glColor3f(0.0f, 0.5f, 0.0f);
if (show_wire)
glutWireCube(2.0);
Particle pr;
for (unsigned int i = 0; i < PCOUNT; i++) {
pr = particles[i];
if (pr.charge > 0) {
glColor3f(1.0f, 0.0f, 0.0f);
}
else if (pr.charge < 0) {
glColor3f(0.0f, 1.0f, 0.0f);
}
else {
glColor3f(1.0f, 1.0f, 0.0f);
}
// glColor3f(1.0f, 1.0f, 1.0f);
glTranslatef(pr.x, pr.y, pr.z);
glutSolidSphere(0.003, 10, 10);
// glutSolidCube(0.008);
glTranslatef(-pr.x, -pr.y, -pr.z);
}
if (show_trail && TRAIL_NODES) {
float color;
glBegin(GL_LINES);
for (unsigned int i = 0; i < PCOUNT; i++) {
pr = particles[i];
color = 1.0f;
if (pr.charge > 0) {
glColor3f(color, 0.0f, 0.0f);
}
else if (pr.charge < 0) {
glColor3f(0.0f, color, 0.0f);
} else {
glColor3f(color, color, 0.0f);
}
// glColor3f(1.0f, 1.0f, 1.0f);
glVertex3f(pr.x, pr.y, pr.z);
int pos = pr.trail_pos - 1;
if (pos < 0) {
pos = TRAIL_NODES - 1;
}
glVertex3f(pr.trail[pos][0], pr.trail[pos][1], pr.trail[pos][2]);
for (unsigned int j = 0; j < TRAIL_NODES - 1; j++) {
unsigned int j_offset = (j + pr.trail_pos ) % TRAIL_NODES;
unsigned int j_next = (j + pr.trail_pos + 1) % TRAIL_NODES;
color = 1.0f * j / TRAIL_NODES;
if (pr.charge > 0) {
glColor3f(color, 0.0f, 0.0f);
} else if (pr.charge < 0) {
glColor3f(0.0f, color, 0.0f);
} else {
glColor3f(color, color, 0.0f);
}
// glColor3f(1.0f, 1.0f, 1.0f);
glVertex3f(pr.trail[j_offset][0], pr.trail[j_offset][1], pr.trail[j_offset][2]);
glVertex3f(pr.trail[j_next][0], pr.trail[j_next][1], pr.trail[j_next][2]);
}
}
glEnd();
#ifdef USE_SPACE_MOUSE
// glClearDepth(1.0f);
glClear(GL_DEPTH_BUFFER_BIT) ;
// glDisable(GL_DEPTH_TEST);
glTranslatef(pointerX, pointerY, pointerZ);
glColor3f(0.0f, 0.0f, 0.8f);
// glRotatef(4.0, 0.0f, 1.0f, 0.0f);
glutWireSphere(0.08, 30, 10);
// glutWireCube(0.1);
// glRotatef(-4.0, 0.0f, 1.0f, 0.0f);
glTranslatef(-pointerX, -pointerY, -pointerZ);
// glEnable(GL_DEPTH_TEST);
#endif
}
}
void calculate_next_frame() {
//////////////////
// calculate forces
float dist, factor, dx, dy, dz;
Particle *p, *q;
for (unsigned int i = 0; i < PCOUNT; i++) {
p = &particles[i];
for (unsigned int j = i + 1; j < PCOUNT; j++) {
q = &particles[j];
dx = p->x - q->x;
dy = p->y - q->y;
dz = p->z - q->z;
dist = sqrt(dx * dx + dy * dy + dz * dz);
factor = FORCE_CONSTANT / dist / dist;
// electric force
p->vadd(factor * p->charge * q->charge, dx, dy, dz);
q->vadd(-factor * p->charge * q->charge, dx, dy, dz);
// gravitation
p->vadd(-factor * p->mass * q->mass, dx, dy, dz);
q->vadd(-factor * p->mass * q->mass, dx, dy, dz);
}
}
#ifdef USE_SPACE_MOUSE
if (sm_apply_force)
for (unsigned int i = 0; i < PCOUNT; i++) {
p = &particles[i];
dx = p->x - pointerX;
dy = p->y - pointerY;
dz = p->z - pointerZ;
dist = sqrt(dx * dx + dy * dy + dz * dz);
factor = POINTER_FORCE_CONSTANT * FORCE_CONSTANT / dist;
p->vadd(-factor * p->mass * q->mass, dx, dy, dz);
}
#endif
//////////////
// keep the particle in the box
for (unsigned int i = 0; i < PCOUNT; i++) {
p = &particles[i];
dist = sqrt(p->x * p->x + p->y * p->y + p->z * p->z) - PULLBACK_THRESHOLD;
if (dist > 0)
p->vadd(PULLING_FORCE_CONSTANT * dist, p->x, p->y, p->z);
}
if (slow_down) {
for (unsigned int i = 0; i < PCOUNT; i++) {
p = &particles[i];
p->vx *= 0.95;
p->vy *= 0.95;
p->vz *= 0.95;
}
}
for (unsigned int i = 0; i < PCOUNT; i++) {
p = &particles[i];
p->posadd();
}
}
void display_callback() {
}
void key_callback(unsigned char key, int x, int y) {
keys_pressed[key] = true;
switch(key) {
case ' ': pause ^= 1; break;
case 'h': rotL = 1; break;
case 'l': rotR = 1; break;
case 'k': rotU = 1; break;
case 'j': rotD = 1; break;
case '0': rotationX = rotationY = rotationZ = 0.0f; break;
case 'q': exit(0); break;
case 'w': show_wire ^= 1; break;
case 't': show_trail ^= 1; break;
case 'r': auto_rotate ^= 1; break;
case 's': slow_down = 1; break;
#ifdef USE_SPACE_MOUSE
case 'f': sm_apply_force = 1; break;
case 'd': sm_rotate = 1; break;
#endif
}
}
void key_release_callback(unsigned char key, int x, int y) {
keys_pressed[key] = false;
switch(key) {
case 'h': rotL = 0; break;
case 'l': rotR = 0; break;
case 'k': rotU = 0; break;
case 'j': rotD = 0; break;
case 's': slow_down = 0; break;
#ifdef USE_SPACE_MOUSE
case 'f': sm_apply_force = 0; break;
case 'd': sm_rotate = 0; break;
#endif
}
}
int main(int argc, char *argv[]) {
// initialize glut
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH);
glutInitWindowSize(1024, 768);
glutCreateWindow("test");
glMatrixMode(GL_PROJECTION);
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LESS);
glClearDepth(1.0f);
glShadeModel(GL_SMOOTH);
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
glutSetKeyRepeat(GLUT_KEY_REPEAT_OFF);
// enable lighting
// GLfloat ambientLight[] = { 0.2f, 0.9f, 0.2f, 1.0f };
// GLfloat diffuseLight[] = { 0.8f, 0.0f, 0.0, 1.0f };
GLfloat specularLight[] = { 0.0f, 1.0f, 1.0f, 1.0f };
// glLightfv(GL_LIGHT0, GL_AMBIENT, ambientLight);
// glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuseLight);
glLightfv(GL_LIGHT0, GL_SPECULAR, specularLight);
glEnable(GL_LIGHT0);
glEnable(GL_LIGHTING); // Enables Depth Testing
glEnable(GL_COLOR_MATERIAL);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(35.0f, (GLfloat)200/(GLfloat)150, 0.1f, 100.0f);
// glColorMask(false, false, true, true);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
// glut callbacks
glutIdleFunc(idle_callback);
glutKeyboardFunc(key_callback);
glutKeyboardUpFunc(key_release_callback);
glutDisplayFunc(display_callback);
particles.resize(PCOUNT);
Particle p;
for (unsigned int i = 0; i < PCOUNT; i++) {
p = Particle();
p.init(randpos, randpos, randpos, i);
particles[i] = p;
}
// initialize glew
glewInit();
glutMainLoop();
return 0;
}