cloth
changeset 0:92983e143a03
initial commit
author | John Tsiombikas <nuclear@member.fsf.org> |
---|---|
date | Mon, 11 Feb 2013 19:40:36 +0200 |
parents | |
children | 76d4b3e8e941 |
files | .hgignore Makefile src/main.cc src/opengl.h src/particle.cc src/particle.h src/plane.cc src/plane.h src/simworld.cc src/simworld.h |
diffstat | 10 files changed, 646 insertions(+), 0 deletions(-) [+] |
line diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/.hgignore Mon Feb 11 19:40:36 2013 +0200 1.3 @@ -0,0 +1,4 @@ 1.4 +\.o$ 1.5 +\.d$ 1.6 +\.swp$ 1.7 +^cloth$
2.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 2.2 +++ b/Makefile Mon Feb 11 19:40:36 2013 +0200 2.3 @@ -0,0 +1,25 @@ 2.4 +src = $(wildcard src/*.cc) 2.5 +obj = $(src:.cc=.o) 2.6 +dep = $(obj:.o=.d) 2.7 +bin = cloth 2.8 + 2.9 +CXXFLAGS = -pedantic -Wall -g 2.10 +LDFLAGS = $(libgl) -limago -lvmath 2.11 + 2.12 +ifeq ($(shell uname -s), Darwin) 2.13 + libgl = -framework OpenGL -framework GLUT -lGLEW 2.14 +else 2.15 + libgl = -lGL -lGLU -lglut -lGLEW 2.16 +endif 2.17 + 2.18 +$(bin): $(obj) 2.19 + $(CXX) -o $@ $(obj) $(LDFLAGS) 2.20 + 2.21 +-include $(dep) 2.22 + 2.23 +%.d: %.cc 2.24 + @$(CPP) $(CXXFLAGS) $< -MM -MT $(@:.d=.o) >$@ 2.25 + 2.26 +.PHONY: clean 2.27 +clean: 2.28 + rm -f $(obj) $(bin)
3.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 3.2 +++ b/src/main.cc Mon Feb 11 19:40:36 2013 +0200 3.3 @@ -0,0 +1,144 @@ 3.4 +#include <stdio.h> 3.5 +#include <stdlib.h> 3.6 +#include "opengl.h" 3.7 + 3.8 +static bool init(); 3.9 +static void cleanup(); 3.10 +static void disp(); 3.11 +static void idle(); 3.12 +static void reshape(int x, int y); 3.13 +static void keyb(unsigned char key, int x, int y); 3.14 +static void mouse(int bn, int state, int x, int y); 3.15 +static void motion(int x, int y); 3.16 + 3.17 +static float cam_theta, cam_phi, cam_dist = 8.0; 3.18 + 3.19 +static bool moving_cloth; 3.20 + 3.21 + 3.22 +int main(int argc, char **argv) 3.23 +{ 3.24 + glutInit(&argc, argv); 3.25 + glutInitWindowSize(800, 600); 3.26 + glutInitDisplayMode(GLUT_RGB | GLUT_DEPTH | GLUT_DOUBLE); 3.27 + glutCreateWindow("cloth"); 3.28 + 3.29 + glutDisplayFunc(disp); 3.30 + glutReshapeFunc(reshape); 3.31 + glutKeyboardFunc(keyb); 3.32 + glutMouseFunc(mouse); 3.33 + glutMotionFunc(motion); 3.34 + glutIdleFunc(idle); 3.35 + 3.36 + if(!init()) { 3.37 + return 1; 3.38 + } 3.39 + atexit(cleanup); 3.40 + 3.41 + glutMainLoop(); 3.42 + return 0; 3.43 +} 3.44 + 3.45 + 3.46 +static bool init() 3.47 +{ 3.48 + glewInit(); 3.49 + 3.50 + glClearColor(0.2, 0.2, 0.2, 1.0); 3.51 + 3.52 + glEnable(GL_DEPTH_TEST); 3.53 + glEnable(GL_CULL_FACE); 3.54 + 3.55 + return true; 3.56 +} 3.57 + 3.58 +static void cleanup() 3.59 +{ 3.60 +} 3.61 + 3.62 +static void disp() 3.63 +{ 3.64 + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 3.65 + 3.66 + glMatrixMode(GL_MODELVIEW); 3.67 + glLoadIdentity(); 3.68 + glTranslatef(0, 0, -cam_dist); 3.69 + glRotatef(cam_phi, 1.0, 0.0, 0.0); 3.70 + glRotatef(cam_theta, 0.0, 1.0, 0.0); 3.71 + 3.72 + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); 3.73 + 3.74 + glutSolidTeapot(1.0); 3.75 + 3.76 + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); 3.77 + 3.78 + glutSwapBuffers(); 3.79 +} 3.80 + 3.81 +static void idle() 3.82 +{ 3.83 + glutPostRedisplay(); 3.84 +} 3.85 + 3.86 +static void reshape(int x, int y) 3.87 +{ 3.88 + glViewport(0, 0, x, y); 3.89 + 3.90 + glMatrixMode(GL_PROJECTION); 3.91 + glLoadIdentity(); 3.92 + gluPerspective(45.0, (float)x / (float)y, 0.5, 500.0); 3.93 +} 3.94 + 3.95 +static void keyb(unsigned char key, int x, int y) 3.96 +{ 3.97 + switch(key) { 3.98 + case 27: 3.99 + exit(0); 3.100 + } 3.101 +} 3.102 + 3.103 +static bool bnstate[16]; 3.104 +static int prev_x, prev_y; 3.105 + 3.106 +static void mouse(int bn, int state, int x, int y) 3.107 +{ 3.108 + int idx = bn - GLUT_LEFT_BUTTON; 3.109 + int st = state == GLUT_DOWN ? 1 : 0; 3.110 + 3.111 + if(idx >= 0 && idx < 16) { 3.112 + bnstate[idx] = st; 3.113 + } 3.114 + 3.115 + prev_x = x; 3.116 + prev_y = y; 3.117 +} 3.118 + 3.119 +static void motion(int x, int y) 3.120 +{ 3.121 + int dx = x - prev_x; 3.122 + int dy = y - prev_y; 3.123 + prev_x = x; 3.124 + prev_y = y; 3.125 + 3.126 + if(moving_cloth) { 3.127 + } else { 3.128 + if(bnstate[0]) { 3.129 + cam_theta += dx * 0.5; 3.130 + cam_phi += dy * 0.5; 3.131 + 3.132 + if(cam_phi < -90.0) { 3.133 + cam_phi = -90.0; 3.134 + } 3.135 + if(cam_phi > 90.0) { 3.136 + cam_phi = 90.0; 3.137 + } 3.138 + } 3.139 + if(bnstate[2]) { 3.140 + cam_dist += dy * 0.1; 3.141 + 3.142 + if(cam_dist < 0.0) { 3.143 + cam_dist = 0.0; 3.144 + } 3.145 + } 3.146 + } 3.147 +}
4.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 4.2 +++ b/src/opengl.h Mon Feb 11 19:40:36 2013 +0200 4.3 @@ -0,0 +1,12 @@ 4.4 +#ifndef OPENGL_H_ 4.5 +#define OPENGL_H_ 4.6 + 4.7 +#include <GL/glew.h> 4.8 + 4.9 +#ifdef __APPLE__ 4.10 +#include <GLUT/glut.h> 4.11 +#else 4.12 +#include <GL/glut.h> 4.13 +#endif 4.14 + 4.15 +#endif // OPENGL_H_
5.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 5.2 +++ b/src/particle.cc Mon Feb 11 19:40:36 2013 +0200 5.3 @@ -0,0 +1,167 @@ 5.4 +#include <vector> 5.5 +#include "opengl.h" 5.6 +#include "particle.h" 5.7 +#include "simworld.h" 5.8 + 5.9 +Particle::Particle() 5.10 + : forces(0, 0, 0), pos(0, 0, 0), velocity(0, 0, 0) 5.11 +{ 5.12 + rad = 1.0; 5.13 + elast = 0.75; 5.14 + mass = 1.0; 5.15 + friction = 0.0; 5.16 +} 5.17 + 5.18 +void Particle::add_ignore(const Particle *p) 5.19 +{ 5.20 + ignorelist.insert(p); 5.21 +} 5.22 + 5.23 +void Particle::set_radius(float rad) 5.24 +{ 5.25 + this->rad = rad; 5.26 +} 5.27 + 5.28 +float Particle::get_radius() const 5.29 +{ 5.30 + return rad; 5.31 +} 5.32 + 5.33 +void Particle::set_mass(float m) 5.34 +{ 5.35 + mass = m; 5.36 +} 5.37 + 5.38 +float Particle::get_mass() const 5.39 +{ 5.40 + return mass; 5.41 +} 5.42 + 5.43 +void Particle::set_elasticity(float e) 5.44 +{ 5.45 + elast = e; 5.46 +} 5.47 + 5.48 +float Particle::get_elasticity() const 5.49 +{ 5.50 + return elast; 5.51 +} 5.52 + 5.53 +void Particle::set_position(const Vector3 &pos) 5.54 +{ 5.55 + this->pos = pos; 5.56 +} 5.57 + 5.58 +void Particle::set_velocity(const Vector3 &vel) 5.59 +{ 5.60 + velocity = vel; 5.61 +} 5.62 + 5.63 +Vector3 &Particle::get_position() 5.64 +{ 5.65 + return pos; 5.66 +} 5.67 + 5.68 +const Vector3 &Particle::get_position() const 5.69 +{ 5.70 + return pos; 5.71 +} 5.72 + 5.73 +Vector3 &Particle::get_velocity() 5.74 +{ 5.75 + return velocity; 5.76 +} 5.77 + 5.78 +const Vector3 &Particle::get_velocity() const 5.79 +{ 5.80 + return velocity; 5.81 +} 5.82 + 5.83 +void Particle::set_friction(float frict) 5.84 +{ 5.85 + friction = frict; 5.86 +} 5.87 + 5.88 +float Particle::get_friction() const 5.89 +{ 5.90 + return friction; 5.91 +} 5.92 + 5.93 +void Particle::add_force(const Vector3 &fvec) 5.94 +{ 5.95 + forces += fvec; 5.96 +} 5.97 + 5.98 +void Particle::step(SimWorld *world, float dt) 5.99 +{ 5.100 + Vector3 accel = forces / mass; 5.101 + forces.x = forces.y = forces.z = 0.0f; 5.102 + 5.103 + velocity = velocity * world->damping + accel * dt - velocity * friction * dt; 5.104 + 5.105 + Vector3 newpos = pos + velocity * dt; 5.106 + 5.107 + Ray ray(pos, newpos - pos); 5.108 + Collision col; 5.109 + 5.110 + if(world->collision(ray, rad, &col)) { 5.111 + pos = col.pos; 5.112 + velocity = -velocity.reflection(col.normal) * elast; 5.113 + } else { 5.114 + pos = newpos; 5.115 + } 5.116 +} 5.117 + 5.118 +bool Particle::collision(const Particle *p2, Collision *col) const 5.119 +{ 5.120 + if(ignorelist.find(p2) != ignorelist.end()) { 5.121 + return false; 5.122 + } 5.123 + 5.124 + Vector3 v = p2->pos - pos; 5.125 + float dist_sq = dot_product(v, v); 5.126 + float radsum = rad + p2->rad; 5.127 + 5.128 + if(dist_sq < radsum * radsum) { 5.129 + float rad_ratio = rad / p2->rad; 5.130 + Vector3 dir = p2->pos - pos; 5.131 + col->pos = pos + dir * rad_ratio; 5.132 + col->normal = -dir.normalized(); 5.133 + col->elast = elast * p2->elast; 5.134 + return true; 5.135 + } 5.136 + return false; 5.137 +} 5.138 + 5.139 +void Particle::draw() const 5.140 +{ 5.141 + float color[] = {0.3, 0.9, 0.2, 1}; 5.142 + 5.143 + glPushMatrix(); 5.144 + glTranslatef(pos.x, pos.y, pos.z); 5.145 + 5.146 + glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color); 5.147 + 5.148 + glutSolidSphere(rad, 16, 8); 5.149 + 5.150 + glPushAttrib(GL_ENABLE_BIT); 5.151 + glDisable(GL_DEPTH_TEST); 5.152 + glDisable(GL_LIGHTING); 5.153 + glEnable(GL_BLEND); 5.154 + glBlendFunc(GL_ONE, GL_ONE); 5.155 + glLineWidth(2.0); 5.156 + 5.157 + glBegin(GL_LINES); 5.158 + glColor3f(0.3, 0, 0); 5.159 + glVertex3f(0, 0, 0); 5.160 + glVertex3f(velocity.x, velocity.y, velocity.z); 5.161 + 5.162 + glColor3f(0, 0, 0.8); 5.163 + glVertex3f(0, 0, 0); 5.164 + glVertex3f(forces.x, forces.y, forces.z); 5.165 + glEnd(); 5.166 + 5.167 + glPopAttrib(); 5.168 + 5.169 + glPopMatrix(); 5.170 +}
6.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 6.2 +++ b/src/particle.h Mon Feb 11 19:40:36 2013 +0200 6.3 @@ -0,0 +1,57 @@ 6.4 +#ifndef PARTICLE_H_ 6.5 +#define PARTICLE_H_ 6.6 + 6.7 +#include <set> 6.8 +#include "vmath/vmath.h" 6.9 + 6.10 +class SimWorld; 6.11 +struct Collision; 6.12 + 6.13 +class Particle { 6.14 +private: 6.15 + Vector3 forces; 6.16 + Vector3 pos, velocity; 6.17 + float rad; 6.18 + float elast; 6.19 + float mass; 6.20 + float friction; 6.21 + 6.22 + std::set<const Particle*> ignorelist; 6.23 + 6.24 +public: 6.25 + Particle(); 6.26 + 6.27 + void add_ignore(const Particle *p); 6.28 + 6.29 + void set_radius(float rad); 6.30 + float get_radius() const; 6.31 + 6.32 + void set_mass(float m); 6.33 + float get_mass() const; 6.34 + 6.35 + void set_elasticity(float e); 6.36 + float get_elasticity() const; 6.37 + 6.38 + void set_position(const Vector3 &pos); 6.39 + void set_velocity(const Vector3 &vel); 6.40 + 6.41 + Vector3 &get_position(); 6.42 + const Vector3 &get_position() const; 6.43 + Vector3 &get_velocity(); 6.44 + const Vector3 &get_velocity() const; 6.45 + 6.46 + void set_friction(float frict); 6.47 + float get_friction() const; 6.48 + 6.49 + void add_force(const Vector3 &fvec); 6.50 + 6.51 + void step(SimWorld *world, float dt); 6.52 + 6.53 + bool collision(const Particle *p2, Collision *col) const; 6.54 + 6.55 + void draw() const; 6.56 + 6.57 + friend class SimWorld; 6.58 +}; 6.59 + 6.60 +#endif // PARTICLE_H_
7.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 7.2 +++ b/src/plane.cc Mon Feb 11 19:40:36 2013 +0200 7.3 @@ -0,0 +1,72 @@ 7.4 +#include "opengl.h" 7.5 +#include "plane.h" 7.6 +#include "simworld.h" 7.7 + 7.8 +Plane::Plane() 7.9 + : pt(0, 0, 0), normal(0, 1, 0) 7.10 +{ 7.11 +} 7.12 + 7.13 +Plane::Plane(const Vector3 &pt, const Vector3 &norm) 7.14 + this->pt(pt), normal(pt) 7.15 +{ 7.16 +} 7.17 + 7.18 +Plane::Plane(const Vector3 &norm, float dist) 7.19 + : normal(norm) 7.20 +{ 7.21 + pt = Vector3(0, 0, 0) - norm * dist; 7.22 +} 7.23 + 7.24 +bool Plane::collision(const Ray &ray, float rad, Collision *col) const 7.25 +{ 7.26 + Vector3 pt = this->pt - normal * rad; 7.27 + 7.28 + float ndotdir = dot_product(ray.dir, normal); 7.29 + if(fabs(ndotdir) < 1e-6) { 7.30 + return false; 7.31 + } 7.32 + 7.33 + float ndotptdir = dot_product((pt - ray.origin), normal); 7.34 + 7.35 + float t = ndotptdir / ndotdir; 7.36 + if(t < 1e-6 || t > (1.0 - 1e-6)) { 7.37 + return false; 7.38 + } 7.39 + 7.40 + col->pos = ray.origin + ray.dir * t; 7.41 + col->normal = normal; 7.42 + col->elast = 1.0; 7.43 + return true; 7.44 +} 7.45 + 7.46 +void Plane::draw(float sz) const 7.47 +{ 7.48 + Vector3 up = Vector3(0, 1, 0); 7.49 + if(fabs(dot_product(up, normal)) < 1e-6) { 7.50 + up = Vector3(0, 0, 1); 7.51 + } 7.52 + 7.53 + Vector3 right = cross_product(up, normal); 7.54 + up = cross_product(normal, right); 7.55 + 7.56 + Matrix4x4 rot_matrix; 7.57 + rot_matrix.set_column_vector(right, 0); 7.58 + rot_matrix.set_column_vector(up, 1); 7.59 + rot_matrix.set_column_vector(normal, 2); 7.60 + rot_matrix.set_row_vector(Vector4(0, 0, 0, 1), 3); 7.61 + 7.62 + glMatrixMode(GL_MODELVIEW); 7.63 + glPushMatrix(); 7.64 + glMultTransposeMatrixf((float*)rot_matrix.m); 7.65 + 7.66 + glBegin(GL_QUADS); 7.67 + glNormal3f(normal.x, normal.y, normal.z); 7.68 + glVertex2f(-sz, -sz); 7.69 + glVertex2f(sz, -sz); 7.70 + glVertex2f(sz, sz); 7.71 + glVertex2f(-sz, sz); 7.72 + glEnd(); 7.73 + 7.74 + glPopMatrix(); 7.75 +}
8.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 8.2 +++ b/src/plane.h Mon Feb 11 19:40:36 2013 +0200 8.3 @@ -0,0 +1,22 @@ 8.4 +#ifndef PLANE_H_ 8.5 +#define PLANE_H_ 8.6 + 8.7 +#include <vmath/vmath.h> 8.8 + 8.9 +struct Collision; 8.10 + 8.11 +class Plane { 8.12 +public: 8.13 + Vector3 pt; 8.14 + Vector3 normal; 8.15 + 8.16 + Plane(); 8.17 + Plane(const Vector3 &pt, const Vector3 &norm); 8.18 + Plane(const Vector3 &norm, float dist); 8.19 + 8.20 + bool collision(const Ray &ray, float rad, Collision *col) const; 8.21 + 8.22 + void draw(float sz) const; 8.23 +}; 8.24 + 8.25 +#endif // PLANE_H_
9.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 9.2 +++ b/src/simworld.cc Mon Feb 11 19:40:36 2013 +0200 9.3 @@ -0,0 +1,104 @@ 9.4 +#include "simworld.h" 9.5 + 9.6 +SimWorld::SimWorld() 9.7 +{ 9.8 + bbmin = Vector2(-1, -1); 9.9 + bbmax = Vector2(1, 1); 9.10 + grav = Vector3(0, -9.81, 0); 9.11 + damping = 0.99; 9.12 +} 9.13 + 9.14 +void SimWorld::set_bounds(float xmin, float xmax, float ymin, float ymax) 9.15 +{ 9.16 + bbmin.x = xmin; 9.17 + bbmin.y = ymin; 9.18 + bbmax.x = xmax; 9.19 + bbmax.y = ymax; 9.20 +} 9.21 + 9.22 +void SimWorld::add_particle(Particle *p) 9.23 +{ 9.24 + part.push_back(p); 9.25 +} 9.26 + 9.27 +bool SimWorld::collision(const Ray &ray, float rad, Collision *col) const 9.28 +{ 9.29 + bool found = false; 9.30 + 9.31 + Vector2 min = bbmin + Vector2(rad, rad); 9.32 + Vector2 max = bbmax - Vector2(rad, rad); 9.33 + 9.34 + // collision with the boundaries 9.35 + Vector3 npos = ray.origin + ray.dir; 9.36 + 9.37 + Vector3 col_pos, col_norm; 9.38 + float d, col_depth = 0; 9.39 + 9.40 + if((d = min.x - npos.x) > col_depth) { 9.41 + col->pos = npos; 9.42 + col->pos.x = min.x; 9.43 + col->normal = Vector3(1, 0, 0); 9.44 + col_depth = d; 9.45 + found = true; 9.46 + } 9.47 + if((d = min.y - npos.z) > col_depth) { 9.48 + col->pos = npos; 9.49 + col->pos.z = min.y; 9.50 + col->normal = Vector3(0, 0, 1); 9.51 + col_depth = d; 9.52 + found = true; 9.53 + } 9.54 + if((d = npos.x - max.x) > col_depth) { 9.55 + col->pos = npos; 9.56 + col->pos.x = max.x; 9.57 + col->normal = Vector3(-1, 0, 0); 9.58 + col_depth = d; 9.59 + found = true; 9.60 + } 9.61 + if((d = npos.z - max.y) > col_depth) { 9.62 + col->pos = npos; 9.63 + col->pos.z = max.y; 9.64 + col->normal = Vector3(0, 0, -1); 9.65 + col_depth = d; 9.66 + found = true; 9.67 + } 9.68 + 9.69 + return found; 9.70 +} 9.71 + 9.72 +void SimWorld::step(float dt) 9.73 +{ 9.74 + for(size_t i=0; i<part.size(); i++) { 9.75 + // add gravity 9.76 + part[i]->add_force(grav * part[i]->get_mass()); 9.77 + 9.78 + part[i]->step(this, dt); 9.79 + 9.80 + // handle collisions with other particles 9.81 + for(size_t j=0; j<part.size(); j++) { 9.82 + Collision col; 9.83 + if(i != j && part[i]->collision(part[j], &col)) { 9.84 + Vector3 rel_vel = part[j]->get_velocity() - part[i]->get_velocity(); 9.85 + 9.86 + float kn = 1.0 / part[i]->get_mass() + 1.0 / part[j]->get_mass(); 9.87 + float imp = dot_product(rel_vel, col.normal) * (col.elast + 1) / kn; 9.88 + 9.89 + if(imp < 0.0) imp = 0.0; 9.90 + 9.91 + Vector3 v = part[i]->get_position() - part[j]->get_position(); 9.92 + float dist_sq = v.length_sq(); 9.93 + float pen_depth_sq = part[i]->get_radius() + part[j]->get_radius() - dist_sq; 9.94 + if(pen_depth_sq < 0.0) pen_depth_sq = 0.0; 9.95 + 9.96 + part[i]->add_force(col.normal * imp * (1.0 / dt * pen_depth_sq + 1)); 9.97 + } 9.98 + } 9.99 + } 9.100 +} 9.101 + 9.102 +void SimWorld::draw_particles() const 9.103 +{ 9.104 + for(size_t i=0; i<part.size(); i++) { 9.105 + part[i]->draw(); 9.106 + } 9.107 +}
10.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 10.2 +++ b/src/simworld.h Mon Feb 11 19:40:36 2013 +0200 10.3 @@ -0,0 +1,39 @@ 10.4 +#ifndef SIMWORLD_H_ 10.5 +#define SIMWORLD_H_ 10.6 + 10.7 +#include <vector> 10.8 +#include "particle.h" 10.9 +#include "vmath/vmath.h" 10.10 + 10.11 +struct Collision { 10.12 + Vector3 pos; 10.13 + Vector3 normal; 10.14 + float elast; 10.15 +}; 10.16 + 10.17 +class SimWorld { 10.18 +private: 10.19 + std::vector<Object*> objects; 10.20 + std::vector<Particle*> part; 10.21 + Vector3 grav; 10.22 + float damping; 10.23 + 10.24 +public: 10.25 + SimWorld(); 10.26 + 10.27 + void set_bounds(float xmin, float xmax, float ymin, float ymax); 10.28 + 10.29 + void set_gravity(const Vector3 &f); 10.30 + 10.31 + void add_particle(Particle *p); 10.32 + 10.33 + bool collision(const Ray &ray, float rad, Collision *col) const; 10.34 + 10.35 + void step(float dt); 10.36 + 10.37 + void draw_particles() const; 10.38 + 10.39 + friend class Particle; 10.40 +}; 10.41 + 10.42 +#endif // SIMWORLD_H_