Hey. I just started using openframeworks, mainly because I’d seen other people use it for a particular project I’ve been doing in processing. The project is a slime mold simulation, which is an agent/particle-based program. I’ve seen other projects reach millions of particles, but mine starts having framerate issues when reaching the thousands. It’s important to mention that I’m running this on a really poor setup, but even still, I’d expect the project to run at least a little better.
I’m very new to openframeworks and c++ in general, so I’m looking for some suggestions or ways to optimize my code. I’d also like to know if there are ways to allocate more CPU memory to a program. My code is pasted below, and I’d really appreciate anyone taking the time to give me some advice.
//Main
#include "ofMain.h"
#include "ofApp.h"
//========================================================================
int main( ){
ofSetupOpenGL(400,400,OF_WINDOW); // <-------- setup the GL context
// this kicks off the running of my app
// can be OF_WINDOW or OF_FULLSCREEN
// pass in width and height too:
ofRunApp(new ofApp());
}
// ofApp.cpp
#include "ofApp.h"
//--------------------------------------------------------------
void ofApp::setup(){
ofSetFrameRate(60);
for (int i = 0; i < NUMAGENTS; ++i) {
float x = ofGetWidth()/2;
float y = ofGetHeight()/2;
agents[i].initialize(x, y);
}
map.initialize();
}
//--------------------------------------------------------------
void ofApp::update(){
for (int i = 0; i < NUMAGENTS; ++i) {
agents[i].move(map);
}
map.diffuse();
map.evaporate();
map.updateMap();
std::printf("value: %f\n", ofGetFrameRate());
}
//--------------------------------------------------------------
void ofApp::draw(){
map.render();
}
#pragma once
#include "ofMain.h"
#include "Map.hpp"
#include "Slime.hpp"
#define NUMAGENTS 10000
class ofApp : public ofBaseApp{
public:
void setup();
void update();
void draw();
void keyPressed(int key);
void keyReleased(int key);
void mouseMoved(int x, int y );
void mouseDragged(int x, int y, int button);
void mousePressed(int x, int y, int button);
void mouseReleased(int x, int y, int button);
void mouseEntered(int x, int y);
void mouseExited(int x, int y);
void windowResized(int w, int h);
void dragEvent(ofDragInfo dragInfo);
void gotMessage(ofMessage msg);
Map map;
Slime agents[NUMAGENTS];
};
#include "Map.hpp"
Map::Map(){
}
void Map::initialize() {
diffusionRate = 0.98;
for (int x = 0; x < ofGetWidth(); ++x) {
for (int y = 0; y < ofGetHeight(); ++y) {
grid[x][y] = 0;
buffer[x][y] = 0;
}
}
}
void Map::setVal(int x, int y, float val) {
grid[x][y] += val;
}
float Map::getVal(int x, int y) {
return grid[x][y];
}
void Map::evaporate() {
for (int x = 0; x < ofGetWidth(); ++x) {
for (int y = 0; y < ofGetHeight(); ++y) {
if (grid[x][y] > 1) {
grid[x][y] = grid[x][y]*diffusionRate;
}
}
}
}
void Map::diffuse() {
for (int x = 0; x < ofGetWidth(); ++x) {
for (int y = 0; y < ofGetHeight(); ++y) {
float sum = 0;
float avg = 0;
for (int ix = -1; ix < 2; ++ix) {
for (int iy = -1; iy < 2; ++iy) {
int currentX, currentY;
if (x==ofGetWidth()-1) {
currentX = 0;
} else if (x==0) {
currentX = ofGetWidth()-1;
} else {
currentX = ix+x;
}
if (y==ofGetHeight()-1) {
currentY = 0;
} else if (y==0) {
currentY = ofGetHeight()-1;
} else {
currentY = iy+y;
}
sum += grid[currentX][currentY];
}
}
avg = sum/9;
float diffusedVal = ofLerp(grid[x][y], avg, diffusionRate);
buffer[x][y] = diffusedVal;
}
}
for (int x = 0; x < ofGetWidth(); ++x) {
for (int y = 0; y < ofGetHeight(); ++y) {
grid[x][y] = buffer[x][y];
//buffer[x][y] = 0;
}
}
}
void Map::updateMap(){
map.allocate(ofGetWidth(), ofGetHeight(), OF_IMAGE_COLOR); // RGB
// loop thru and set a random color for each pixel
for(int y = 0; y < map.getHeight(); ++y){
for(int x = 0; x < map.getWidth(); ++x){
float r = grid[x][y];
float g = grid[x][y];
float b = grid[x][y];
if (r > 255) {
r = 255;
}
if (g > 255) {
g = 255;
}
if (b > 255) {
b = 255;
}
ofColor color(r, g, b);
map.setColor(x, y, color);
}
}
// update the ofTexture in image with its ofPixels
map.update();
}
void Map::render(){
map.draw(0.f, 0.f);
}
#include <stdio.h>
#ifndef _MAP
#define _MAP
#include "ofMain.h"
class Map {
public:
void initialize();
void diffuse();
void evaporate();
void setVal(int x, int y, float val);
float getVal(int x, int y);
void updateMap();
void render();
ofImage map;
float grid[400][400];
float buffer[400][400];
float diffusionRate;
Map();
private:
};
#endif
#include "Slime.hpp"
#include "ofApp.h"
#include "Map.hpp"
Slime::Slime(){
}
void Slime::initialize(float x, float y) {
int xP = int(x);
int yP = int(y);
position.set(xP, yP);
angle = ofDegToRad(ofRandom(360));
speed = 1;
DEPOSITVALUE = 170;
maxSpeed = 0.1;
minSpeed = 0.1;
turnSpeed = 45;
sensorOffsetDst = 9;
sensorAngleSpacing = ofDegToRad(22.5);
sensorSize = 1;
}
void Slime::move(Map& map) {
detect(map);
direction = ofVec2f(cosf(angle), sinf(angle));
direction.normalize();
direction.x = direction.x*speed;
direction.y = direction.y*speed;
position.x = position.x + direction.x;
position.y = position.y + direction.y;
wrap();
map.setVal(floor(position.x), floor(position.y), DEPOSITVALUE);
}
float Slime::sense(float sensorAngleOffset, Map& map) {
float sum = 0;
float sensorAngle = angle + sensorAngleOffset;
ofVec2f sensorDir = ofVec2f(cos(sensorAngle), sin(sensorAngle));
sensorDir.x = sensorDir.x*sensorOffsetDst;
sensorDir.y = sensorDir.y*sensorOffsetDst;
ofVec2f sensorCenter = ofVec2f(position.x + sensorDir.x, position.y + sensorDir.y);
for (int offsetX = -sensorSize; offsetX <= sensorSize; ++offsetX) {
for (int offsetY = -sensorSize; offsetY <= sensorSize; ++offsetY) {
ofVec2f pos = ofVec2f(sensorCenter.x + offsetX, sensorCenter.y + offsetY);
pos.x = floor(pos.x);
pos.y = floor(pos.y);
if (pos.x >= 0 && pos.x < ofGetWidth() && pos.y >= 0 && pos.y < ofGetHeight()) {
sum += map.getVal(floor(pos.x), floor(pos.y));
}
}
}
return sum;
}
void Slime::detect(Map& map) {
float forwardWeight = sense(0, map);
float leftWeight = sense(sensorAngleSpacing, map);
float rightWeight = sense(-sensorAngleSpacing, map );
if (forwardWeight > leftWeight && forwardWeight > rightWeight) {
angle += 0;
} else if (forwardWeight < leftWeight && forwardWeight < rightWeight) {
angle += ofDegToRad(turnSpeed);
} else if (rightWeight > leftWeight) {
angle -= ofDegToRad(turnSpeed);
} else if (leftWeight > rightWeight) {
angle += ofDegToRad(turnSpeed);
}
}
void Slime::wrap() {
if(position.x < 0 ){
position.x = ofGetWidth()-1;
} else if(position.x > ofGetWidth()-1){
position.x = 0;
}
if(position.y < 0 ){
position.y = ofGetHeight()-1;
} else if(position.y > ofGetHeight()-1){
position.y = 0;
}
}
void Slime::show() {
ofSetColor(255);
ofDrawCircle(position.x, position.y, 5);
}
#include <stdio.h>
#ifndef _SLIME
#define _SLIME
#include "ofMain.h"
#include "Map.hpp"
class Slime {
public:
void initialize(float x, float y);
void move(Map& map);
void wrap();
void show();
float sense(float sensorAngleOffset, Map& map);
void detect(Map& map);
ofVec2f position;
ofVec2f direction;
float angle;
float speed;
float maxSpeed;
float minSpeed;
float turnSpeed;
float sensorOffsetDst;
float sensorAngleSpacing;
float sensorSize;
float DEPOSITVALUE;
Slime();
private:
};
#endif
Quick Explanation: the TrailMap is a 2d grid that contains a value corresponding to every pixel. As the agents move around the map, they deposit a value onto the pixels that correspond with their position (meaning they increase the 2d grid value by x amount, function setVal () in Map). Each agent has three sensors, one in front, one to its front and right, and one to its front and left. These sensors detect the value of the the TrailMap in that location of the sensor, and then the agent turns toward the highest value and deposits an amount onto the TrailMap. Every frame the TrailMap diffuses(), meaning each on the map is set to the average of the sum of its 8 neighboring values. Then, the TrailMap decays(), meaning each of its values are multiplicatively decreased.
An awesome blog post about the project in case you wanted to read more: [physarum - Sage Jenson]
Thanks