Programming Skeletal Mesh Bones : A Swimming Fish [C.B.Price init. 24-01-13] [C.B.Price rev. 12-04-13] [C.B.Price rev. 13-04-13] Basic Model We model the fish backbone as a series of bones with joints. Each bone has an associated processing unit as shown below. The output of this unit drives the muscles associated with each bone-joint. Also each processing unit is connected to its neighbour. This basic model is illustrated below. Now the question is how do we choose the processing unit? One model used extensively in the literature is a simple oscillator. This makes sense, since the wiggling of a swimming fish has the nature of an oscillator. So our conceptual model is a series of coupled oscillators, each oscillator has a state which drives the associated muscle making the bone segment rotate. This is illustrated below, where the angles of each segment for a short fish are shown. We use the notation ππ to denote the angle of bone i in the backbone Now let’s see how to make the simplest mathematical model of an oscillator. From your experiments with the monster truck suspension (without damping), you will have found out that the vertical displacement of the truck has the shape of a cosine curve shown below on the left, where the horizontal axis is time. Also trigonometry tells us how to calculate the cos of an angle, shown on the right. Of course the angle π is steadily increasing with time as the truck oscillates up and down. In other words we can think of an abstract oscillator as a point moving around a circle with a constant speed. 2 1.5 1 0.5 0 -0.5 -1 -1.5 -2 0 2 4 6 8 10 12 14 16 18 20 If we call the angle π then the value of this increases steadily with time. We can express this relationship as Δπ =π Δπ‘ where π is the speed of rotation around the circle (angular velocity). Now let’s consider our model of a backbone. We have a series of bones driven by their own oscillator, so we must write the above equation for each oscillator, like this Δπ1 = π1 Δπ‘ Δπ2 = π2 Δπ‘ Δππ = ππ Δπ‘ Here we are saying that oscillator 1,2, …i have their individual angular speed of rotation ππ around their circle. The variables ππ are known as the “phase” of each oscillator. The next stage in the model development is to couple each oscillator to its neighbours. The literature of mathematical biology directs us here. Let’s take the example of just two bones, therefore two oscillators. The above two equations are extended to account for the coupling like this Δπ1 = π1 + πΌsinβ‘(π2 − π1 ) Δπ‘ Δπ2 = π2 + πΌsinβ‘(π1 − π2 ) Δπ‘ We see there are extra terms on the right which represent the coupling. First note that there is a sin function involved. This takes the difference between the phase of each oscillator, calculates the sin of this difference then multiplies by a coefficient πΌ which sets the strength of the coupling between the oscillators. Now let’s extend this to a system of four bones and therefore four oscillators. The equations then become Δπ1 = π1 + πΌsinβ‘(π2 − π1 ) Δπ‘ Δπ2 = π2 + πΌsinβ‘(π1 − π2 ) + πΌsinβ‘(π3 − π2 ) Δπ‘ Δπ3 = π3 + πΌsinβ‘(π2 − π3 ) + πΌsinβ‘(π4 − π3 ) Δπ‘ Δπ4 = π4 + πΌsinβ‘(π3 − π4 ) Δπ‘ The first thing you notice is that there is an additional term in the equations for bones 2 and 3. This is because bone 2 is connected to both bone 1 and 3, and bone 3 is connected to two bones, 2 and 4. On the other hand bone 1 (the head of the fish) is only connected to bone 2 and bone 4 (the tail of the fish) is only connected to bone 3. Simulation Results for a Series of 4 Bones. Here we present some initial simulations for a 4-bone system which displays the behaviour of a swimming fish. The parameters are as follows ………….. . Let’s first look at plots of the phases ππ of the four oscillators, shown in the plot below on the left. The horizontal axis is time and the vertical axis shows how the phase of each coupled oscillator changes with time Looking at the phase plots, we notice two things. First all the phases ππ β‘increase on straight lines with time. Second, there is a difference between the phases of neighbouring bone-oscillators and this phase difference remains the same. What does this mean? Well, remember that the phase of an oscillator is how far it is around on its circle, so here the bone-oscillators are at different points around their circle, and this difference is constant between each bone. So the bones are oscillating, but there is a lag between the movement of each bone. Could this generate a swimming motion? Well, yes, so let’s see how. We cannot use the phase to drive the bone muscles (and therefore rotate the bones) since the phases are linearly increasing. We need to convert these phases into an oscillating drive for each muscle. This can be easily done by using a sin function. So we calculate the rotation of each bone in the real world using this simple expression ππ = π΄π ππ(ππ ) where A is a scaling factor. The results of doing this are shown in the above figure (right) where ππ has been plotted against time for all four bones. You will see that all bones are rotated with the same angle, but the rotation of each bone is lagged in time. This time lag produces the swimming motion. Here are some screen shots of the backbone at increasing times. Fig. The Model for N-Bones. The above model can easily be extended for a chain of N-bones where each bone-oscillator is connected only to its two neighbours. A fuller mathematical analysis not reproduced here [ cite ..] gives the following conditions for the model to work, to produce swimming movement: (a) All ππ should be set to the same valueβ‘π, except the head oscillator where π0 should be slightly larger than this value and the tail oscillator where ππ should be smaller than this value. (b) We have seen that there is a constant phase difference between the oscillators, as shown in Fig.?? We can choose the value of this phase difference as we wish. Let’s call this value πΏ. (c) We are free to investigate changing πΌ, the coupling strength (d) Having chosen πΏ and πΌ then the angular velocity of the head oscillator must be set to πβπππ = π + πΌsinβ‘(πΏ) (e) Also the angular velocity of the tail oscillator must be set to ππ‘πππ = π − πΌsinβ‘(πΏ) Let’s see how this was applied to run the 4-bone simulation shown in the figures above. (a) Set π = 1 (b) Set πΏ = π⁄4 corresponding to 45 degrees (free choice) (c) Set πΌ = 0.2 (free choice) (d) Calculate πβπππ = 1.14 (e) Calculate ππ‘πππ = 0.86 Possible Investigations There are two interesting parameters which could be explored, πΏ and πΌ. Perhaps the coupling strength would be a good place to start. Changing this value will change the values of πβπππ and ππ‘πππ and therefore change the behaviour of the fish. Here’s a comparison (below) of πΌ = 0.2 (left) and πΌ = 1.0 (right) where we have plotted the angle difference between bone-oscillators 3 and 4. (We have recalculated the head and tail frequencies which become πβπππ = 1.7071 and ππ‘πππ = 0.2929). Note that the frequency of the angle difference remains the same, and the size of the angle difference looks the same. However, tighter coupling means that the final swimming state is achieved more rapidly. Now let’s have a look at changing the value of the desired phase shift πΏ. Let’s set this to 0.3491 (which corresponds to 20 degrees). Returning to πΌ = 0.2 we now recalculate the head and tail frequencies which become πβπππ = 1.0684 and ππ‘πππ = 0.9316 for this new value of πΏ. Here’s a comparison of the new behaviour, again compared with the baseline parameters. Now we see again the same number of cycles, and the same slow rate of growth, but the difference lies in the amplitude of the swimming oscillations. They are now considerably smaller. It looks as though the parameters πΌ and πΏ can provide us with a means to design a swimming fish whose behaviour we can specify according to (i) how fast the fish moves from rest and gets into the stable swimming mode and (ii) the size (amplitude) of the fish swimming motion Code for the Phases, the Bone angles and the visualisation Let’s look at the equation for the second oscillator copied from above. Δπ2 = π2 + πΌsinβ‘(π1 − π2 ) + πΌsinβ‘(π3 − π2 ) Δπ‘ Multiplying both sides by Δπ‘ puts this in a form suitable for coding like this Δπ2 = [π2 + πΌsinβ‘(π1 − π2 ) + πΌsinβ‘(π3 − π2 )]β‘Δπ‘ which would result in code looking like this, theta2 += (omega2 + alpha*sin(theta1 – theta2) + alpha*sin(theta3 – theta2))*dT; This would be followed by the calculation of the bone angular displacement, boneAngle2 = sin(theta2); and finally visualisation using UDK functions as, SkelControlSingleBone2.BoneRotation.Pitch = scaling*boneAngle2 Of course the code for a series of bones would use an array structure to hold individual values of phase (theta) and bone angles. This can be reviewed in CBP74_Backbone_Fish.uc Code to link the above computations with the UDK Skeletal Mesh It is assumed that the skeletal mesh is created using 3DSMax or Maya. It is important to remember that no animation should be created in these packages. The mesh is imported into UDK (see UDK documentation) and can be viewed in the AnimSetEditor as shown below. Note the default names of the bones applied by the package. Next, an animation tree must be constructed since this is used to link the skeletal mesh to our code. As shown below in the AnimTreeEditor, the AnimTree node is linked to the skeletal mesh and picks up the bone names as imported from the Max or Maya file. Each bone is then assigned a SkelControlSingleBone which is assigned a name and linked to the appropriate skeletal mesh bones. The names of these nodes are picked up by the UDK code. See UDK documentation for details on how to set up the animation tree. The actual code (in the class CBP74_Backbone_Fish.uc) which picks up the animation tree, accesses the single bone nodes and uses these to move the bones contains a number of elements. First in the default properties of this class we find the declaration of the following skeletal mesh component which is added to the actor. Two links are made, first with the skeletal mesh and second with the animation tree, using their respective names. Begin Object class=SkeletalMeshComponent Name=SkeletalMeshComponent0 SkeletalMesh=SkeletalMesh'CBP74_Assets_Extra.SkeletalMeshes.boned-cylinder' AnimTreeTemplate=AnimTree'CBP74_Assets_Extra.Animations.Backbone' End Object Components.Add(SkeletalMeshComponent0) smesh=SkeletalMeshComponent0 Let’s look at the declarations of various arrays needed to link with the bones. At the top of the class code we find two arrays var() array<name> SCSBNameArray; var array<SkelControlSingleBone> SCSBArray; The first array will contain the names of the bones as identified in the animation tree (“Contbone1”, etc). These will be specified using UnreadEd. The second array holds the links to the skeletal mesh single bone controllers which will be used to rotate the bones. This array is populated as follows. Within the class there is a function PostInitAnimTree( …) executed automatically by the engine. This scans the list of bone controller names supplied in UnrealEd and finds the associated SCSB (Skeletal control single bone controller) and stuffs this into the SCSBArray, like this. for(i=0;i<nrBones;i++) { SCSB = SkelControlSingleBone(smesh.FindSkelControl(SCSBNameArray[i])); SCSBArray[i] = SCSB; } Now our code has access to the single bone controllers and can use these to uprate their rotation in the Visualization() function like this, using the values of the bone angles computed and stored in the bone angle array. for(i=0;i<nrBones;i++) { SCSBArray[i].BoneRotation.Pitch = scaling*boneAngleArray[i]; }