• entries
    743
  • comments
    1924
  • views
    580389

Constraints

Sign in to follow this  

1767 views



I'm pushing forward with adding the constraints system to my "engine/game/whatever" now. Bullet provides a comprehensive range of constraints for connecting rigid bodies together, but the API doesn't do much in terms of management. You just have to ensure you have removed all the constraints attached to a body before you destroy the body, since the constraints in Bullet use references rather than pointers.

To avoid worrying about this at the level I'm coding the game at, I've made the decision to have constraints handled quite differently to bodies, on the basis that a constraint implies the existence of a valid body (or bodies) whereas a body does not imply any constraints.

The physics system has a Physics::createBody method that returns a pointer to a Body. This pointer is owned externally to the physics system, and it fires an event when it is destroyed that the physics listens to and uses to remove its Bullet btRigidBody from the world. This Body is normally managed in the owning object by, for example, a scoped_ptr so that ownership of the body is controlled by its, er, owner.

Constraints on the other hand are owned by the physics system. You call something like Physics::createPointConstraint, passing in two bodies and the two local pivot points. The call returns a pointer to the constraint, which can be used to remove the constraint or modify its parameters and so on, but the actual constraint is owned by the physics object.

If you don't ever need to remove a constraint or modify its properties, you can just call createPointConstraint in a fire and forget manner, without even storing the pointer returned.

Internally, each Constraint in the physics system maintains a list of the Bodies it is attached to, and each Body maintains a list of Constraints that are attached to it. When a body is destroyed, it first checks and destroys any constraints attached to it, and removes the references to the same constraint any other body may have.

So when you have created a fire and forget constraint, it will be destroyed just before the first body is destroyed. If you manually destroy a constraint, using Physics::removeConstraint, it is tidied up in the same way and removed from the bodies references lists.

The Physics::constraintRemoved(Constraint*) event is fired whenever a constraint is destroyed, so if you have stored a pointer to a constraint somewhere, you can also connect to this event to be able to nullify your local reference should the constraint be destroyed. So a caller who stores a constraint pointer is responsible for listening out for an implicit destruction and updating itself accordingly.

So you have the best of both worlds, fire and forget for permanent constraints, or precise control over a specific constraint when you need it.

I've added a simple mouse constraint to the test bed, so you can pick up and drag objects around with the mouse. Be useful when debugging various future things to leave this in place for now.

Toward the end of the video, you can also see a key press is auto generating capsules from the model skeleton in preparation for the rag doll system. Now I have the constraints system working, I can start to now figure out how to connect these together into a coherent rag doll, which is the next step. From there, I need to work out how to read back the positions of these as they evolve through the simulation to construct a matrix palette from them to pose the model into its rag doll pose. Then we have automated "death" for any character with a skeleton, which is the long term goal and the main reason for building the physics on Bullet this time around.

Have realised just now though that I don't have any kind of collision filtering working yet, hence the character jumping a bit when I create the rag doll shapes, since nothing is in place to tell my character controller to ignore collisions with the rag doll shapes. I'm not actually even sure how Bullet handles collision filtering yet, so need to probably get this sorted out in a nice and tidy manner before I proceed.

[EDIT] Thanks for featuring this one. Bit of an odd one for a feature, since its mainly rambling that probably doesn't mean a lot to anyone else since its about code structure and architecture, but nice to be in the top list anyway.

Sadly, recent experiments have suggested I need to go back to the drawing board with the rag doll stuff. The chain of capsules generated directly from the animation skeleton seems to be too complex to build a stable rag doll from. Seems that the way to get a stable rag doll is to minimize the number of bodies/shapes involved and the animation requires too many intermediate joints that need to be composed into a single shape for good rag doll behaviour.

A good rag doll, I think, needs to be two capsules for the body, joined with a hinge, then two capsules each for each arm, joined by a hinge and attached to the body by a cone twist, and two capsules for each leg, joined with a hinge and joined to the lower body shape (pelvis) with either a hinge or a very constrained cone twist. Finally a head shape attached to the body with a cone twist.

I also seem to have an issue using fixed constraints in that they snap to an arrangement relative to each other that is not the same as when they are created. I tried creating fixed constraints to attach the foot capsule to the lower leg, when the foot was at right angles to the lower leg, but it snapped around so the foot was pointing straight down from the leg instead, so this needs addressing if I am to use any fixed constraints - need to find a way to constrain the shape so it maintains the relative rotation same as it is in when the constraint is created.

I have the test bed set up to create all of the required constraints just on pairs of capsules now - I have Fixed, Point, Hinge and Cone. Point can either be two bodies, or one body attached to a world point (used by the mouse joint). Its as above, a thin wrapper around the equivalent btTypedConstraint, in such a way that their lifetime is automatically managed as I've explained above. You have a separate method for creating each one in the Physics interface, but this isn't going to change a lot so I'll not bother making this any more extensible as I like the way Physics manages the constraints for me.

Had a fun bug that took a day or two to find:


void Physics::bodyDestroyed(Body *body){ for(auto &c: body->constraints) { removeConstraint(c); } rep->world->removeCollisionObject(body->bd); rep->bodies.trim(std::remove(rep->bodies.begin(), rep->bodies.end(), body));}Bet you can't see it either. Turns out removeConstraint() takes the body out of the body->constraints vector, invalidating the iterator. Once found, was a very simple fix:

void Physics::bodyDestroyed(Body *body){ auto copy = body->constraints; for(auto &c: copy) { removeConstraint(c); } rep->world->removeCollisionObject(body->bd); rep->bodies.trim(std::remove(rep->bodies.begin(), rep->bodies.end(), body));}Just goes to show, no matter how many years of C++ you have under your belt, you can still get tripped up by the most basic of errors.

Might actually add an ignoreBody parameter to the removeConstraint method, internally, since there is no actual need to remove the constraint from the body that is being destroyed here, but this is a micro optimisation and won't add anything of particular value, just a taddling of extra complexity.

So, anyway, need to now dream up a way to have a completely independent set of shapes for a rag doll for a character, find a way to pose these shapes based on the animation skeleton state at the time, and find a way to read the shapes back to create a matrix palette for the original animation skeleton to draw the character in the rag doll pose. This is going to be a more complex thing now, with some extra content creation needed at some stage in my pipeline to define the shapes and how they relate to the animation skeleton. Maybe I'll add some features to support this to Charm, my 3D model editor. Or maybe I'll write a separate tool for this. Not too sure at this stage, need to give this some pondering, so will think of something else to start working on in the meantime as I need to let these ideas simmer in my back-brain for a while I think.

Thanks for stopping by as always.
Sign in to follow this  


2 Comments


Recommended Comments

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now