22: fish: rigging: the base swim loop
In order to rig the fish for swimming (which will include animating the fish) we have to know some things about our fish.
Different types of fish have different types of body, hence different types of swimming locomotion. The main purpose of the swimming body motion is to produce propulsion in an efficient and ergonomic way, and since anatomy differs from fish to fish, their swimming motion is vastly different also. Jeez, this is badly written.. I need some coffee... View these reference videos shamelessly embedded from youtube while the water is heating:
And unless you own a PETA t-shirt, you might want to watch this. Excellent reference!
Right, much better. Fish locomotion (sustained swimming) is generally divided into two groups, undulatory and oscillatory. "Divided" is not a good word, though, because they are more like two opposite extremes, with most fish falling somewhere in between.
Oscillatory is where the propulsion comes from oscillating the median and pectoral fins with no or very little body/tail movement.
Undulatory motions involve the wave-like motion passed along the propulsive structure (body and tail)
Here's an illustration that shows the differences:
Fresh water largemouth bass is an example of a fish with a carangiform body, where there's little side to side translation/rotation on the head and progressively more amplitude of the undulation the further down the body you go.
Here are some very nice reference links. There's a lot to read, but even skimming through it greatly increased my basic understanding of how fish swim:
Fish Locomotion Study at fisheriesmanagement.co.uk
Fish Locomotion: Kinematics and Hydrodynamics of flexible foil-like fins
Review of Fish Swimming Modes for Aquatic Locomotion
So having identified the type of motion the fish will need to perform, and also knowing roughly how our rig will work, we can try and plan ahead a little.
Basically, we will try and setup one rig for a basic swim loop and behaviour for thrusts, like twisting the body etc, and then feed that into a path following rig, all of which are automatically set up and populated given a path (dummy object anim, user created nurbs curve, or a particle simulation, where the velocity along the path will drive the speed and intensity of the swim cycle.
I've sketched out this flow chart for the rig (right click to view full size):
Last step of preparation is to prepare the Maya for some heavy rigging, which will speed the process up considerably. Set the menu mode to 'Animation' (unless you're only using the hotbox) and set up some hotkeys for the most frequently used windows. Here are mine, but use whatever feels natural to you. I will refer to these hotkeys, so unless you have specific hotkeys already, I'd recommend setting them up like this:
Graph Editor - Ctrl Alt 'g'
Script Editor - Ctrl Alt 's'
HyperGraph - Ctrl Alt 'h'
Expression Editor - Ctrl Alt 'e'
Connection Editor - Ctrl Alt 'c'
Now onto the first steps of rigging.
Just for the record, I've modeled the fish looking down along the positive Z-axis, so when I say translateX, I mean side-to-side, and translateZ means forwards/backwards. If you're following these steps, it might be a good idea to orient your fish in the same direction and freeze the transform before going further.
In the flow chart you see that we're creating some blendShapes for fish variations. Duplicate the fish a few times and name them descriptively, like bass_fat, bass_thin, bass_long depending on what variations you want to make. I usually like to keep a duplicate of the original fish mesh there as well, in case I screw up or want to tweak the original mesh later (inserting a blendshape at the front of the chain.
I have also started grouping my meshes to keep things tidy, which will be important down the line. How you organize your rig is up to you, but keep the structure in mind throughout the process, and it will be much easier to troubleshoot stuff later on. I have made 3 groups, one for geometry, one for the rig (with sub groups for swim_RIG, xform_RIG and skin_RIG) and one for the control objects and my outliner now looks like this:
I'll start with a fat one, as some fish are hungrier than others (hover to see selected verts and influence area). The white mesh is the original mesh, the blue is the blendShape mesh:
For the tail_waveUp and tail_waveDown meshes we will apply a sine deformer and animate it's translate Y attr for some fins floppyness. These meshes will be used as blendShapes later, so by painting the blendShape weights we'll isolate the effect to the fins.
Select the tail_waveUp mesh and create a sine deformer ( 'Create Deformers' -> 'Non-linear' -> 'Sine'). Name the sine deformer Handle 'tailWaveUpwards_sine' and parent it under your swim_RIG group (if you have one).
Reset the sineHandle transform scale to 1 (for more intuitive wavelength and translate values).
Set the Amplitude to something low, like 0.1 and the wavelength to 3.
Set rotate X to -20 so it slants backwards a bit and right click on the sine1's offset attribute and click 'Create Expression'. The expression window comes up and immediately set a name for your expression. I'll call it 'tailWaveUpwards_sineOffset_expr'.
sine1.offset = - frame / 5;
Press play and see if the motion of the waves looks plausible angle, wavelength and speed wise. Make adjustments if needed.
Now repeat for the tailWaveDownwards, leaving out the minus sign in the expression so it moves down instead of up, and parent both sineHandle transforms under the swim_RIG group:
Lastly, we have the dorsalSpines_down shape. If you watched the reference videos, you perhaps noticed that the spiny part of the dorsal fin is standing up or laying down depending on what "mode" the fish is in. To achieve this up and down motion, we'll create a blendShape, but to create the blendShape, we'll set up a mini rig to act as the modelling tool. Start by creating some joints along each of the modeled spines ('Skeleton' -> 'Joint Tool'). Anchor the top of the back with another joint chain.
Select the spine joints (except the roots) and select all the anchor joints. Shift select the mesh and skin it to the selected joints: 'Skin' -> 'Bind Skin' -> 'Smooth bind' (option box). Bind to: Selected joints, Closest in hierarchy with 'Max influences' set to 3. Now rotate the spine joint chains' root joints down so the spine rays lie flat on the back:
Delete the history of the mesh and pull some vertices to smooth the mesh at the base of the spiny fin (toggling on 'smooth mesh display' (3) might help).
Select our body variations meshes (except the tailWaveUp and -Down meshes) before also selecting the 'largeMouth_bass_swimGeo' and create a blendShape ('Create Deformer' -> 'BlendShape' optionbox. Set the name to 'bodyShapes', Origin to 'Local', and all three checkboxes to 'OFF'
Try them out and make any adjustments to the blendShape targets as you see fit.
Now, create a blendShape each for the tailWaveUpwards and tailWaveDownwards meshes. I called them tailWaveDownShapes and tailWaveUpShapes
Select the fish and either go to 'Edit Deformers' -> 'Paint Blend Shape Weights Tool' or RMB click on the fish and go to 'Paint' -> 'blendshape' -> 'tailWaveDownShapes - targetWeights'.
First set the 'Paint operation' to either Replace or Scale, set the 'Value' to 0 and click the 'Flood' button to set all weights to 0 (fish goes black).
Now use the 'Add operation', 'Value' 1, to roughly paint the tips of the bottom fins white. Checking the 'Clamp' checkboxes (both) may help produce predictable results. Next switch to 'Smooth' operation and make the weights nice and smooth.
Try and get both sides of the fins to have similar values, so we don't get inter penetrating polygons when they wiggle around.
Repeat for the 'tailWaveDownShapes - targetWeights' painting the dorsal fins and the top of the tail:
Turn on the blendShape and see how it looks.
Let's start with a standard fish spine. First I'll google around for some reference:
Fish generally pivot their body around their neck, which is to say, when they turn, they seemingly turn their head in the desired direction by whipping their tail (not actually turning their neck). The effect, though, is that they pivot around their neck and propel forwards. So let us start the spine from the neck and draw a series of joints down along the body.
Now I'll duplicate this joint chain 2 times and append "_swim" to each of the joints in one of the duplicate hierarchies, and "_anim" to each of the joints in the other. To the original joints' names I append "_skin".
The "anim" ones we'll hook up to some FK anim controllers later to give the animator some straightforward FK level of control, the "swim" chain we'll animate the swim loop with, and the original one we'll skin the swim loop fish mesh with, weighing the influence of the swim loop anim using the master control (which we'll create later).
I strongly recommend naming the joints something descriptive. I chose to append 'J' (for 'Joint') to my anatomical description, followed by number and then variant ("swim" or "anim"):
<anatomical description>'J'_<number>_<variant>
This way, when you track down an expression, or crawl through the web of connections in the hyperGraph later, everything will be soo much clearer.
Hide the duplicate joint chains and start adding some spine rays/rigs to the spine:
Now I'll do the head joints, and I'll only do them in the "skin" hierarcy, to try and keep the rig as light as possible. We'll spawn the full fish rig using our script later, so I'm trying to keep in mind that there might be 20 or more of them swimming around in Maya in a given scene, and the goal is to make that happen and running in realtime.
The head is fairly rigid due to the skull, so I'll adjust the spine joints' position a bit (select joints, hit 'insert', now you can move them using the 'move pivot tool' without affecting the children. A joint is essentially just a pivot, so the effect is the same as moving them, except the children stay put).
NOTE: if you're adjusting your joints at this stage, remember to unhide and select the corresponding joints in the "anim" and "swim" chains as well. You want to keep them identical copies still.
Now activate the joints tool, and from the side view click on the swimMasterJ_skin joint to declare it as a parent for the joints we will create, and click somewhere further forward, between the gills. A joint will be created. Click at the tip of the nose to create another joint. Switch to the top view and create a joint just a little off the center. This will be our pivot for the upper lip bone. Switch back to the side view and create a joint at the corner of the mouth. From the top view move it along the x-axis until it sits at the corner of the mouth.
The lip joints will be driven using an IK chain, so now is a good time to adjust the local rotation axes, so let up select the newly created joints, and make sure the joints' x-axis points towards it's child joint (except for the headTipJ).
One way of checking is setting the rotation mode to 'gimbal' and check that the y-axis is controlling side to side rotation, the z-axis is up/down and the x-axis is 'roll', or 'bank'.
Another way is going into component mode ('F8'), activate the 'question mark' button to see the local rotation axes. In order to be able to actually rotate the local rotation axes around you have to set the rotation mode to 'local'.
Now, when the head joint is pointing toward the headTipJ, the headTipJ is pointing straight forward (since we'll mirror the lip joints we don't want to 'favor' one side), the left upper lip joint is pointiing back down toward the upperLipCornerJ joint, which in turn is aligned with the continuing line from the parent joint.
The time has come where we mirror the upper lip, so select the upperLipJ_L and bring up the 'Mirror Joint' options ('Skeleton' -> 'Mirror Joint' option box):
Set the 'Mirror Across' choice to 'YZ' since we're mirroring to the other side, choose 'behavior' and since I named my upper lip joint with a "_L" suffix I can search for "_L" and automatically rename the new mirrored joints "_R" instead.
Repeat for the jaw; Activate the 'Joint Tool', click on the head joint (our parent joint), create two jaw joints (one for the breathing loop, and one for FK animation control), a jaw tip and next to it, a lowerLipJ_L joint and last a lowerLipCornerJ_L joint. Orient the joints (select the first jaw joint, go to 'Skeleton' -> 'Orient Joints'), fix the orientation of the jaw tip, making it point its x-axis forward, and snap the lowerLipCornerJ_L to the position of the upperLipCornerJ_L joint.
Rigging: the base swim loop
Rigging: Fins cloth setup
Rigging: Path progression
Scripting: Crowd/flocking/schooling