Applying a one-time position offset to the entire space

Official forum for the Chipmunk2D Physics Library.
Post Reply
pfg2009
Posts: 13
Joined: Fri Aug 13, 2010 12:31 pm
Contact:

Applying a one-time position offset to the entire space

Post by pfg2009 »

I'm working on an infinite scrolling setup, where some dynamically-generated (and destroyed) environment scrolls horizontally in a single direction as the hero fool-heartedly marches on. The simulation is using floats. The problem is that once the environment reaches 100,000s or so on the x-axis, the lost decimal places in floats begin to show up as simulation glitches.

To fix the problem, what I want to do is pick an arbitrary safe threshold along the x-axis (say 1000) and once the environment moves past that point, take everything in the Chipmunk space (area of anywhere from 950 - 1050) and shift it back to 0. I'm happy and comfortable to lightly hack into the Chipmunk source code (6.1.1) to make this happen. To that effect, I've added a new cpSpace function (below) that attempts to shift the entire space by a given offset.

Code: Select all

void cpSpaceOffset(cpSpace* space, cpVect offset) {

	// Shift all bodies (secretly)
	for (int i=0; i<space->bodies->num; i++) {
		cpBody* body = space->bodies->arr[i];

		// Update body position without waking it up if it was sleeping
		cpBodyPlaceBody(body, cpvadd(body->p, offset), body->a, body->v, body->w);

		cpSpaceReindexShapesForBody(space, body);
	}
	
	// Shift all static shapes (manually)
	::pseudo code: loop through all static shapes in the space and for each::
		cpShape* shape = ...;
		
		switch(shape->klass->type){
			case CP_CIRCLE_SHAPE: {
				cpCircleShape *circle = (cpCircleShape *)shape;
				circle->c = cpvadd(circle->c, offset);
				break;
			}
			case CP_SEGMENT_SHAPE: {
				cpSegmentShape *seg = (cpSegmentShape *)shape;
				seg->a = cpvadd(seg->a, offset);
				seg->b = cpvadd(seg->b, offset);
				break;
			}
			case CP_POLY_SHAPE: {
				cpPolyShape *poly = (cpPolyShape *)shape;
				for (int i=0; i<poly->numVerts; i++) {
					poly->verts[i] = cpvadd(poly->verts[i], offset);
				}
				break;
			}
			default: break;
		}
	:: end loop ::
	cpSpaceReindexStatic(space);
	
	// Shift all contact points in a desperate attempt to preserve collisions
	for (int i=0; i<space->arbiters->num; i++) {
		cpArbiter* arb = space->arbiters->arr[i];
		int c = cpArbiterGetCount(arb);
		for (int j=0; j<c; j++) {
			cpContact* ct = &arb->contacts[j];
			ct->p = cpvadd(ct->p, offset);
		}
	}
}
The problem is that it doesn't quite work - objects seem to jump at odd angles when the shift happens. What other internal values do I need to offset to make this shift appear seamless and invisible to the internal Chipmunk simulation logic?
User avatar
slembcke
Site Admin
Posts: 4166
Joined: Tue Aug 14, 2007 7:13 pm
Contact:

Re: Applying a one-time position offset to the entire space

Post by slembcke »

So I did that for an infinite terrain race game prototype a few years ago:
http://howlingmoonsoftware.com/downloads/ByteRacer.zip

The source is mostly in Ruby if you peek inside the application bundle. It's been a really long time since I've looked at it though... Maybe not to useful.

Anyway, what I did was pretty much the same as your basic idea. Every time the player drives over by 1000 units (I think it was probably a half screen width maybe), shift all the bodies in the space over by -1000. I avoided using the built in static body and instead would attach the geometry to manually created static bodies. That way I could easily keep track of all of the static bodies and shift them over just like the rest. No hacks or private APIs required. Loop over the regular bodies using cpSpaceEachBody(), loop the arrays of the static and rogue bodies manually, then call cpSpaceReindexStatic().

Another thing I did was to break the static geometry up into chunks that were 1000 units wide. Each chunk had it's own static body. When the chunk moved far enough offscreen, remove the chunk. Pretty simple

