Collision Detection with (and other questions about) ofxBox2d

lol. If it is of any comfort for you, I have narcolepsy (to a quite severe degree)

Ah, sorry to hear that. You do seem to be very productive in spite of it, as evidenced by your swiftness and thoroughness in helping others, so you’re doing something right.

OK, I’ve taken your code and set it to use the Kinect depth image instead of the video loader and here is the result.

Notes:

  1. It’s working! The circle no longer jumps when a hand goes above the head. There’s some jitteriness due to the kinect image being less stable than the test videos you were using, but the yellow circle stays in the same general area unless the shape of the detected contours changes drastically. Within that general area it still jumps around a bit… should I add another layer of averaging/lo-pass filter or is that what Smoothing Factor is supposed to do?

  2. Unless I make the Smoothing Factor 1, the size of the contour is continually shrinking and growing. (Making it 0 will freeze it in whatever size/shape it was up until it was 0- often a smaller shape)

  3. Hair is a problem for the kinect. Taking the beanie off throws it into chaos (particularly when Smoothing Factor is not 1). Guess I need to get a haircut before my performance.

Just in case, here’s the code of the video I linked.

#pragma once
#include "ofMain.h"
#include "ofxBox2d.h"
#include "ofxGui.h"
#include "ofxKinect.h"
#include "ofxCv.h"



class ofApp : public ofBaseApp {

public:
	void setup();
	void update();
	void draw();
	void exit();

	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);


	ofPolyline smoothPoly(const ofPolyline& polyline);
	void analyze(const ofPolyline& curFrame);
	void updateKinect(bool isDepthCam);
	void updateContours(bool isDepthCam);
	void drawKinect(bool isDepthCam);

	//void oscSendRoll

	float clip(float n, float lower, float upper);

	//ofPolyline m_curFrame;
	ofPolyline m_prevFrame;
	ofPolyline m_resampleSmoothed;


	ofxPanel m_panel;
	ofParameter<int> m_threshold;
	ofParameter<int> m_minArea;
	ofParameter<int> m_maxArea;
	ofParameter<float> m_nearClip;
	ofParameter<float> m_farClip;
	ofParameter<float> m_screenScaler;
	ofParameter<int> m_polylinePointCount;
	ofParameter<int> m_smoothingSize;
	ofParameter<float> m_smoothingShape;
	ofParameter<float> smoothingFactor = { "Smoothing Factor", 0.25, 0.0, 1.0 };
	ofParameter<bool> bDrawSmoothed = { "Draw Smoothed", true };
	//int m_polylinePointCount = 104;
	//ofVideoPlayer m_kinect;
	//ofVideoGrabber kinect;
	ofxKinect m_kinect;

	ofImage m_grayImage;
	ofxCv::ContourFinder m_contour;
	int angle;
	int m_camWidth;
	int m_camHeight;

	std::deque<ofPolyline> m_polys;

	ofxBox2d m_box2d;
	vector<shared_ptr<ofxBox2dCircle>> m_circles;

	ofPolyline m_previousPoly;

	int m_size = 2;
	//int width, height;

	float m_screenWidth = 20; //*should* set this via oF but setting it manually for now 


	int movLoaded = 0;
	//void loadMov(int movNum);
};
#include "ofApp.h"

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


	m_panel.setup("", "", 20, 50);
	m_panel.add(m_threshold.set("threshold", 15, 0, 100));
	m_panel.add(m_minArea.set("minArea", 13, 0, 200));
	m_panel.add(m_maxArea.set("maxArea", 199, 0, 200));
	m_panel.add(m_nearClip.set("near clip", 885.0, 500.0, 4000.0));
	m_panel.add(m_farClip.set("far clip", 1045.0, 1000.0, 10000.0));
	m_panel.add(m_screenScaler.set("screen scale", 3.02, 0.5, 4.0));
	m_panel.add(m_polylinePointCount.set("polyline point count", 250, 0, 650));
	m_panel.add(m_smoothingSize.set("smoothing size", 2, 0, 30));
	m_panel.add(m_smoothingShape.set("smoothing shape", 0.0, 0.0, 1.0));
	m_panel.add(bDrawSmoothed);
	m_panel.add(smoothingFactor);

	//loadMov(1);

	m_box2d.init();
	m_box2d.setGravity(0, 30);
	m_box2d.createGround(0.0f, m_kinect.getHeight(), m_kinect.getWidth(), m_kinect.getHeight());
	m_box2d.createBounds(ofRectangle(0, 0, m_kinect.getWidth(), m_kinect.getHeight()));
	m_box2d.enableEvents();

	//	ofAddListener(m_box2d.contactStartEvents, this, &ofApp::contactStart);
	//	ofAddListener(m_box2d.contactEndEvents, this, &ofApp::contactEnd);

	m_polys.resize(m_polylinePointCount);
	/*for (int i = 0; i < m_polys.size(); i++)
	{
		for (int j = 0; j < m_polylinePointCount; j++)
		{
			m_polys[i].addVertex(0, 0, 0);
		}
		//cout << m_polys[i].size() << endl;
	}*/
	m_kinect.setRegistration(true);
	m_kinect.init();
	m_kinect.open();//necessary?

	m_grayImage.allocate(m_kinect.getWidth(), m_kinect.getHeight(), OF_IMAGE_GRAYSCALE);
}

	
//void ofApp::loadMov(int movNum) {
//	if (movNum >= 0 && movNum < 7) {
//		string mov = "sil.mov";
//		if (m_kinect.load(mov)) {
//			cout << "loaded : " << mov << endl;
//			cout << "movNum : " << movNum << endl;
//			movLoaded = movNum;
//			m_kinect.setLoopState(OF_LOOP_NORMAL);
//			m_kinect.play();
//		}
//	}
//}

