noc05_flocking_simulation

Flocking Simulation

For the midterm, I decided to do an biosystems with creatures. As first step, I did a flocking simulation for the creatures’ movement part. As I assume my creatures live submarine, I use dark blue color for the whole scene.

Apart from flocking, I also add attraction and repulsion forces of mouse. Thus I can interact with those “particles” to make it more interesting.

And I also add an tail for the particles, just to see its trace. I use an little trick of Fbo for the tail. I draw two Fbo on each other, like the feeadback loop in two mirrors. And use transparency of Fbo to make it fade out. The effect was interesting.

(The screen recording makes it a bit stuck, it runs smoothly without screen recording.)

source code

ofApp.hpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#pragma once
#include "ofMain.h"
#include "particle.hpp"
#include "flockingForce.hpp"
#include "ofxGui.h"
class ofApp : public ofBaseApp{
public:
void setup();
void update();
void draw();
ofFbo fbo;
ofFbo feedback;
vector<particle> particleSystem;
ofxPanel panel;
ofParameter<float>separationDistance;
ofParameter<float>alignmentDistance;
ofParameter<float>cohesionDistance;
ofParameter<float>maxSpeed;
ofParameter<float>maxForce;
ofParameter<float>damping;
ofParameter<float>separationStrength;
ofParameter<float>alignmentStrength;
ofParameter<float>cohesionStrength;
};

ofApp.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
#include "ofApp.h"
//--------------------------------------------------------------
void ofApp::setup(){
ofSetVerticalSync(true);
ofEnableSmoothing();
ofBackground(50);
// ofSetColor(255);
ofSeedRandom(0);
//fbo
fbo.begin();
ofClear(0,255);
fbo.end();
fbo.allocate(ofGetWidth(),ofGetHeight(), GL_RGBA32F);
feedback.begin();
ofClear(0,255);
feedback.end();
feedback.allocate(ofGetWidth(),ofGetHeight(), GL_RGBA32F);
for(int i = 0; i < 80; i++){
particle p;
p.setup(ofRandom(-1,1),ofRandom(-1,1));
particleSystem.push_back(p);
}
//control panel
panel.setup();
panel.add(separationDistance.set("separationDistance",25,0,100));
panel.add(alignmentDistance.set("alignmentDistance",50,0,100));
panel.add(cohesionDistance.set("cohesionDistance",50,0,100));
panel.add(maxSpeed.set("maxSpeed",3,0.1,100));
panel.add(maxForce.set("maxForce",0.005,0.001,0.1));
panel.add(damping.set("damping",0.75, 0.01, 1));
panel.add(separationStrength.set("separationStrength", 1.5, 0.0, 5));
panel.add(alignmentStrength.set("alignmentStrength", 1.0, 0.0, 5));
panel.add(cohesionStrength.set("cohesionStrength", 1.0, 0.0 , 5));
}
//--------------------------------------------------------------
void ofApp::update(){
ofSetWindowTitle(ofToString(ofGetFrameRate()));
for (int i = 0; i < particleSystem.size(); i++){
//control panel
particleSystem[i].separation.distance = separationDistance;
particleSystem[i].alignment.distance = alignmentDistance;
particleSystem[i].cohesion.distance = cohesionDistance;
particleSystem[i].maxSpeed = maxSpeed;
particleSystem[i].maxForce = maxForce;
particleSystem[i].damping = damping;
particleSystem[i].separation.strength = separationStrength;
particleSystem[i].alignment.strength = alignmentStrength;
particleSystem[i].cohesion.strength = cohesionStrength;
//particles
particleSystem[i].update();
particleSystem[i].infiniteWalls();
particleSystem[i].addRepulsionForce(ofPoint(mouseX, mouseY), 400, 32000);
particleSystem[i].addAttractionForce(ofPoint(mouseX, mouseY), 700, 26000);
for( int j = 0; j < particleSystem.size(); j++){
// d > 0 makes sure that j != i
if( j != i){
particleSystem[i].addFlockingForce(particleSystem[j]);
}
}
}
}
//--------------------------------------------------------------
void ofApp::draw(){
//planA
ofBackgroundGradient(ofColor::darkBlue, ofColor::black);
// ofBackgroundGradient(ofColor::darkCyan,ofColor::darkSlateBlue);
//planB
// ofBackgroundGradient(ofColor::darkRed, ofColor::black);
fbo.begin();
// ofClear(0);
ofSetColor(200,0,0,10);
ofDrawRectangle(0,0,ofGetWidth(),ofGetHeight());
fbo.draw(0,0);
//fbo.draw(ofGetWidth()-50,0);
ofSetColor(0,0,255);
///////////////Draw here///////////////////
for(int i = 0; i < particleSystem.size(); i++){
//planA
particleSystem[i].draw();
//planB
// particleSystem[i].drawShape();
// particleSystem[i].drawPicture();
}
panel.draw();
//////////////////////////////////////////
fbo.end();
feedback.begin();
// ofSetColor(0,0,255);
feedback.draw(0,0);
feedback.end();
// ofSetColor(255);
fbo.draw(0,0);
}

flockingForce.hpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#pragma once
#include "ofMain.h"
class flockingForce {
public:
ofPoint sum;
ofPoint steer;
float count = 0.0;
float distance;
float strength;
};

