Posts tagged ‘rigging. transform spaces’
December 8, 2012
So as I previously mentioned, multi-directional constraints can be achieved by invalidating the transforms. What I did’nt mention is that its pretty damn hard mixing up a group constraint mechanic with a non-group mechanic.
I’m resolving myself to have either one or the other, i.e a single object that can switch to multiple parents, or a group of objects that can share/switch to a single parent. With the group method I need to tweak some things and wrap the switching into a simple function. The structure basically looks/acts like this.
We have a switch attribute, that basically stores its targets, and index of target to switch to. The last target is deemed the world or invalidation target (unless i expose it for change). The reason for this if we have two objects A and B, with both in each others targets we need to invalidate one when it tries to switch to itself. We also need to switch that first E.g.
- A, B both have each others targets, both set to world.
- B switches to A, A is still set to world – the link is fine.
- A switches to B – the causes circular dependency.
So to fix this we walk the controllers and determine which will be invalidate, in this case B trying to connect to B will invalidate itself automatically to the world, allowing A to happily connect to it.
Why is this nice, well the core doesn’t change at all. All we need to build is a check for invalidation based on the index, invalidate and switch in the correct order.
Essentially a switch-box for switches.
February 25, 2012
So Maulik over at his blog is looking into developing multi-directional constraints. I’ve recently started looking into this and wanted to share my thoughts.
Essentially bi-directional/multi-directional constraints are a sub-set of regular transform space switching – crucially the difference comes not in the abstraction and application of spaces but in the ‘messaging’ of transformations. With a standard space switch your basically pushing the source objects transform into the targets and then multiplying it by an offset transform to keep it in its original place. Something like so:
1. We store the transform of B relative to A.
2. We set A’s transform to B’s relative to A’s parent space.
3. We apply the stored offset back.
A = B * A.parent^-1 * Offset
Deriving the type of switch (rotational/entire) is a trivial case of just returning that part of the matrix. With bi-directional/multi-directional constraints everything described above is that same, what changes is the messaging of the transforms being applied. If we take the example of two characters A and B playing a game of tug-of-war, we can see that if they both pull nothing happens. Each is sending its transform notifications to the other and its causing a stalemate. This is our cyclic dependency.
To rectify this you need to control how/when transform messages are sent. On initialization of the system only one party should be sending messages to the other – when the switch happens, you invalidate i.e switch the transform messages being sent to the controlled party with a neutral one e.g world. Now you can send transform messages in the opposite direction from the original controlled party to the controlling one:
1. B sends transform messages to A.
2. Invalidate A (so it’s messages being sent to it are neutral e.g from the world).
3. Send A’s transform messages to B.
If you’re software supports weak referencing things get easier – basically a weak reference (in 3ds max) monitors a nodes transforms without referencing the node directly. Only when changes occur does the monitor tell its actual dependencies – so how this works in practice is that you make the weak reference to the node, (an array of targets for example) but only pass transform messages once the nodes been invalidated. There’s some trickery too – adding a blank pointer to the target so it still updates for example.
1. Two nodes: A and B.
2. A’s targets = #(B,world) – weak references without referencing the targets themselves.
3. B’s targets = #(A,world).
4. A’s transform is set to world, B’s set to A.
5. Invalidate B’s by setting it to world.
6. Set A’s transform to B’s
Once you’ve set the transform, its messages flow through correctly. No cyclic nature happens because its has been invalidated first. I will try to discuss more on the ui side of this – it gets a little tricky..