//--------------------------------------------------------------
void ofApp::update() {
	m_box2d.update();
	updateKinect(true);
	updateContours(true);
}

//--------------------------------------------------------------
void ofApp::draw() {
	ofPushMatrix();
	ofScale(m_size, m_size);
	ofSetRectMode(OF_RECTMODE_CORNER);
	ofSetColor(255);

	drawKinect(true);

	for (ofPolyline const& p : m_contour.getPolylines()) //THE PROBLEM...
	{
		ofSetLineWidth(5);
		ofSetColor(ofColor(255, 0, 0));
		ofNoFill();


		//			ofSetLineWidth(1);
		//			ofSetColor(0, 0, 0);
		//			ofFill();
		ofPolyline s = p.getSmoothed(m_smoothingSize, m_smoothingShape);
		//            if(bUseAnalyze){
		//                ofPolyline smoothedPoly = analyze(s);



		if (bDrawSmoothed) {
			analyze(s);
			m_resampleSmoothed.draw();
			if (m_resampleSmoothed.size()) {
				ofSetColor(ofColor::yellow);
				ofDrawCircle(m_resampleSmoothed.getVertices()[0], 5);
			}

		}
		else {
			p.draw();
			if (p.size()) {
				ofSetColor(ofColor::yellow);
				ofDrawCircle(p.getVertices()[0], 5);
			}
		}

		//                createContourEdge(smoothedPoly);
		//            }else{
		//                ofPolyline smoothedPoly = smoothPoly(s);
		//                createContourEdge(smoothedPoly);
		//            }



	}
	//m_contourEdge->resize(m_size);
	//m_contourEdge->draw(); //...IS IN HERE.

	ofSetColor(255);
	for (auto circle : m_circles)
	{
		circle->draw();
	}
	//panel.setSize(panel.getWidth() / size, panel.getHeight() / size);
	//panel.setPosition(panel.getPosition().x / size, panel.getPosition().y / size);
	ofPopMatrix();
	m_panel.draw();

	ofDrawBitmapStringHighlight("FPS: " + ofToString(ofGetFrameRate()), 20, 20);
}

void ofApp::exit() {
	//	m_kinect.setCameraTiltAngle(15.0);
	m_kinect.close();
}

float ofApp::clip(float n, float lower, float upper) {
	return std::max(lower, std::min(n, upper));
}


void ofApp::drawKinect(bool isDepthCam)
{
	if (isDepthCam)
	{
		m_kinect.drawDepth(0, 0);
		return;
	}

	m_kinect.draw(0, 0);
}