particle.hpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#pragma once
#include "ofMain.h"
#include "flockingForce.hpp"
class particle{
public:
void setup(float x, float y);
void update();
void draw();
void drawShape();
void addFlockingForce( particle &p );
void seek(ofPoint _target);
void infiniteWalls();
void drawPicture();
// void drawTail();
void addRepulsionForce(ofPoint _posOfForce, float radius, float strength);
void addAttractionForce(ofPoint _posOfForce, float radius, float strength);
ofPoint steer;
ofPoint pos, vel, acc;
ofPoint target;
float damping;
float scaler;
float maxSpeed;
float maxForce;
flockingForce separation, alignment, cohesion;
float radius;
ofImage img;
};

particle.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
#include "particle.hpp"
//--------------------------------------------------------------
void particle::setup(float x, float y){
// target.set(0,0);
// pos.set(ofRandom(ofGetWidth()), ofRandom(ofGetHeight()));
pos.set(ofGetWidth() / 2, ofGetHeight()/2);
vel.set(x,y);
acc.set(0,0);
damping = 0.9;
radius = 10;
img.load("circle.png");
}
//--------------------------------------------------------------
void particle::update(){
vel += acc;
vel.limit(maxSpeed);
pos += vel;
// dampling is critical. or it will move too fast to stay at target. try 0.99.
vel *= damping;
acc *= 0;;
}
//--------------------------------------------------------------
void particle::draw(){
ofDrawCircle(pos,radius);
ofVec2f velNormal = vel;
velNormal.normalize();
// ofDrawLine(pos.x,pos.y,pos.x+velNormal.x*10,pos.y+velNormal.y*10);
}
//--------------------------------------------------------------
void particle::drawShape(){
// Draw a triangle rotated in the direction of velocity
float theta = ofRadToDeg(atan2(vel.y ,vel.x )) + 90;
//or use this,watch out degree not radians.
// float theta = ofVec2f(1, 0).angle(vel) + 90;
ofSetColor(175);
ofSetLineWidth(0);
ofPushMatrix();
ofTranslate(pos.x,pos.y,0);
ofRotate(theta);
ofBeginShape();
ofVertex(0, -radius*2);
ofVertex(-radius, radius*2);
ofVertex(radius, radius*2);
ofEndShape();
ofPopMatrix();
}
//------------------------------------------------------------
void particle::drawPicture(){
img.update();
img.draw(pos);
}
//------------------------------------------------------------
//void particle::drawTail(){
//
//}
//--------------------------------------------------------------
void particle::addFlockingForce( particle &p ){
ofPoint diff;
ofPoint diffNorm;
float distance;
diff = pos - p.pos;
distance = diff.length();
diffNorm = diff.normalize();
// separation
if( distance > 0 && distance < separation.distance ){
// each particle different separate force
diffNorm /= distance;
separation.sum += diffNorm;
separation.count++;
}
if(separation.count > 0 ){
separation.sum /= separation.count;
if(separation.sum.length() > 0){
separation.sum.normalize();
separation.sum *= maxSpeed;
separation.steer = separation.sum - vel;
separation.steer.limit(maxForce);
acc += separation.steer * separation.strength;
}
}else{
acc += 0;
}
// alignment
if( distance > 0 && distance < alignment.distance ){
alignment.sum += p.vel.getNormalized();
alignment.count++;
}
if(alignment.count > 0 ){
alignment.sum /= alignment.count;
alignment.sum.normalize();
alignment.sum *= maxSpeed;
alignment.steer = alignment.sum - vel;
alignment.steer.limit(maxForce);
acc += alignment.steer * alignment.strength;
}else{
acc += 0;
}
//cohesion
if( distance > 0 && distance < cohesion.distance ){
cohesion.sum += p.pos;
cohesion.count++;
}
if(cohesion.count > 0 ){
cohesion.sum /= cohesion.count;
seek(cohesion.sum);
}else{
acc += 0;
}
}
//--------------------------------------------------------------
void particle::seek(ofPoint _target){
target =_target;
ofPoint direction = target - pos;
direction.normalize();
direction *= maxSpeed;
steer = direction - vel;
steer.limit(maxForce);
acc += steer * cohesion.strength ;
}
//---------------------------------------------------------
void particle::infiniteWalls(){
if (pos.x - radius > ofGetWidth()){
pos.x = - radius;
}
if (pos.x + radius < 0){
pos.x = ofGetWidth()+radius;
}
if (pos.y - radius > ofGetHeight()){
pos.y = - radius;
}
if (pos.y - radius < 0){
pos.y = ofGetHeight() + radius;
}
}
//--------------------------------------------------------------
void particle::addRepulsionForce(ofPoint _posOfForce, float radius, float strength){
ofPoint posOfForce = _posOfForce;
ofPoint direction;
float distance;
ofPoint force;
direction = posOfForce - pos;
distance = direction.length();
direction.normalize();
if((distance > 10) && (distance < radius)){
force = -1/ (distance * distance * distance ) * direction * strength;
acc += force;
}
}
//--------------------------------------------------------------
void particle::addAttractionForce(ofPoint _posOfForce, float radius, float strength){
ofPoint posOfForce = _posOfForce;
ofPoint direction;
float distance;
ofPoint force;
direction = posOfForce - pos;
distance = direction.length();
direction.normalize();
if((distance > 50) && (distance < radius)){
force = 1/ (distance * distance ) * direction * strength;
acc += force;
}
}