Jump to content

How do bones effect the calculation of an object's position


PatS

Recommended Posts

I am trying write code that will consume, animate, and render an animation from a SCML file.

What I do not seem to grasp is how to apply the bones attributes to the objects.

There is one plug in that handles bones, but I do not have a computer that it will run on so I cannot get very far when I try to dissect the logic.

Is it possible to get pseudo code that describes how to do this

Thanks,

-Pat

Link to comment
Share on other sites

I think lucid is working on some more documentation that will illuminate further.

In the meantime, I put together a SPriG (software SDL) renderer for SCMLpp. You can find it in the SCMLpp repository. SPriG should run on nearly anything.

Do you have any specific questions? Is it a procedure for the parent transformations that you're after?

For the transforms, SCMLpp r41 does that in SCML::Entity::draw() on line 2471 of SCMLpp.cpp and SCML::Entity::draw_tweened_object() on line 2600 of the same file. It's not totally cleaned up yet, but it demonstrates a naive implementation which works. Spriter/SCML does not use matrices for these transforms, so you do need to go through specific steps to get there.

The basic idea (as I see it) is to take the child position, scale it by the accumulated scale_x and scale_y, then rotate it about the origin by the accumulated angle, then translate it by the parent position. After that, you combine the transforms by adding the angles, multiplying the scales, and set the parent/accumulated position to the new child position.

Link to comment
Share on other sites

[EDIT]

Hi PatS, we will be updating the documentation as soon as we can as well, but feel free to ask any more questions here in the meantime.

Basically first you multiply the object's(sprite or bone) x and y value's by the parent's scale_x, and scale_y values, then assuming px and py are these multiplied x and y values, and parentX, and parentY are the parents absolute position. The return value of this function would contain the absolute x and y of the child.

PointF rotatePoint(float px,float py, float parentAngle,float parentX, float parentY)
{
float s = sin(toRadians(parentAngle));
float c = cos(toRadians(parentAngle));
float xnew = (px * c) - (py * s);
float ynew = (px * s) + (py * c);
xnew+=parentX;
ynew+=parentY;
return PointF(xnew,ynew);
}

Also note that the parentX and parentY values must be absolute. This means in the SCML file if the parent has parents, these x and y values, etc should already be mapped to the parent using the same formulas. Also note, that in the SCML format, all of the bones appear in a flattened hierarchy, which means that this will always require only one pass. No bone will have a parent that comes after it in the file. Reading sequentially one time through will always be enough. Lastly, depending on the coordinate system of the target platform, that is, whether angle increases counterclockwise and y increases upward (as in the format), or the opposite, you may have to make angle=360-angle; or y*=-1; before plugging them in the formula.

Let me know if this helps. And don't hesitate to ask any more questions, I don't want implementations to slow down while waiting for documentation. Documentation requires extra time to ensure it contains detailed examples and illustrations where necessary.

Link to comment
Share on other sites

I think I have it.

Does this pseudo code look correct?



Save the position, angle, and scale from the root of the hierarchy (they are parentPosition, parentAngle, and parentScale)

Loop for the remaining hierarchy entries

a) Calc the bones position by interpolating this object's position with the corresponding object in the next key frame

b) Multiply the resulting X and Y values by the interpolated scale X and Y

c) Calc the newPosition rotate_point(bone.pos, parentAngle, parentPosition)

/* these are used for the next item. */
d) Calc the parentPosition by interpolating this object's position with the corresponding object in the next key frame

e) Calc the parentAngle by interpolating this object's angle with the corresponding object in the next key frame

Loop

The object is drawn with it's upper left corner at the final new position, rotated by the final parentAngle at the object's pivot point.

So if the object's bone hierarchy as 2 entries, I would process the object's parent's parent, then the object's parent and finally the object.

The key frame has 10 bones, only those two that are direcly related to object are processed.

One more question, if the SCML file has spin=0, should the spin be treated as -1 or 1?

Thanks

-Pat

Link to comment
Share on other sites

spin should only occur if the angle's are exactly the same, or the angle change is a tiny rounding errors difference. I will update the documentation soon.

As for the pseudo code

this is how I will implement it myself when doing implementations, and was the original intention when designing SCML.

It actually makes things much more simple, and you should never have to worry about whether a parent has a parent.