void ofApp::updateKinect(bool isDepthCam)
{
	if (isDepthCam)
	{
		m_kinect.setDepthClipping(m_nearClip, m_farClip);
	}
	m_kinect.update();
}

void ofApp::updateContours(bool isDepthCam)
{
	if (!m_kinect.isFrameNew())
	{
		return;
	}
	m_contour.setMinAreaRadius(m_minArea);
	m_contour.setMaxAreaRadius(m_maxArea);
	if (isDepthCam)
	{
		m_grayImage.setFromPixels(m_kinect.getDepthPixels());
		m_contour.findContours(m_grayImage);
		return;
	}
	//m_contour.setTargetColor(m_trackingColor);
	m_contour.setThreshold(m_threshold);
	m_contour.findContours(m_kinect);
}

ofPolyline ofApp::smoothPoly(const ofPolyline& _polyline)
{
	auto polyline = _polyline.getResampledByCount(m_polylinePointCount /* + 1*/);
	//cout << polyline.size() << endl;
	glm::vec3 first_vert = { 0,0,0 };
	if (m_polys.size() > 0 && m_polys[0].size() > 0)
	{
		first_vert = m_polys[0].getVertices()[0];
	}
	size_t first_index = 0;
	float min_dist = std::numeric_limits<float>::max();
	for (size_t i = 0; i < polyline.size(); ++i)
	{
		auto d = glm::distance2(polyline[i], first_vert);
		if (d < min_dist)
		{
			min_dist = d;
			first_index = i;
		}
	}

	if (first_index > 0)
	{
		vector<glm::vec3> rearrangedVerts;
		rearrangedVerts.insert(rearrangedVerts.begin(), polyline.begin() + first_index, polyline.end());
		rearrangedVerts.insert(rearrangedVerts.end(), polyline.begin(), polyline.begin() + first_index);
		ofPolyline rearrangedPoly;
		rearrangedPoly.addVertices(rearrangedVerts);
		m_polys.push_front(rearrangedPoly);
	}
	else
	{
		m_polys.push_front(polyline);
	}
	m_polys.pop_back();

	ofPolyline averaged;
	//cout << m_polys.size() << endl;
	for (size_t i = 0; i < m_polylinePointCount; ++i)
	{
		glm::vec3 avg;
		for (size_t j = 0; j < m_polys.size(); ++j)
		{
			//cout << m_polys[j].size() << endl;
			//cout << m_polys[j][i] << endl;
			if (i < m_polys[j].size())
			{
				avg += m_polys[j][i];
			}
			//cout << "vertex[" << j << "][" << i << "] added" << endl;
		}
		avg /= m_polys.size();
		averaged.addVertex(avg);
	}
	return averaged;
}


void ofApp::analyze(const ofPolyline& curFrame) {


	//	ofPolyline line = curFrame;
	ofPolyline resampled = curFrame.getResampledByCount(m_polylinePointCount);

	// A++ if you fix getResampledByCount end points !
	//

//
//
	while (resampled.size() < m_polylinePointCount) {
		resampled.getVertices().push_back(resampled[resampled.size() - 1]);
	}
	//
	if (m_prevFrame.size() > 0) {
		//        cout << ",,,,,\n";
				// if you want to make this faster
				// you will get an A +
				// this is pretty slow :(
				// but it works.

		int smallestStart = -1;
		float smallestAvgLen = 10000000;

		float fCount = m_polylinePointCount.get();
		auto p_count = m_polylinePointCount.get();
		for (int i = 0; i < p_count; i++) {

			float avgLen = 0;
			for (int j = 0; j < p_count; j++) {
				avgLen += glm::distance2(resampled[(j + i) % p_count], m_prevFrame[j]) / fCount;
				//
			}
			if (avgLen < smallestAvgLen) {
				smallestAvgLen = avgLen;
				smallestStart = i;
			}

		}

		ofPolyline temp;
		for (int i = 0; i < p_count; i++) {
			temp.addVertex(resampled[(i + smallestStart) % p_count]);
		}
		resampled = temp;
	}
	m_prevFrame = resampled;
	//	ofPolyline tempT = resampled.getResampledByCount(100);
	//
	//	while (tempT.size() < 100) {
	//		tempT.getVertices().push_back(tempT[tempT.size() - 1]);
	//	}

	ofPolyline& tempT = resampled;
	if (m_resampleSmoothed.size() == 0 || ofIsFloatEqual(smoothingFactor.get(), 1.0f)) {
		m_resampleSmoothed = tempT;
	}
	else {
		for (int i = 0; i < tempT.size(); i++) {
			m_resampleSmoothed[i] = (1.0 - smoothingFactor) * m_resampleSmoothed[i] + (smoothingFactor.get() * tempT[i]);
		}
	}

	//if you want this function to return something it should be m_resampleSmoothed rather than resampled

	//	return resampled;

}

