One of the most wanted features for a 2D animation software is the bones feature. The original developer didn't implement bones in Synfig because it could become a restriction to the freedom of the animator to give expression to the creation. Finally, the decision of include the bones feature was taken and a core bones implementation is already available in the Synfig code.
Bones feature has two main parts. The core of the bones feature and the graphical user interface of the bones feature. At the time of writing this article, only the core of the bone feature is implemented with a very primitive user interface.
This article will introduce you in the knowledge of how are currently the bones feature implemented and how should it continue its development. Once finished the bones development, this pages should be moved to the talk page and the Dev namespace should be removed from the article to turn it into a regular documentation page.
The firsts seeds of the bones development are the notations in the Bone Layer. At first stage the idea of a Bone layer was around because most of the features of Synfig are applied in the form of layer on a stack. During the bones development we realized that the concept of bone layer is not strictly needed. Currently it is possible to not have any Skeleton layer (this is how it is called in the Bone Layer page) and the bones can be working on a Synfig document.
Bones affect points (blinepoints or individual vertexes). This means that when a bone is modified it can affect to points that are under its influence.
- Local Width Scale
- Local Length Scale
- Recursive Width Scale
- Recursive Length Scale
- Setup Origin
- Setup Angle
- Setup Length
Although they can look to be a lot of parameters, we can concentrate only on some of them: origin, angle, length/width (we will talk later about the recursive ones). Let's see how do those parameters affect to a point that is under the influence of a bone:
- When a bone modifies its angle, the point rotates around the origin.
- When a bone modifies its origin, the point translates the same amount than the bone's origin.
- When a bone modifies its length or width, the point scales in the length or width direction of the bone relative to the bone's origin.
All the above point modifications under bone influence needs some other information to allow know the exact position of the point after the influence. For example, when the bone rotates, the distance of the point to the bone's origin is very important, because it determines the rotation radius.
So, there must be a "setup" position of the point relative to the bone where the calculations are made from. Following that reasoning, the bone should have two sets of values, the setup values and the animated values. And so, the points should have a setup values and a calculated values when the bone influence is applied.
Those two set of values (setup and animated or calculated) are representation of the same object in two states, the setup state and the animated state.
Apart of the single bone -> point influence it is possible that a point were influenced more than one bone. If only one bone could affect to a point, then we would be talking about cutout animation. But bones are much more than cutout animation. The points can be influenced by more than one bone at a time. To solve this problem we implemented the concept of weighted influence. A point is influenced by a the pair of the bone an its weight so each single bone influence is multiplied by its weight and then added to all the other weighted bones and finally divided by the sum of weights. This is known as linear combination of bones.
Value Node Bone, BoneInfluence, BoneWeightPair, Skeleton
To calculate the bone influenced animated position of a point, we take the setup position (point and bone) and apply the animated values of the bones (the bone influence transformation to calculate the modified value of the point. This is performed for each bone and then linearly combined with the bones weights. The higher the weight is, the more influence the bone has on the particular point.
But, how are those operations performed in the current design?
Initially we can think on bones as layers: "All the context below the layer is affected by the bone layer". A design like that implies to scrubb the layer stack and apply the bones effect to all the layers below in the bone's scope. That leads some problems because maybe you don't want to affect by a bone some kind of layers (i.e. filters) or perhaps you don't want to apply the bones influence to all the geometry layers that are below the bone layer. For one reason or other, the user would like to decide which layers are affected by the bones and even which parameters should be affected. That breaks the layer concept of Synfig and needs to use other Synfig feature: The Value Node.
So the decision on what parameter is influenced by a bone is taken converting the subject of the influence (vertex or blinepoint) into a BoneInfluence Value Node Convert type. The conversion offers the following sub parameters:
- ValueNode_BoneWeightPair List: This is a list of ValueNode_BoneWeightPair value node types
- Link: This is the point at setup position
And Value Node Bone Weight Pair has the following subparameters:
- bone: This is the reference to a ValueNode_Bone
- weight: this is the weight of this bone over the value node.
As the bones can be used several time at different bones influences we need to define them as Value Nodes because they are reference counted objects.
To hold the bones data there are two classes:
Bone & ValueNode_Bone
Bone has the follwoing members:
- Name: Name of the bone
- Origin: Current Origin of the bone relative to its bone parent
- Origin0: Current Origin at setup
- Angle: Current Angle of the bone relative to its bone parent
- Angle0:: Current Angle at setup
- Scalelx: Current local scale of the bone along the length of the bone
- Scalely: Current local scale of the bone along the perpendicular of the lenght of the bone
- Scalex: Current recursive scale of the bone along the lenght of the bone
- Scaley: Current recursive scale of the bone along the perpendicular of the length of the bone
- Lenght: Current lenght at setup.
- Strength: Current strenght at setup. It acts like the Length but in the perpendicular direction (check this!)
- Setup: Boolean that defines if the bone is showing its setup position or not.
- Parent: Pointer to the parent ValueNode_Bone. It can be animated along the time
Bone class is responsible of the set get for all its members as well as of retrieve the needed transformation matrixes of itself infleunce over a single point. It is the placeholder of the data.
On the other hand ValueNode_Bone class defines the proper memebers to:
- Define a "layer parameter type" interface of the Bone class. The Bone members has a "parameter like" interface on a ValueNode_Bone
- Takes care of collection of bones at canvas level. Bones must be unique objects and its usage in different places must be known. So a bone's map is needed in order to find if the bone is used and who is using it. This is needed to resolve recursive parenting of the bones in the bones hierarchy.
- Defines the ValueNode_Bone_Root as a class inherited of the ValueNode_Bone. It defines the propoer values for a root bone (no parent).
- Collects information about how many bones affect a given value node (to draw the set of bones that affect it when the value node is selected)
- Collects information about possible parents of a given bone (to avoid bad reparenting).
Current Bone - User interface
In Synfig Studio all the some of the layer parameters and some of the value node types are represented in the work area as ducks to easy user handling. This feature for value nodes has been defined for the ValueNode_Bone. So when the user selects a ValueNode_Bone the ducks representation of its values are shown in the canvas.
As the bones has two set of values (setup and non setup) there has been defined two duck masking states: Setup and notn setup modes. The default mode mask is non setup, so when the user want to watch the setup values it is needed to press ALT-7.
To hold the bones value nodes and show them public to the user interface there is needed something that allows the user add, delete and change the bones value nodes easily. For that porpouse it was designed the Skeleton Layer. A Skeleton layer is just a Layer that holds a name and a list of bones. Skeleton layer position in the layer's stack is not relevant. That's because the ValueNode Bones are that, ValueNodes and are not dependent of any layer.
When the user selects a Skeleton layer, all its bones are shown in the current canvas and so the user can identify and modify them.
Also when the user selects a layer that has a Value Node converted to Bone Influence (explained above) Synfig Studio shows all the bones that influence the converted Value Node, as well as any other bone that can influnece that bone (like a bone parent and all its ancestors). This is particulary useful when working with layers under bone influence because it allows the user determine quickly which bones are influencing the current selected layer(s) and later modify the bones for the proper animation.
It is important to remark that delete a bone doesn't delete it from the canvas. As any value node it exists until all the references to it exist so even deleting the skeleton layer, the created bones are available for usage in any BoneInfluence subparam or any other reparenting between bones. IIRC, bones cannot be deleted at all and always exists. There should be a way to mark them as in the trash can once the user don't want them to be shown in the available bone list. Later the user could rescue any them from the thomb it needed.
Also bones representation on the canvas depends on who is requesting to draw the value node bone. If the Skeleton layer is under a transformation stack due to the user placed it under some distortion layers, the value node bone's ducks are affected by the transformation stack. It doesn't mean that the bones are transformed like that. For example, when a layer that has value node bone influence in it, is selected the value node bone's ducks are drawn with the current transformation stack of the layer. But if the bones are influencing to other value node bone influence types that are under othre transformation stack its ducks are tranformed as well. Bones are defined always relative to its parent and if the bone is a root bone (no parent) it is defined relative to the ValueNode_Bone_Root which is placed at the global origin (0,0), unscaled and has a lenght of 1.0 and an angle of 0 degrees.
Appendix: How does bones feature work?
This is a description of the internal of how does bones work - Skeleton Layer. It is not necessary to understand/read this part to understand the usage of the bones from the point of view of the user interaction.