Download scene: File:constraint_networks_end_result.hipnc
Houdini allows you to build rigid body constraints with polylines. The lines represent the constraints, and the ends are attached to the rigid body objects by name matching. If no name matches, then they get attached to the world.
Constraint networks can be built with 'connect adjacent pieces' and a few wrangles, but more interesting results can be had by procedurally modelling the constraint edges, offsetting the constraint ends, and mixing multiple constraint types.
This topic sprawled out into 3 sections:
- ConstraintNetworks - this page, the basics, creating constraints manually
- ConstraintNetworks2 - use RBD to make weird walking creatures
- ConstraintNetworks3 - the new sop tools to create RBD networks introduced in 17.5
Simple approach with connect adjacent pieces
From skimming the docs and looking at other peoples setups, here's what we need to do. In the above diagram the packed rbd objects are blue, the constraint polylines are green, and the points of the polylines are red:
- packed geo have @name
- points of the polylines have a @name that matches to the packed geo they'll be linked to
- polyline prims have @constraint_name, used to match it to the constraint network type in dops
- polyline prims have @restlength to say how long they should try and be
The above gif shows how I've built constraint networks thus far. Connect adjacent pieces (which creates the @name attrib on the points), adjust the distance threshold and min connections to wire the points together, set @constraint_name in a primitive wrangle to 'con' (nice easy name to remember), and create the constraint network and hard constraint relationship dop to point to 'con' as a name, should see the constraints appear. How does it sim?
What? Oh, the prims need a @restlength attribute. Without it the constraints assume they should be of 0 length. Inserting a 'convert line' sop before the prim wrangle will add a restlength for us. Now when we sim, we get this:
Nothing happens. The constraint lengths are maintained, there's no other forces in the scene, so it all looks motionless. Good, but boring. Lets add some gravity.
Whoops. We better lock down one of the ends of this chain.
Lock constraints to the world
If you look at the prim wrangle, and inspect the geo spreadsheet in points mode, you can see that the points have a @name attribute, which matches to the names of the packed geo. That's how the constraint network knows how to connect things; it matches on name. If @name is an empty string, that constraint becomes pinned to the world. In a wrangle here I set the group to 0 (ie grab the first point), and in a wrangle type @name="";. Let's see what happens:
Sure enough that constraint is pinned, and the chain swings around that point. Unfortunately the first cube falls to the abyss. Why? Well, now none of the points have a @name that is 'piece0', therefore its an unconstrained object, and falls to its death. How to fix?
NOTE: In 16.0 and earlier, if @name on the polyline point couldn't find any packed geometry with the same name, it would also treat it as a world constraint. In 16.5 this is no longer the case, it only works if @name is an empty string. A lot of earlier setups (mine included) would use a @name like 'world', these setups will need to be updated!
Create an anchor
This is what we wanna build; a new little mini line near the first box, one end has @name of 'piece0' so its pinned to that box, the other has an empty name so its pinned to the world.
Coming up with a clean procedural way to do this isn't as intuitive as I'd expect. My current method is as follows in gif form (it's a long one, 2m10s, look at the time indicator in the lower right to know when it'll loop) :
Those steps are:
- remove the old pin-to-world point wrangle, we don't need it
- isolate the packed geo we want to pin with a blast node
- use an add sop to convert it back into a point
- transform a copy of the point away a little bit, and merge it
- add sop to convert those 2 points into a line
- convert line sop to get @restlength
- prim wrangle to set s@constraint_name='con'
- point wrangle to set one of those points to have @name=
- merge with the original constraint lines
- sim, make merry
Constraint type position vs all
Lets have another look at the setup so far; polylines between the objects, an extra little one for the first object pinned to the world, it all does its rbd constrain dance:
These are all hard constraints. Well if they're hard, why are they rotating? Especially the first object that should be pinned to the world? The reason is the default constraint behaviour is to pin position, not rotation. To tell it to do both you add a extra prim attribute to the polylines, @constraint_type, and set it to 'all'. Here's what happens:
The constraints now work like rigid wires, and lock all the rotation down. Sometimes you'll want one or the other, or maybe even a combo of the two, its handy to know you can do this. It also supports only constraining rotation (by setting @constraint_type to 'rotation'), Julian Johnson has some good examples for this on odforce: http://forums.odforce.net/topic/30476-constraint-network-constraint_type-rotation/
More interesting constraint networks
Again, lets look at the sim, pay attention to how the constraints lines move in relation to the boxes:
While the sim is working, the constraints move through the boxes like they weren't there; if anything its as if the boxes are constrained to the points, which doesn't feel quite right. Instead, compare to this setup:
Ah, the constraint ends are now sitting on the faces of the boxes, it all looks a little more believable. How has this witchcraft been achieved?
With some tricky procedural modelling. Here's the two constraint setups:
Simple enough; while the original looks like an unbroken line, the new setup is just little single edges between the blocks. When that is fed into the constraint network, Houdini just deals with it. This is more powerful than it seems at first glance. If constraints don't need to go through the centroids of the packed geo, you can model constraints like you might do in real life, and it'll behave as expected. Eg, shifting the constraints to the lower front corners of the boxes:
Or more interesting, using a 'copy and transform' to make multiple copies of the constraints. Note that if you specify enough links, the setup does what you expct and can no longer rotate, even with leaving @constraint_type in its default 'position' method:
Constructing those interesting networks
This is where your procedural modelling tricks come in handy. You've been learning all those tricks right, and not just diving into dops headfirst? I hope so.
It's all a bit more complicated than it appears at first glance, but the payoff is you can feed in fairly arbitrary packed geo setups, and get what you expect.
Full disclosure; while writing this tutorial I've rebuilt this about 4 times in completely different ways. I still reckon there's an easier way, but this'll do for now.
5th time's a charm! I was definitely overthinking it in the previous version (which is linked at the top of this page), the following method is much cleaner and easier. Basically, use a primitive sop to scale each polyline around its centroid:
- Connect adjacent pieces, set max connections to 2, and search radius high enough to make edges (2 in this case)
- Fuse in unique mode to split the verticies so each prim is unique
- primitive sop to scale each prim down to the right size
- transform to move the pivots to the corner
- copy and transform sop to make a matching prim on the other corner
- convert line sop to get restlength
Swapping hard constraints for spring constraints
Download scene: File:constraint_networks_end_result.hipnc
Here's where all the hard work starts to pay off. In dops, you can just swap out the hard constraint node for a spring constraint. Change the name its looking for, blam, you got springs:
Well, almost. I forgot to set the world constraint to use the same 'con' name, but more importantly, the default spring strength of 1 is way too low. Here's me going from 1, to 1000, to 10000, to 100000, only then do the springs start to behave:
The mistake above hints at what you can do next, make some constraints have the name 'hard', the rest 'spring'. In dops set the hard and spring constraint nodes to look for those names, merge the two nodes, and feed that to the constraint relationship node:
Constrain a sheet of grids
Download scene: File:rbd_grid_springs.hip
Same workflow as above, but in grid form.
- Assemble and pack your geo
- Connect adjacent pieces
- Convert line
- Fuse in unique mode so each little segment is isolated
- Primitive sop! As much as I dislike it, its perfect here for scaling the polylines down around their individual centroids
- Set attrs, etc
- The docs of course: http://www.sidefx.com/docs/houdini/nodes/dop/constraintnetwork#anchor-types
- Richard Lord's fantastic examples: http://richardlord.tumblr.com/
- Lots of Johnny Farmfield's experiments: https://vimeo.com/farmfield/videos
- Great examples on rotation constraints by Julian Johnson in this odforce post.
On to part 2, where we use animated constraints to drive rbd shapes, and ultimately make some walking creatures: ConstraintNetworks2