2D Physics System

Heh, thanks. Actually it says “use the cursors” which means the cursor keys. However, since it confused you I’ve updated it to say “cursor keys”.

I’ve never been quite so complimented by any comment on these forums. Prof FizzWizzle is one of my fave games of recent years (all time?). I absolutely love it. Thank you for the motivation.

Thanks for putting the effort in on this :slight_smile: I’ll take a look at Arbiters later today (gotta get through the working day first). The max contact points has been annoying me for a while actually so it looks like it’s time to fix it.

You could use the “CollisionTest” class (or an extension of ) to test out your polygon collision code without relying on Arbiters while I try and work out what to do :slight_smile:

PS - KPolygon? special sort of Polygon?
PPS - with contributions to the source I want to try and be strict about coding standards/javadoc from the very start to avoid any issues that we see in other places (call it a less learnt). I’d don’t mind doing the reformatting and/or javadocing if it’s needed. Apologies if the Java coding standards and javadoc everywhere makes people less likely to contribute.

This stuff is still pretty new but I’m not sure what you’d be looking for in docs/tutorials. As physics go this is about as simple as it gets. Create World. Create Bodies. Add Bodies to the World. Step world.

Is there something specific I could produce to help you out?

@All

Thanks for the feedback everyone. Much appreciated.

I’ll be adding springs as joints next probably and trying to work out how we can have bodies that are pseudo physcial - i.e. moving platforms that are driven from a script, constrained in some way but also work properly with the physics world.

Oh, and working on a totally unrelated space hulk game :wink:

Kev

[quote]'ve never been quite so complimented by any comment on these forums.
[/quote]
Kev, YOU the loosest slots in Vegas. 8)

I’ve updated the Arbiter code so MAX_CONTACTS is used every it needs to be. I’ve upped it to 10 and stuff still seems to work ok :slight_smile:

Kev

Awesome :). If you get time could you please post a full build of it on cokeandcode since I can’t download everything from the SVN repository from this internet computer without clicking every single file from IE.

Thanks,
Keith

PS: let me know when you update the Arbiter file on SVN so I can get it & try it out! Polygons are basically done (i think) except for when 2 vertices cross & there are 4 lines intersecting each other, but I should be able to fix it soon now that 2+ contact points are possible.

How annoying that must be!

Here ya go: http://www.cokeandcode.com/phys2d/

Kev

Woo hoo! it works! Thanks for fixing it!

Besides that vertice problem, polygons collision is working.

I’ve deleted all other shape & collision classes like Box & Circle while testing polygons but now I suppose I only have to bring in Circle since a Box is a polygon.

I had to change a bit of your code though to work with polygons. I chucked out AABox in favour of the shape keeping its own Point centre & max radius ‘circularBound’ which are used to do bounds testing. Also, the shape itself stores its coordinates, so for example when it is rotated, the actual coordinates are changed rather than the body’s rotation field.

Also, the polygon currently stores coords in a java.awt.geom.Point2D.Float array.

Once I get the vertice problem fixed I’ll put up a demo, probably in a week at this rate. What are your thoughts on having a circular bound & having the shape store its own coords? I use a circular bound around a centroid so that when the polygon is rotated, there’s no need to recalculate the bound. Also when its scaled, the bound can just be scaled. The polygon stores its array of corners, as well as a circularBound & a point centroid.

The shapes keeping their own positions makes sense to me, but the original demo relied on them not working that way. Not sure how you’ve fixed it - do all the demos still work? OOI, why did you need to change it?

Don’t remove the Box collision classes. The box specific code is bound to be more efficient than treating a box as a 4 sided polygon - and since boxes/circles is what most people will end up with it’d be best to keep the common path as performant as possible.

As to circle bounds, I had them originally, changed out when doing the quad space work since it made that much easier. I don’t recompute bounds either, just make them big enough for any rotation - pretty similar to circles in that case. Don’t really mind which as long as everything continues to work correctly.

Just need the code to integrate with whats there without breaking any of the existing functionality or performance really. As long as it does that I’m happy.

Can’t wait to see the working demo!

EDIT:

Can we flip them out of this, I’d rather not have any binding to AWT in there if possible.

Kev

I love the rope bridge thing. I played with it a bit trying to get a trampoline effect. It works pretty well but it’s obvious the bridge doesn’t stretch at all. You think you could add a spot that can stretch some? I’d love to be able to launch the ball up like a trampoline.