I have no idea what would be causing the jumping. It should work just fine as long as the geometry always lines up correctly. Positions aren't really cached. I might be able to guess if I saw a video though?
Can't sleep... Chipmunks will eat me...
Check out our latest projects! -> http://howlingmoonsoftware.com/wordpress/
pfg2009
Posts: 13
Joined: Fri Aug 13, 2010 12:31 pm
Contact:

Re: Applying a one-time position offset to the entire space

Post by pfg2009 »

Okay, I'll dive in deeper and try to understand the source of the jumpiness. In the meantime, do I need to bother manually updating all the arbiter contact points? I did a little more digging and it seems that the "magic" of contact point persistence in the belly of cpArbiter is cached by their shapes and the contact points seem to be recalculated at every step. Am I correct?
User avatar
slembcke
Site Admin
Posts: 4166
Joined: Tue Aug 14, 2007 7:13 pm
Contact:

Re: Applying a one-time position offset to the entire space

Post by slembcke »

Correct. The contact points are recalculated at the beginning of each frame.
Can't sleep... Chipmunks will eat me...
Check out our latest projects! -> http://howlingmoonsoftware.com/wordpress/
pfg2009
Posts: 13
Joined: Fri Aug 13, 2010 12:31 pm
Contact:

Re: Applying a one-time position offset to the entire space

Post by pfg2009 »

Thanks. My eyes are spinning at this point and I'll dig in more tomorrow, but so far is looks as if:

1) cpSegmentShapes associated with a global static body and shifted using the code below work as expected:

Code: Select all

			cpSegmentShape *seg = (cpSegmentShape *)shape;
			seg->a = cpvadd(seg->a, *offset);
			seg->b = cpvadd(seg->b, *offset);
2) cpCircleShapes associated with a global static body and shifted using the code below work as expected:

Code: Select all

			cpCircleShape *circle = (cpCircleShape *)shape;[/b]
			circle->c = cpvadd(circle->c, *offset);
3) cpPolyShapes associated with a global static body and shifted using the code below DON'T get correctly shifted in the underlying spatial tree and they retain their old spots:

Code: Select all

			cpPolyShape *poly = (cpPolyShape *)shape;
			for (int i=0; i<poly->numVerts; i++) {
				poly->verts[i] = cpvadd(poly->verts[i], *offset);
			}
I use the following code to update all these static shapes and I see my callback called correctly:

Code: Select all

	cpBodyEachShape(space->staticBody, myShapeApplyOffset, myData);
	cpSpaceReindexStatic(space);
However, the spatial tree doesn't seem to register the new positions of poly shapes. Should I be shifting poly shapes differently? Update the underlying planes, perhaps, whatever those are?
pfg2009
Posts: 13
Joined: Fri Aug 13, 2010 12:31 pm
Contact:

Re: Applying a one-time position offset to the entire space

Post by pfg2009 »

Yeah, that was my problem - I wasn't shifting poly shapes correctly. Updated code:

Code: Select all

			cpPolyShape *poly = (cpPolyShape *)shape;
			for (int i=0; i<poly->numVerts; i++) {
				poly->verts[i] = cpvadd(poly->verts[i], *offset);
				poly->planes[i].d = cpvdot(poly->planes[i].n, poly->verts[i]);
			}
Thanks for the help!
User avatar
slembcke
Site Admin
Posts: 4166
Joined: Tue Aug 14, 2007 7:13 pm
Contact:

Re: Applying a one-time position offset to the entire space

Post by slembcke »

Ah right, need to shift the splitting planes as well. That was the sort of thing I was trying to avoid by attaching all the shapes to static bodies that I would move instead. There's not a lot to go wrong when you aren't poking around in the private API. It also makes it really easy to know when to remove old geometry and do it in batches.
Can't sleep... Chipmunks will eat me...
Check out our latest projects! -> http://howlingmoonsoftware.com/wordpress/
pfg2009
Posts: 13
Joined: Fri Aug 13, 2010 12:31 pm
Contact:

Re: Applying a one-time position offset to the entire space

Post by pfg2009 »

You're right and I'm restructuring the code to do exactly that. That said, I enjoy poking around the code underneath the hood to better understand what's going on. Also, I fell in love with using cpArray - super useful utility class. Anyway, thanks for the tips!
Post Reply

Who is online

Users browsing this forum: No registered users and 8 guests