Take this example:

                    





What I would do implementation side is create an array for parent coordinates.

Of course your language will probably require you to size the array beforehand, or use a dynamically resizeable vector, but for simplicities sake, I'll ignore those aspects here. Also for the sake of simplicity, when I say bone info, I mean angle, position, scale, etc.

Lets call our array 'bones'

you would just go in order and fill this array:

you see bone_ref id="0" has no parent, so you'd just copy the bone info straight from the SCML into the array at bones[0]

then at bone_ref id 1 the parent is 0, so instead of directly copying the info to bones[1], you'd map the info to it's parent info at bones[0], and save that mapped info to bones[1] instead of what's directly in the SCML file

then at bone_ref id 2, the parent is 1, so you'd get the parent info from bones[1]

all from the same array you're working with, so you'd get the parent info that's already been mapped at bones[1].

in this way the hierarchy will automatically work itself out by going in this flattened order, and you should never have mirror or analyze the structure.

SCML will never have parentage in an order where you haven't filled that array with the information yet, and the same goes for sprites, you can use this same array, and you'll only have to map the information to whatever is there. You never have to check back through the hierarchy or check for deeper parents because the information in your array would already be based off of any parent's before it in the hierarchy.

if that doesn't make any sense, let me know, and I'll try to explain it another way.

Link to comment
Share on other sites

To clarify, when spin=0, you simply interpolate the angle, otherwise the logic in the GettingStarted doc (pg 7) applies. Correct?.

For the bones, I ended up storing an array of pointers to the bone coords with each object in a keyframe. I build the array by traversing the bones from the object's parent back. When I update the object during playback I simply traverse the object's bone array from bottom to top.

To illustrate: Lets say that the bone hierarchy looks like this

  
Torso
/ | \
Head Arm Leg
| |
Hand Foot

The keyframe has an array of bones

Torso

Head

Arm

Hand

Leg

Foot

The Foot object has an array of bones:

Foot

Leg

Torso

When I update the foot, I process the it's bone array in reverse order (Torso, Leg, and Foot).

If understand what you are suggesting, the bone structure will have it's coords and pointer to it's parent. I had starting with a similar approach, where the bones form a linked list that you follow until there is no parent, however, I did not see an easy /eloquent why to process the bones from the top down.

One more comment, the field named scale thew me off, I assumed the it was describing the size of the object.

One more question, how do I interpret the pivot point in the object? Do I covert it to pixels by multiplying it by the object sprites height and width?

Thanks very much for your help

-Pat

Link to comment
Share on other sites

hi pat. That is correct about scale, multiply scalex by the original width of the sprite, and scaley by the original height of the object, and pivotpoint should be multiplied by that final width and height.

as for why to process top down

The keyframe has an array of bones

Torso

Head

Arm

Hand

Leg

Foot

The Foot object has an array of bones:

Foot

Leg

Torso

you process the torso once with it's total lack of parents, and save this info.

the head,you calculate it's final position once, and save this info

then the arm, and hand, and the leg and foot.

all this occurs once in the order you find everything in the scml file. When you get to hand for instance, you don't trace back to arm and then torso. You already calculated the arm once, so you don't even have to bother worrying about whether it's connected to something else, even if it was a chain of 20 bones. Whatever the immediate parent is, if you just go in order, it should already be calculated, and require no back tracing.

In most cases you would have a few things attached to the leg, the leg sprite, the foot bone maybe, and the foot sprite.

instead of tracing back the legsprite, to the leg, then the torso, and calculating all that againm

then the the foot bone, calculating the leg, and torso again, then the foot sprite calculating the foot bone, then the leg bone, then the torso. If you calculate and store all the bones in top down order, each bone is calculated only once per frame no matter what.

  
Torso
/ | \
Head Arm Leg
| |
Hand Foot

if the foot's immediate parent is the leg, you already have the leg's position precalculated. whether the leg is the first bone in the chain, or goes back one more to the torso, or your skeleton traces back 20 bones from the leg, there's only one parent position/angle/dimension to worry about, and that's already been calculated to it's final position, so you don't have to check if it has any further parents, because if it did, you've already taken that into account if you read in the values, and calculate them in order. Your way would work as well, but it requires extra work from you, and extra processing power (which increases exponentially the deeper the bone hierarchy gets)