Need spring joints for that I think - or at least semi elastic type things. It’s a good idea :slight_smile:

Kev

Here’s how far I’ve got with polygons: http://www.freewebs.com/commanderkeith/PolygonPhysics.jnlp

There are a couple of bugs that beat me, but maybe someone clever could look into it :):
1) convex (>180 degree) vertices lead to overlap (see demo 6)
2) points in a line cause ‘bumps’ as something slides down an otherwise straight line (see demo 2)
3) the stacking boxes (demo 7) doesn’t work properly without a high framerate - it’s as if the penetration amount is being taken account of - the boxes overlap each other for frame rates of less than 20FPS.

In the demo you can see the size & angle of the forces being applied - they’re all at vertices of polygons. Also, you can zoom in using PG_DOWN or the mouse wheel to get a good look at what’s happening. Dragging the mouse moves the view.

The source is at http://www.freewebs.com/commanderkeith/PolygonPhysics.zip.

This is basically how it works:
// find point a on polyA that’s contained in polyB.
// check if the line from a to a-1 intersects a line on polyB.
// if it does, that polyB line is the ‘incident face’ - ie the one that the ‘normal force’ will be applied at 90 degrees.
// if it doesn’t, do same with a & a+1. if still no instersection, just ignore point a.
// repeat for polyB on polyA.
// penetration (sep) is just the distance from the contained point a to the incident face (just perpendicular dist).

Kev & I would love some help with this project, and to be (geekily) honest its quite an interesting physics & geometry problem! I don’t want to bore everyone, but here’s the (long but complete) code that works out the forces between 2 polygons, in PolygonPhysics/src - net.phys2d.raw.collide.KPolygonKPolygonCollider:
(note that I changed the array reference [ b ] to [b_] to stop bold text appearing).

