Inheriting from cpPivotJoint

Official forum for the Chipmunk2D Physics Library.
supertommy
Posts: 56
Joined: Tue Sep 11, 2007 2:30 pm
Contact:

Inheriting from cpPivotJoint

Post by supertommy »

Today I tried to make a joint that inherits from cpPivotJoint, but I ran into a couple of snags. There isn't really any elegant way to supply my own cpJointClass object, and calling the inherited preStep and applyImpulse functions is not possible.

May I propose something like the attached patch? It just exposes a couple of static functions and slightly modifies the interaction between the new and init functions.

It touches only cpPivotJoint, but if you like it, I can make a new patch which does the same to all the joints.
User avatar
slembcke
Site Admin
Posts: 4166
Joined: Tue Aug 14, 2007 7:13 pm
Contact:

Re: Inheriting from cpPivotJoint

Post by slembcke »

I was thinking about this too. I think the best way to handle this is to split the "class" definitions out into another header. That way you can copy the struct and replace functions that you want without having to make the joint functions visible. Not quite as elegant as real OO, but it could easily be wrapped by any OO or non OO language.

I'll take a look at your patch after work.
Can't sleep... Chipmunks will eat me...
Check out our latest projects! -> http://howlingmoonsoftware.com/wordpress/
supertommy
Posts: 56
Joined: Tue Sep 11, 2007 2:30 pm
Contact:

Re: Inheriting from cpPivotJoint

Post by supertommy »

Yeah, I see what you mean. The joint functions are currently private, my patch makes them public, but we really want them to be protected, to use c++ terms.

One more thing I thought of, by the way. Should we add a destructor function to the cpJointClass? It could be that someone would like to do some cleaning up as their joint dies.
User avatar
slembcke
Site Admin
Posts: 4166
Joined: Tue Aug 14, 2007 7:13 pm
Contact:

Re: Inheriting from cpPivotJoint

Post by slembcke »

Well, I guess this is as good as any time to share my future plans for programable constraints within Chipmunk and how joints fit in with that.

Constraints control how to rigid bodies are supposed to move in relation to one another. Both collision contacts and joints are examples of constraints. Those are the most obvious kinds of constraints as they are largely based on (and actively correct) the position. There are other sorts of constraints that would be very useful as well.
  • Accurate Drag - The "damping" parameter of a cpSpace is not accurate substitute for drag.
  • Accurate Damped Springs - While spring forces are not a constraint, the damping is because it changes based on the relative velocity of the two attached objects. Applying these forces manually once per step is not only annoying, but inaccurate.
  • Motors - A motor supplies a force or a torque that is dependent on the current speed it already is moving by.
  • Force Limited Joints - Give a normal joint a maximum force that it can use to correct itself. A useful step towards breakable joints or mouse control.
  • Simple Machines - Many simple machines are costly to simulate. Consider a crank with a rope. You could simulate the rope being wound around the crank, but that would be wasteful if you only wanted the result and not a fully interactive rope. By constraining the torque and rotation of one body to a force and distance applied to another, you have a drawbridge controlled by a wheel. Jam the wheel to stop the draw bridge.
  • Top-Down Friction - Not sure if there is a better name for this. Currently Chipmunk works really well for games viewed from the side, but not from the top. You can simulate a coin rolling across a table on it's edge, but not sliding on it's face.
  • Rolling friction - In the real world objects sliding against each other come to a stop because there is friction. A rolling object comes to a stop because it loses energy from deforming where it touches the ground. This might be a bit of a stretch as it would have to link itself to the contact constraint somehow.
A good example for the use of programmable constraints is a car. While the moon buggy example works alright, it could be made simpler and better using fancy constraints. By using damped spring constraints, you could set very high damping values without causing stability problems. You also would be able to set it and forget it without having to call the spring function and reset forces every step. As for powering the car, you could create a simple motor constraint where the motor's force is based on the current rotational speed. Lastly, the brakes could be modeled as a second motor that always applies a constant stopping torque that is clipped so that it doesn't reverse the direction. Now imagine that all of these constraints are already in Chipmunk for you.

By making it simple to extend existing constraints and to program new ones, it would be a good way for the community to share code. It would help people modularize and share many bits of code that would otherwise simply be written inside the update loop.
Can't sleep... Chipmunks will eat me...
Check out our latest projects! -> http://howlingmoonsoftware.com/wordpress/
supertommy
Posts: 56
Joined: Tue Sep 11, 2007 2:30 pm
Contact:

Re: Inheriting from cpPivotJoint

Post by supertommy »

This sounds very promising. I'm especially looking forward to the top-down constraints you mentioned. I did actually try to implement top-down friction joints for a racing game some time ago, but it turns out I suck at math and physics :cry:.
supertommy
Posts: 56
Joined: Tue Sep 11, 2007 2:30 pm
Contact:

Re: Inheriting from cpPivotJoint

Post by supertommy »

slembcke wrote:I was thinking about this too. I think the best way to handle this is to split the "class" definitions out into another header. That way you can copy the struct and replace functions that you want without having to make the joint functions visible. Not quite as elegant as real OO, but it could easily be wrapped by any OO or non OO language.
Were you able to find a way to do this? I've thought about it, and the only way I can see might work, would be to make a getter function for each of the joint classes. Something like:

Code: Select all

