Particle Physics without ofxbox2d

Hi There!

Thought I’d share where i’ve gotten to with creating my own physics and particle system.

It’s mainly for learning / fun but i am very aware of the proverbial bubble i am in so please any thoughts/ constructive criticisms would be much appreciated - just trying to learn!

a couple of issues i have no idea how to solve:

1 - all my particle (in this case balls) once they hit the ‘ground’ stay around 5 pixels under the ground

2 - its really sluggish and is taking up about 17% cpu initially, theres almost certianly something noobish going on here

ofApp.cpp

#include "ofApp.h"

Particle particle;
		
Particle p_mouse;
const int n_particles = 20;
Particle particle_group[n_particles];

//--------------------------------------------------------------
void ofApp::setup(){

	ofVec2f vel(0, 0);
	ofVec2f grav(0, 5);
	ofVec2f acc(20, 0);
	int particle_rad = 10;

	

	for (int i = 0; i < n_particles; i++) {
		particle_group[i].setup(ofRandom(ofGetWidth()), ofRandom(ofGetHeight()), ofRandom(5, 20));
	}
	//int mouse_x;
	//int mouse_y;
	//p_mouse.setup(mouse_x, mouse_y, ofRandom(2, 4));

	//particle.setup(ofGetWidth() / 2, ofGetHeight() / 2, particle_rad);
	//particle.set_bounciness(8);
	//particle.set_p_radius(20);
	//particle.set_fluid_density(0.2);
}

//--------------------------------------------------------------
void ofApp::update() {

	particle.update();
	for (int i = 0; i < n_particles; i++) {
		particle_group[i].update();
	}
	
}

//--------------------------------------------------------------
void ofApp::draw(){
	ofEnableBlendMode(OF_BLENDMODE_ADD);
	particle.draw();
	
	for (int i = 0; i < n_particles; i++) {
		particle_group[i].draw();
	}
}

//--------------------------------------------------------------
void ofApp::keyPressed(int key){

}

//--------------------------------------------------------------
void ofApp::keyReleased(int key){

}

//--------------------------------------------------------------
void ofApp::mouseMoved(int x, int y ){

}

//--------------------------------------------------------------
void ofApp::mouseDragged(int x, int y, int button){

}

//--------------------------------------------------------------
void ofApp::mousePressed(int x, int y, int button){


}

//--------------------------------------------------------------
void ofApp::mouseReleased(int x, int y, int button){

}

//--------------------------------------------------------------
void ofApp::mouseEntered(int x, int y){

}

//--------------------------------------------------------------
void ofApp::mouseExited(int x, int y){

}

//--------------------------------------------------------------
void ofApp::windowResized(int w, int h){

}

//--------------------------------------------------------------
void ofApp::gotMessage(ofMessage msg){

}

//--------------------------------------------------------------
void ofApp::dragEvent(ofDragInfo dragInfo){ 

}

Particle.h

#pragma once
#include "ofMain.h"
class Particle
{
public:

	
	void setup(int pos_x, int pos_y, int p_rad);
	void update();

	void draw();


	ofVec2f position;
	ofVec2f velocity;
	ofVec2f acceleration;
	ofVec2f gravity = ofVec2f(0, 5);
	int p_radius = 10;
	float fluid_density = 0.1;

	float p_bounciness = 0.5;
	int bounces_y;
	int bounces_x;

	ofVec2f reacting_force;
	ofVec2f drag(ofVec2f vector);



	void screen_collision();
	void sum_vectors();
	void set_bounciness(float i);
	void set_fluid_density(float i);
	void set_p_radius(int i);
	void reset_bounces();


	ofVec2f null_vector;

	void debug();

	~Particle();

};


Particle.cpp

#include "Particle.h"

void Particle::setup(int pos_x, int pos_y, int p_rad) {

	position.set(pos_x, pos_y);
	p_radius = p_rad;
	null_vector.set(0, 0);
}


void Particle::update() {
	
	debug();
	screen_collision();
	sum_vectors();
	
	acceleration.set(0, 0);
	
	
}

void Particle::draw() {

	ofCircle(position.x, position.y, p_radius);
}

ofVec2f Particle::drag(ofVec2f vector) {
	ofVec2f drag = fluid_density * vector * (0.1 * p_radius);
	return drag;

}

void Particle::sum_vectors() {

	ofVec2f drag_n = (drag(velocity));
	ofVec2f	acc_n = acceleration + gravity - drag_n;
	ofVec2f vel_n = velocity + acc_n;
	ofVec2f pos_n = position + vel_n;
	
	acceleration.set(acc_n);
	velocity.set(vel_n);
	position.set(pos_n);


}

void Particle::screen_collision() {
	
	if (position.y >= ofGetHeight() - p_radius) { 
		bounces_y += 1;
		position.set(position.x, ofGetHeight() - p_radius);
		float react_vy = (velocity.y * -1) * p_bounciness/bounces_y;
		acceleration.set(acceleration.x, react_vy);//trying to reflect and bounce y axis
		velocity.set(velocity.x, 0);


	}

		
	
}

void Particle::debug() {
	cout << "position = " << position << "\n";
	cout << "velocity = " << velocity << "\n";
	cout << "acceleration = " << acceleration << "\n";
	cout << "gravity = " << gravity << "\n";
	cout << "over screen bottom " << (ofGetHeight() ) - (position.y + p_radius) << "\n";
	cout << "reacting force = " << reacting_force << "\n";
	cout << "screen height = " << ofGetHeight() << "\n";
	cout << "bottom of ball = " << ofGetHeight() + p_radius;
	cout << "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n";
	
}

void Particle::set_bounciness(float i) {

	p_bounciness = i;

}

void Particle::set_fluid_density(float i) {

	fluid_density = i;
}

void Particle::set_p_radius(int i) {

	p_radius = i;

}

void Particle::reset_bounces() {
	bounces_y = 0();
	bounces_x = 0();
}


Particle::~Particle() {

}


Since, you’re literally running everything as public in your Particle class, you could change it to a struct.
The only difference to a class is that in a C++ struct, everything is by default public.

Instead of the setup(...) method, you could provide a parametrized constructor for your Particle. This way you can simply initialize particles directly without the need to call setup().

Particle.h:

Particle(int _x, int _y, int _r);

Particle.cpp:

Particle::Particle(int _x, int _y, int _r)
: position(_x, _y), p_radius(_r) { }

You can also add a custom default constructor that would work much like your setup() now.

You should also get rid of the empty Particle destructor definition - it’s counterproductive - or set it to Particle::~Particle() = default;.

Also in the draw() method why don’t you draw a circle instead of creating a free floating ofCircle like you do? I’d simply ofDrawCircle(position.x, position.y, p_radius); there instead.

Could this be linked to you setting your velocity.y to 0 in screen_collision()? Shouldn’t the velocity vector rather be reversed (e.g. velocity *= -1;)?

1 Like

Hey I liked the code you posted and I didn’t see any major changes could might help. So, nice job! Sometimes printing things to the terminal with a function like Particle::debug() can slow things down a bit, and you could try drawing these values with ofDrawBitmapString(). As diff-arch points out, you may want to separate the Particle class members into public and protected (or private) access. This will also help with how the code is organized and how it “reads” too.

1 Like

@diff-arch thanks so much for going into so much detail, so so helpful, I don’t know about most of things you mention so this gives my learning a great direction

You’re probably right about the velocity.y thing, I’ll get that changed

@TimChi again, thanks so much, invaluable comments for my learning, I will comment out the debug function and see if it changes things.

Thanks again, I will defo be doing more posts like this!