Link to comment
Share on other sites

no problem. also on that last post, I think I forgot to answer your question on the spin=0;

that should mean don't even change the angle. don't bother tweening because the next one should be the same. If there's a case where it says spin=0, and that's not true, and the angles are different, it's a problem with the save file that I need to fix

Link to comment
Share on other sites

Is there a reason that Spriter can not just export the pre-calculated position for each Timeline object??

It seems kinda redundant for the plugin to to all the work of interpolating / mapping of bones, when the end result is just modified position data of the original Object's. Why not just bake the bone-data into the object's positions themselves?

I realize there's use cases for having access to the bane data, but I'd guess they are in the minority, most games will just want to run whatever the designer has created.

Perhaps a checkbox to toggle this??

Edit - BTW, I'm currently working on implementations for Flash Starling and EaselJS Canvas, which will add more support than the existing plugin, like ability to swap pieces for another, or control playbackl speed. I'm 90% of the way there, remaining issues are rotation and bones.

Link to comment
Share on other sites

Is there a reason that Spriter can not just export the pre-calculated position for each Timeline object??

It seems kinda redundant for the plugin to to all the work of interpolating / mapping of bones, when the end result is just modified position data of the original Object's. Why not just bake the bone-data into the object's positions themselves?

I think the difficulty comes from an object only being able to tween between two points in a straight line and / or rotate about a point (which is possibly moving in a straight line). If there are multiple parent bones rotating individually, the path of the child can't be expressed with those two motions.

It might be possible in simpler cases to eliminate a bone from the export, but I wouldn't want to be the one who has to program it so it gets it right all the time.

Tim

Link to comment
Share on other sites

timpart is correct. Just imagine this scenario

pretend the arrow is a bone, and SPR is a sprite

keyframe 0 has this:

-------->SPR

keyframe 1 has the bone rotated 180 degrees to this:

SPR

hardcoded interpolation data would just have the sprite moving in a straight line from one side to the other. Bones are definitely more processor intensive, but that's the cost of that type of animation. That being said, after 1.0 is done I have an experiment that might prove useful in this regard. The same way export to PNG will eventually allow you to choose a frame rate, and then export pngs at each tween level, I'd like to see what the results are with a bone free export that does the same thing, hardcoding the position/angle/dimension data from multiple points in between frames. I'm assuming there's a threshold for when you can't tell it's not being truly rotated anymore, say for every 10 degrees of bone rotation there would be an addition keyframe added inbetween. Even making a ball move in a semi circle with only 7 or 8 frames appears like a smooth movement, so it will probably work really well. This of course would be a separate export option, since your artist would still need the bone data if they wanted to use it for animation purposes.

Link to comment
Share on other sites

timpart is correct. Just imagine this scenario

pretend the arrow is a bone, and SPR is a sprite

keyframe 0 has this:

-------->SPR

keyframe 1 has the bone rotated 180 degrees to this:

SPR<----------

hardcoded interpolation data would just have the sprite moving in a straight line from one side to the other. Bones are definitely more processor intensive, but that's the cost of that type of animation. That being said, after 1.0 is done I have an experiment that might prove useful in this regard. The same way export to PNG will eventually allow you to choose a frame rate, and then export pngs at each tween level, I'd like to see what the results are with a bone free export that does the same thing, hardcoding the position/angle/dimension data from multiple points in between frames. I'm assuming there's a threshold for when you can't tell it's not being truly rotated anymore, say for every 10 degrees of bone rotation there would be an addition keyframe added inbetween. Even making a ball move in a semi circle with only 7 or 8 frames appears like a smooth movement, so it will probably work really well. This of course would be a separate export option, since your artist would still need the bone data if they wanted to use it for animation purposes.

Ah, ya that makes total sense.

As an alternative to that, what about the concept of "Filler Keys", these are like an extra layer of keyframes that the designer can toggle on or off. When toggled on, Spriter simply insert keys at the selected frameRate, shown as a series of non-editable keyFrames of a different color.

So, in your example, the animation is just 3 actual keyFrames, but to avoid bone overhead, the designer Chooses "Enable Filler Keys" and selects a target frameRate of 30, spriter slaps in a keyframe at every 33ms for the entire animation and bones are excluded from the export.