//--------------------------------------------------------------
void ofApp::keyPressed(int key) {
	switch (key)
	{
	case 'c':
		m_circles.clear();
		break;
	case 'd':
		break;
	}
}

//--------------------------------------------------------------
void ofApp::keyReleased(int key) {
	//if (key == OF_KEY_LEFT) {
	//	loadMov(((movLoaded + 1) % 7));
	//}
	//else if (key == OF_KEY_RIGHT) {
	//	loadMov(((movLoaded - 1 + 7) % 7));
	//}
}

//--------------------------------------------------------------
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) {
	/*auto circle = make_shared <ofxBox2dCircle>();
	circle->setPhysics(3.0, 0.5, 0.1);
	circle->setup(m_box2d.getWorld(), x / m_size, y / m_size, 32);
	m_circles.push_back(circle);*/
}

//--------------------------------------------------------------
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) {

}

Thanks. It sucks, and I am only able to manage it by taking an absurd amount of pills.

It is very strange how the smoothing behaves. It shouldn’t be doing that. When I tested it it did not do that but it actually smoothed the shape, but it introduces a lag.

What can help with the hair issue is to downscale the grayimage by some factor before passing it to findContours. Then you upscale the results of the contour finder by the same factor so it matches again the original image.
This is a very common practice in computer vision, which helps to reduce noise, and also improves performance. It is funny, that in a lot of cases lower resolution images are better than higher res for CV stuff.

Shrinking and re-upping the scale of the image helps both to get a smoother shape and to fix the weird shrinking/growing glitch- but only if I get a little farther from the camera.

I wanted to keep the high-res image to draw and use the low-res image for the contour edge. Right now I’m using two contourFinders, so that I can do this:

m_shader.begin();
	setShader();
	for (const ofPolyline& p : m_drawContour.getPolylines()) //THE PROBLEM...
	{
		ofBeginShape();
		for (int i = 0; i < p.getVertices().size(); ++i)
		{
			ofVertex(p.getVertices().at(i).x, p.getVertices().at(i).y);
		}
		ofEndShape();
	}
	m_shader.end();

with the high-res one, but this seems to hurt performance. Is there a better way to shade the high-res image? Using the shader with the ofImage just colors the whole screen with the shader.

Also, what’s a good way of using the same code with different types of ofxBox2d shapes, eg. is there a way to have a vector that holds both circles and rectangles? I guess if I want to use the same code on objects of both kinds I can use a template with a variable type.

Hi, sorry for the belated reply.

Yes, that is the slowest way to render stuff into open gl. can you just draw the polylines ? I think that is far more performant.

for (auto& p : m_drawContour.getPolylines()) 
	{
p.draw();
}

if this does not work for you, putting the vertices into an ofMesh is better.

ofMesh mesh;
mesh.setMode(OF_PRIMITIVE_LINE_STRIP);// or which ever other mesh mode.
	for (const ofPolyline& p : m_drawContour.getPolylines()) //THE PROBLEM...
	{
               mesh.addVertices(p.getVertices());
	}

m_shader.begin();
	setShader();
mesh.draw();
	m_shader.end();

all shapes seem to inherit from ofxBox2dBaseShape so you can store all in a vector<ofxBox2dBaseShape*> or better vector<unique_ptr<ofxBox2dBaseShape>>.

vector<unique_ptr<ofxBox2dBaseShape>> shapes;

shapes.emplace_back(make_unique<ofxBox2dCircle>());
shapes.emplace_back(make_unique<ofxBox2dRect>());