public int collide(Contact[] contacts, Body bodyA, Body bodyB) {
	
	float x1 = bodyA.getPosition().getX();
	float y1 = bodyA.getPosition().getY();
	float x2 = bodyB.getPosition().getX();
	float y2 = bodyB.getPosition().getY();
	
	boolean touches = bodyA.getShape().boundsIntersect(bodyB.getShape());
	if (!touches) {
		return 0;
	}
	
	KPolygon polyA = (KPolygon) bodyA.getShape();
	KPolygon polyB = (KPolygon) bodyB.getShape();
	polyA.name = "A";
	polyB.name = "B";
	
	int count = 0;
	boolean first = true;
	Point2D.Float[] pointsA = polyA.getPoints();
	Point2D.Float[] pointsB = polyB.getPoints();
	
	OuterLoopA:
	for (int a = 0; a < pointsA.length; a++){
		if (polyB.contains(pointsA[a])){
			//System.out.println("polyB contains a");
			int aPlus = (a+1 == pointsA.length ? 0 : a+1);
			Vector2f point = null;
			Vector2f normal = null;
			float separation = Float.MAX_VALUE;
			for (int b = 0; b < pointsB.length; b++){
				int bPlus = (b+1 == pointsB.length ? 0 : b+1);
				if (KPolygon.linesIntersect(pointsA[a].x,pointsA[a].y,pointsA[aPlus].x,pointsA[aPlus].y,
											pointsB[b_].x,pointsB[b_].y,pointsB[bPlus].x,pointsB[bPlus].y)){
					float newSeparation = (float)Line2D.ptLineDist(pointsB[b_].x, pointsB[b_].y, pointsB[bPlus].x, pointsB[bPlus].y, pointsA[a].x, pointsA[a].y);
					if (separation < newSeparation){
						continue;
					}
					separation = newSeparation;
					Vector2f face = new Vector2f(pointsB[b_].x-pointsB[bPlus].x, pointsB[b_].y-pointsB[bPlus].y);
					normal = new Vector2f(face.getY(), -face.getX());
					//System.out.println("polyB contains point a, a-aPlus intersects polyB");
					normal.normalise();
					point = new Vector2f(pointsA[a].x, pointsA[a].y);
				}
				int aMinus = (a-1 == -1 ? pointsA.length-1 : a-1);
				
				if (KPolygon.linesIntersect(pointsA[a].x,pointsA[a].y,pointsA[aMinus].x,pointsA[aMinus].y,
											pointsB[b_].x,pointsB[b_].y,pointsB[bPlus].x,pointsB[bPlus].y)){
					float newSeparation = (float)Line2D.ptLineDist(pointsB[b_].x, pointsB[b_].y, pointsB[bPlus].x, pointsB[bPlus].y, pointsA[a].x, pointsA[a].y);
					if (separation < newSeparation){
						continue;
					}
					separation = newSeparation;
					Vector2f face = new Vector2f(pointsB[b_].x-pointsB[bPlus].x, pointsB[b_].y-pointsB[bPlus].y);
					normal = new Vector2f(face.getY(), -face.getX());
					//System.out.println("polyB contains point a, a-aMinus intersects polyB");
					normal.normalise();
					point = new Vector2f(pointsA[a].x, pointsA[a].y);
				}
			}
			if (point != null){
				//System.out.println("added contact no. "+count);
				contacts[count].setSeparation(separation);
				contacts[count].setPosition(point);
				contacts[count].setNormal(normal);
				count++;
				if (count >= Arbiter.MAX_POINTS-1){
					return count;
				}
				continue OuterLoopA;
			}
		}
	}
	OuterLoopB:
	for (int b = 0; b < pointsB.length; b++){
		if (polyA.contains(pointsB[b_])){
			//System.out.println("polyA contains b");
			int bPlus = (b+1 == pointsB.length ? 0 : b+1);
			Vector2f point = null;
			Vector2f normal = null;
			float separation = Float.MAX_VALUE;
			for (int a = 0; a < pointsA.length; a++){
				int aPlus = (a+1 == pointsA.length ? 0 : a+1);
				if (KPolygon.linesIntersect(pointsB[b_].x,pointsB[b_].y,pointsB[bPlus].x,pointsB[bPlus].y,
											pointsA[a].x,pointsA[a].y,pointsA[aPlus].x,pointsA[aPlus].y)){
					float newSeparation = (float)Line2D.ptLineDist(pointsA[a].x, pointsA[a].y, pointsA[aPlus].x, pointsA[aPlus].y, pointsB[b_].x, pointsB[b_].y);
					if (separation < newSeparation){
						continue;
					}
					separation = newSeparation;
					Vector2f face = new Vector2f(pointsA[a].x-pointsA[aPlus].x, pointsA[a].y-pointsA[aPlus].y);
					normal = new Vector2f(-face.getY(), face.getX());
					//System.out.println("polyA contains point b, b-bPlus intersects polyA");
					normal.normalise();
					point = new Vector2f(pointsB[b_].x, pointsB[b_].y);
				}
				int bMinus = (b-1 == -1 ? pointsB.length-1 : b-1);
				
				if (KPolygon.linesIntersect(pointsB[b_].x,pointsB[b_].y,pointsB[bMinus].x,pointsB[bMinus].y,
											pointsA[a].x,pointsA[a].y,pointsA[aPlus].x,pointsA[aPlus].y)){
					float newSeparation = (float)Line2D.ptLineDist(pointsA[a].x, pointsA[a].y, pointsA[aPlus].x, pointsA[aPlus].y, pointsB[b_].x, pointsB[b_].y);
					if (separation < newSeparation){
						continue;
					}
					separation = newSeparation;
					Vector2f face = new Vector2f(pointsA[a].x-pointsA[aPlus].x, pointsA[a].y-pointsA[aPlus].y);
					normal = new Vector2f(-face.getY(), face.getX());
					//System.out.println("polyA contains point b, b-bMinus intersects polyA");
					normal.normalise();
					point = new Vector2f(pointsB[b_].x, pointsB[b_].y);
				}
			}
			if (point != null){
				//System.out.println("added contact no. "+count);
				contacts[count].setSeparation(separation);
				contacts[count].setPosition(point);
				contacts[count].setNormal(normal);
				count++;
				if (count >= Arbiter.MAX_POINTS-1){
					return count;
				}
				continue OuterLoopB;
			}
		}
	}
	return count;
}

Cheers,
Keith

It probably won’t solve all your problems, but I would recommend changing your collision detection routines from looking for points inside polygons ( O(n*n) unless you’re doing something really clever ) to looking for intersections between line segments ( can be done in O(n log n ) ).
As well as being more scalable, it will catch collisions that looking for point inclusion won’t, particularly with fast-moving or thin bodies.