Just a thought...this should saves some extra processing time at runtime.

Link to comment
Share on other sites

Is there a reason that Spriter can not just export the pre-calculated position for each Timeline object??

It seems kinda redundant for the plugin to to all the work of interpolating / mapping of bones, when the end result is just modified position data of the original Object's. Why not just bake the bone-data into the object's positions themselves?

I realize there's use cases for having access to the bane data, but I'd guess they are in the minority, most games will just want to run whatever the designer has created.

Perhaps a checkbox to toggle this??

Edit - BTW, I'm currently working on implementations for Flash Starling and EaselJS Canvas, which will add more support than the existing plugin, like ability to swap pieces for another, or control playbackl speed. I'm 90% of the way there, remaining issues are rotation and bones.

Linear interpolation of positions, cannot properly simulate linear angle interpolation. What happens is, joints will pop, and separate. This can be easily demonstrated if you animate some joints without using bones. If you don't need interpolation, why not just have Spriter export a series of PNG frames?

Link to comment
Share on other sites

Linear interpolation of positions, cannot properly simulate linear angle interpolation. What happens is, joints will pop, and separate. This can be easily demonstrated if you animate some joints without using bones. If you don't need interpolation, why not just have Spriter export a series of PNG frames?

I'm interpolating the positions just fine, it's just the bones that are the issue (well not really an issue, just a matter of adding support for them, time permitting).

I guess my request could be described as:

"The ability for my designer to use bones, as a shortcut, when animating, but, to not have that to deal with the extra overhead of processing the bones at runtime.".

Maybe it's not possible, but it would be nice to figure out a way to do enable this.

Link to comment
Share on other sites

  • 1 month later...

(I'm posting here because I have a bone related question and most other similar questions refer to this thread)

Because it is my first post, I have to say that Spriter is an amazing program and believe it has a great future. Keep up the good work! :D

I'm trying to do a haxe/nme implementation. I managed to do the rendering (with tweening) of simple animations. Now, I'm struggling with the bone implementation.

I read this thread several times and I'm pretty sure I understand how to apply parent/child transformations (in one pass). However, I'm not sure how to combine this with tweening. Do I have to:

- tween each bone's/object's transformations and then calculate the hierarchy

or

- calculate the hierarchy and then do the tweening on the final result

or

- something else I'm missing

I guess the first approach is correct because the second one can be reduced to baking transformations to objects, but I just want to make sure I'm doing the right thing from the beginning.

Link to comment
Share on other sites

I think tweening each bone/object is the only way to go. The results would be different if you tweened the final results instead because parent rotation tweening would be converted to child position tweening and it would be an ugly mess.

Woohoo, it works!

Now, when I have implemented the basics, everything makes perfect sense. Thank you!

Link to comment
Share on other sites

  • 2 weeks later...
Basically first you multiply the object's(sprite or bone) x and y value's by the parent's scale_x, and scale_y values, then assuming px and py are these multiplied x and y values, and parentX, and parentY are the parents absolute position. The return value of this function would contain the absolute x and y of the child.

PointF rotatePoint(float px,float py, float parentAngle,float parentX, float parentY)
{
float s = sin(toRadians(parentAngle));
float c = cos(toRadians(parentAngle));
float xnew = (px * c) - (py * s);
float ynew = (px * s) + (py * c);
xnew+=parentX;
ynew+=parentY;
return PointF(xnew,ynew);
}

Hi lucid, what I don't understand is that, looking in the editor, the scale_x and scale_y of the bones seems to affect the size of their childs, instead of their position.

I mean, if I stretch a bone, the child get deformed instead of positioned.

What am I getting wrong?

Link to comment
Share on other sites

hey funHazard, it affects both. aside from the position, it you multiply the scaleX by the child scaleX as well.

sorry, I was just focusing on the position part of the question. I'll update the documentation as soon as I can to include a detailed explanation of how to do bones.

Link to comment
Share on other sites

  • 1 month later...

I was interested in using Spriter with a Haxe NME project, as well. I was just wondering if your code was open-source, or if you were interested in sharing it? Thanks! :D

Woohoo, it works!

Now, when I have implemented the basics, everything makes perfect sense. Thank you!

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...