In order to access these and the functions that belong to each different type you will need to cast based on the type.


    for(auto& s: shapes){
        auto t = typeid(*(s.get())).name();
        
        std::cout << t << "\n";
                
        if (typeid(ofxBox2dCircle).name() == t){
        ofxBox2dCircle* circle =  static_cast<ofxBox2dCircle*>(s.get());

        }else  if (typeid(ofxBox2dRect).name() == t){
                        ofxBox2dRect* rect =  static_cast<ofxBox2dRect*>(s.get());
        }

Hi, sorry for the belated reply.

Hey, no worries.

I’ve tried both your methods for the polyline, but it seems they don’t allow me to fill the shape, even if I use

ofFill();
mesh.draw(OF_MESH_FILL);

all shapes seem to inherit from ofxBox2dBaseShape so you can store all in a vector<ofxBox2dBaseShape*> or better vector<unique_ptr<ofxBox2dBaseShape>>.

Oh yeah, this works fine, thanks. I set the shape-specific parameters before assigning the respective shape to a base shape pointer and it works fine:

std::shared_ptr<ofxBox2dBaseShape> c;
		
		int shapeType = (ofRandom(100) > 50) ? 1 : 0;
		if (shapeType == 0)
		{
			shared_ptr<ofxBox2dCircle> cr = make_unique<ofxBox2dCircle>();
			cr->setPhysics(1, 0.5, 0.9);
			cr->setup(m_box2d.getWorld(), ofRandom(0, m_kinect.getWidth()), 0, ofRandom(20, 50));
			c = cr;
		}
		else
		{
			shared_ptr<ofxBox2dRect> rc = make_unique<ofxBox2dRect>();
			float w = ofRandom(5, 45);
			float h = ofRandom(5, 215);
			ofRectangle rect = ofRectangle(ofRandom(m_boundSides.get(), m_kinect.getWidth() - m_boundSides.get() * 2), -m_yTranslate, w, h);
			rc->setPhysics(1, 0.5, 0.9);
			rc->setup(m_box2d.getWorld(), rect);
			c = rc;
		}		
c->setData(new ObjectData()); 
//...data assigment
m_circles.emplace_back(c);

Also, I’m looking at the original question of this thread and how to calculate the velocity of shapes moved by the contourEdge and I’m not sure what the syntax is to even get the position of the closest point on the polyline. I tried this…

	if (e.a->GetType() == b2Shape::e_circle || e.a->GetType() == b2Shape::e_polygon)
	{
		float m = e.a->GetBody()->GetMass();
		b2Vec2 v = e.a->GetBody()->GetLinearVelocity();
		v *= m;
		auto p = m_resampleSmoothed.getClosestPoint(e.a->GetBody()->GetTransform());
		e.a->GetBody()->ApplyForce(v, p, true);
	}

but that doesn’t work, of course, because ofPolyline::getClosestPoint() returns ofDefaultVertexType and for ApplyForce() and I need a b2vec2.

I didn’t find the oF documentation page very helpful and not sure where to find examples of how to use it.

I see. It depends on the mesh mode you use.

mesh.setMode(OF_PRIMITIVE_TRIANGLE_STRIP);
//or
mesh.setMode(OF_PRIMITIVE_TRIANGLE_FAN);

should make the shape filled.
Although this will not fill it properly when you have convex shapes for instance. The correct way would be to use an ofPath

ofPath path;
for (const ofPolyline& p : m_drawContour.getPolylines()) //THE PROBLEM...
	{

		path.newSubPath();
		for(	auto& v : p.getVertices()){
			path.lineTo(v);
		}
	}
path.setFilled(true);
path.setFillColor(ofColor::red);//which ever color you want 
path.setStrokeColor(ofColor::blue);//which ever color you want 
path.setStrokeWidth(2);// call this to define the outline width if you want it to get drawn. By default it will not draw the outline
path.draw();

I would think that box2d calculates this by itself if the polyline is transformed into a boundary. I thinkk it was discussed earlier in this thread.

	auto p = m_resampleSmoothed.getClosestPoint(e.a->GetBody()->GetTransform());
b2vec2 pp;
pp.x = p.x;
pp.y = p.y;
		e.a->GetBody()->ApplyForce(v, pp, true);

Although this will not fill it properly when you have convex shapes for instance. The correct way would be to use an ofPath

Hey, that works really well, thanks. Making a separate ofPath for each contour (ie. putting ofPath path in place of path.newSubPath() also prevents lines from being drawn between different contours and doesn’t seem to affect performance significantly. And it’s running faster than with the BeginShape() method.

I would think that box2d calculates this by itself if the polyline is transformed into a boundary. I thinkk it was discussed earlier in this thread.

I see you mentioned it back in #11, but I never tested it because I needed to figure out how to have the same-ish point index each frame (which was a whole thing…). Actually, the line

auto p = m_resampleSmoothed.getClosestPoint(e.a->GetBody()->GetTransform());

itself throws an error (red squiggly under the ‘e’ in ‘e.a’):

no suitable user-defined conversion from "const bTransform" to "const ofDefaultVertexType" exists

Pretty unclear about what all these types are and how they relate to or can be cast to one another…

It will. BeginShape is the worst way of drawing. It is called immediate mode, and each command, say ofAddVertex, makes an openGL call immediatelly, and considering that there is a bottleneck between CPU to GPU it can easily become very slow. Personally, I never use it.

Does the following work?

auto t = e.a->GetBody()->GetPosition();
auto p = m_resampleSmoothed.getClosestPoint({t.x, t.y});

ofDefaultVertexType is a typedef for glm::vec3. It was added when there was the transition from ofVec3f to glm::vec3, in case anyone wanted to keep on using the legacy class.
If you dont know what a type is or waht it does, what I usually do, before searching online, is to right click over the type name and select “go to definition” or something like that, which shows the .h file where that class was defined which most of the times has inline documentation.

Does the following work?

auto t = e.a->GetBody()->GetPosition();
auto p = m_resampleSmoothed.getClosestPoint({t.x, t.y});

That didn’t work, because getClosestPoint requires a vector3, so the code below doesn’t throw any errors because I just added in the z-axis as 0.0.

		if (e.a->GetType() == b2Shape::e_circle || e.a->GetType() == b2Shape::e_polygon)
		{
			float m = e.a->GetBody()->GetMass();
			b2Vec2 v = e.a->GetBody()->GetLinearVelocity();
			v *= m;
			auto t = e.a->GetBody()->GetPosition();
			auto p = m_resampleSmoothed.getClosestPoint({ t.x, t.y, 0.0 });
			e.a->GetBody()->ApplyForce(v, { p.x,p.y }, true);
		}

		if (e.b->GetType() == b2Shape::e_circle || e.b->GetType() == b2Shape::e_polygon)
		{
			float m = e.b->GetBody()->GetMass();
			b2Vec2 v = e.b->GetBody()->GetLinearVelocity();
			v *= m;
			auto t = e.b->GetBody()->GetPosition();
			auto p = m_resampleSmoothed.getClosestPoint({ t.x, t.y, 0.0 });
			e.b->GetBody()->ApplyForce(v, { p.x,p.y }, true);
		}

So the result is that circles seem to be receiving a little bit of force, but it’s kind of subtle/irregular and doesn’t really add that much momentum. On the other hand, rectangles are continuously sent bouncing and spinning off of things with too much force- presumably because the force being applied is relative to the rotation of the rectangle?

I imagine the velocity isn’t being transferred properly to the circles because e.g. in the in the instant I move my arm out from below the shape, its velocity gets trastically reduced… I probably need to make a vector of the previous x-number of positions of each vertice and then paint them out or something.

Well, performance in the morning and I’m nodding off. Won’t make it in time for tomorrow, but I would like to get a handle on this…

my experience with ofxBox2d is not that vast, and I’ve just fully relied on it to make all the maths, so I really dont know why your code is not working.
Notice that the shapes also have a rotation speed, so that might be affecting somehow.

I hope your performance went well, despite not having this ready.
cheeers

Hey @roymacdonald The performance did go well, thanks. Afterwards, I found and fixed a silly bug and the momentum transfer works! Just need to tweak the force/angular force values so objects don’t spin wildly around.

Been pretty busy but thought I should give this topic some closure.

@s_e_p glad to know you got it to work.
I think that you can set box2d elements so these dont spin. cant recall how but I remember having it done in the past