const cpJointClass *GetPivotJointClass() {
    static cpJointClass *pivotJointClass = NULL; 

    if (!pivotJointClass) {
        pivotJointClass = malloc(sizeof(cpJointClass));
        memcpy(pivotJointClass, GetJointClass(), sizeof(cpJointClass));
        pivotJointClass->type = CP_PIVOT_JOINT;
        pivotJointClass->preStep = pivotJointPreStep;
        pivotJointClass->applyImpulse = pivotJointApplyImpulse;
    }

    return pivotJointClass;
}
but I don't really think that's any more elegant than making the joint functions public and just warning people not to call them unless they know what they're doing.
User avatar
slembcke
Site Admin
Posts: 4166
Joined: Tue Aug 14, 2007 7:13 pm
Contact:

Re: Inheriting from cpPivotJoint

Post by slembcke »

You can declare the struct in the header using an extern and define it in the C code making use of the static functions in scope.
Can't sleep... Chipmunks will eat me...
Check out our latest projects! -> http://howlingmoonsoftware.com/wordpress/
supertommy
Posts: 56
Joined: Tue Sep 11, 2007 2:30 pm
Contact:

Re: Inheriting from cpPivotJoint

Post by supertommy »

I did consider the following example, involving the joint cpFooJoint, which inherits from cpPivotJoint and overrides the preStep function, but not the applyImpulse function:

in cpJoint.h

Code: Select all

extern const cpJointClass cpPivotJointClass;
in cpJoint.c

Code: Select all

const cpJointClass cpPivotJointClass = {
    CP_PIVOT_JOINT,
    pivotJointPreStep,
    pivotJointApplyImpulse,
};
in cpFooJoint.c

Code: Select all

const cpJointClass cpFooJointClass = {
    CP_CUSTOM_JOINT,
    fooJointPreStep,
    cpPivotJointClass.applyImpulse, /* DANGER: cpPivotJointClass.applyImpulse may or may not be initialized */
};
The problem with this approach is that the order in which cpPivotJointClass and cpFooJointClass are initialized is undefined (please correct me on this if I'm wrong), so this approach would fail for some people, but not for everyone. Depending on the mood of their linker.

I don't know what approach you had in mind, but I thought I should warn you about this potential trap.
User avatar
slembcke
Site Admin
Posts: 4166
Joined: Tue Aug 14, 2007 7:13 pm
Contact:

Re: Inheriting from cpPivotJoint

Post by slembcke »

So I've been doing a bit of a spike today playing with some of these ideas. You can take a peek here: https://chipmunk-physics.googlecode.com ... aint-spike
  • I generalized, slimmed down, and modularized the existing code.
  • I removed the use of pseudo-velocities for joint correction. Joints are a bit springier now, but ultimately more flexible and programable.
  • Added force limits to existing joint types. This made it easy to implement grabbing with the mouse.
  • Added a proper damped spring constraint. Unlike the cpDampedSpring() function, it doesn't work with forces that you have to apply manually and reset. You simply create the spring constraint and add it to the space.
I haven't really dealt with inheritance at all. Though now that I've actually started working with some real constraint programming, I'm doubting why I ever thought it would be useful to do so. For it to be really useful you'd have to split up the pre step and apply functions a lot more. For performance reasons, I'm not sure it's a good idea to do that. None of the ideas I listed previously would need to inherit anything. What would you use joint inheritance to do?
Can't sleep... Chipmunks will eat me...
Check out our latest projects! -> http://howlingmoonsoftware.com/wordpress/
supertommy
Posts: 56
Joined: Tue Sep 11, 2007 2:30 pm
Contact:

Re: Inheriting from cpPivotJoint

Post by supertommy »

I checked out that branch, and I really like those changes. Splitting all the joints/constraints into separate files is a good idea, and making cpConstraintInit accessible from outside of cpConstraint.c does address one of the problems I had with creating my own joints (although, does it not belong in cpConstraint.h, close to cpConstraintDestroy?)

I also like how the preStep and applyImpulse functions look so much simpler now.

I started this thread as a result of discussing breakable joints in another thread. I thought I could implement a cpBreakablePivotJoint by inheriting from cpPivotJoint, and putting a test in the prestep function to see if the forces were too large. Now that you've made a breakable constraint, that particular idea is no longer useful, but there could potentially be other uses for having working inheritance. Like, maybe I want to play a sound when my breakable joint snaps. Maybe by creating a cpLoudBreakableJoint which implements the preStep function like this:

Code: Select all

void
cpLoudBreakableJointPreStep(cpConstraint *constraint, cpFloat dt, cpFloat dt_inv)
{
    cpBreakableJoint *breakable = (cpBreakableJoint *)constraint;
    cpConstraint *child = breakable->child;

    if (child->klass->getImpulse(child) * breakable->last_dt_inv >= constraint->maxForce){
        playSound("break.wav");
    }

    cpBreakableJointPreStep(constraint, dt, dt_inv);
}
I'm not actually planning to do this, but I don't think it's an entirely ridiculous thing to do. My point is, I guess, that providing powerful and flexible mechanisms is a Good Thing, even if we don't know what people will end up using them for.

This isn't really terribly important to me, and I can certainly see that exposing the preStep and applyImpulse functions have both pros and cons, so this decision is all up to you Scott. I'm not going to complain either way.
Post Reply

Who is online

Users browsing this forum: No registered users and 13 guests