Once you’ve identified an intersection, you need to determine which is the incident face, and which is the penetrating vertex. This can be a bit tricky, but you can generally get away with just choosing the solution that results in the smallest corrective impulse.

Good idea. Originally I did use intersections but the trouble was that there were 2 contact points to apply forces to for every overlap rather than just one inner point. Getting the force angles & sizes right for 2 force-points was too tricky. Also the code I made for that method was about 3 times longer than what I have here - this (& the fact it didn’t work properly anyway) made me wonder whether it would actually be more efficient.

I actually hadn’t thought about thin bodies at all :stuck_out_tongue: - the inner point method with a slow frame rate or fast object will completely miss collisions. But this could be solved by updating the physics multiple times between redraws. Since the actual physics calculations are pretty fast (its only drawing that can take ages) that problem should be fixable for anybody who runs into it.

Thanks for the pointers,
Keith

EDIT: come to think of it, fast-moving bodies/slow frame rate/ thin bodies problem will still be present in the intersection method because of the smallest-impulse way of computing the angle of the force, will it not? Both probably need to be solved by doing small time updates.

Not necessarily, it all depends on how you determine which is the incident face and which is the offending vertex.
Choosing the smallest impulse solution should probably be the last resort after you’ve exhausted looking at the two body’s relative velocities and so on.

Very cool physics engine, I like especially the stacking demo part.

Did somebody consider doing a bridgebuilder type demo-game with it? Seems the main part (physics) is already ready :slight_smile:

Kev’s code is certainly ready to do stuff like that, give it a try!

I can’t wait to use this in a game either, trouble is that i need polygons and i’ve got to iron out the bugs…

http://www.freewebs.com/commanderkeith/screenshot.png

This is my progress on polygons, its working much better now that there’s no overlapping. To solve that I just had to use a negative ‘separation’ value… grrrrrrr >:(

This has mitigated some of the troubles, but as you’ll find in demo 9 with the stacking triangles, there are still massive problems.

WebStart: www.freewebs.com/commanderkeith/PolygonPhysics.jnlp

Source: www.freewebs.com/commanderkeith/PolygonPhysicsSource.zip (note that this is a work in progress, I haven’t worked in Kev’s original Boxes etc yet)

Keith

PS: Here are some performance stats to compare the original code with the polygon code. To get these stats I just looped world.step(0.02f) 3000 times using the stacking box demo for both:
Polygon boxes:
seconds elapsed: 12.169297
nanos per world.step(0.02f): 4056432.2

Box boxes:
seconds elapsed: 9.543804
nanos per world.step(0.02f): 3181268.0

So the polygon collision code takes about 28% longer. This can be improved once I profile it.

I looked a bit more into it and I wonder why is everything float and not double? ::slight_smile:

Speed-wise they should be the same.

With these kind of dimensions you don’t need more than 7 significant digits.

And you save half the RAM. (but that’s peanuts)

I used floats because I thought they were faster than doubles, but apparently they aren’t:

http://www.velocityreviews.com/forums/t134954-float-vs-double-speed-on-p4-machine.html, see Roedy Green’s reply.

It could be changed to doubles, but it would be a bit of work removing all of the float casts. Not much point anyway, as Riven said there’s not much of an advantage to have that little bit of extra precision.

Thanks for taking a look at the code. By the way, I’ve changed the polygon collsion code so its working much better. I’ll post it and a new demo in a couple of hours. I figured out what was making the triangles demo go haywire - I was scaling the triangle’s y-coordinates by -1 to flip them over, but that meant that the points weren’t listed in anti-clockwise order which is what the collision code assumes. Anyway, now the triangles bounce off properly and you don’t see the big ones sucking the smaller ones in.

Ok, you’re getting pretty close now :slight_smile: So, how do you want to go about integrating these changes. If you’ve changed alot of stuff unrelated to the collision code then we’ve got two choices:

  1. I integrate stuff, probably modifing your mods on the way to fit in with the existing code

  2. You integrate stuff (I add you to the authors on google code) and you can modify it however is easiest for you.

With 1) it may take me a while (given upcoming commitments). With 2) you’ll end up taking the brunt of the flak as people find problems and need them resolved. Either way it might well make sense to add your the project on google code if you’re so inclined :slight_smile:

Your dollar,

Kev