Jump to content
  • entries
    940
  • comments
    5,894
  • views
    864,006

Animation in Leadwerks 5


Josh

2,915 views

 Share

The design of Leadwerks 4 was meant to be flexible and easy to use. In Leadwerks 5, our foremost design goals are speed and scalability. In practical terms that means that some options are going to go away in order to give you bigger games that run faster.

I'm working out the new animation system. There are a few different ways to approach this. In situations like this I find it is best to start by deciding the desired outcome and then figuring out how to achieve that. So what do we want?

  • Fast performance with as many animated characters as possible. Hopefully, tens of thousands.
  • Feedback to the CPU on the orientations of bones for things like parenting a weapon to the character's hand, firing a shot, collision detection with limbs, etc.
  • Low memory usage (so we can have lots and lots of characters).

In Leadwerks 4, a bone is a type of entity. This is convenient because the same entity positioning commands work just fine with bones, it's easy to parent a weapon to a limb, and there is a lot of consistency. However, this comes at a cost of potential performance as well as memory consumption. A stripped-down Bone class without all the overhead of the entity system would be more efficient when we hit really large numbers of animated models.

So here's what I am thinking: Bones are a simplified class that do not have all the features of the entity system. The Model class has a "skeleton" member, which is the top-most bone in a hierarchy of bones for that model. You can call animation commands on bones only, and you cannot parent an entity to a bone, since the bone is not an entity. Instead you can attach it by making a copy that is weighted 100% to the bone you specify, and it becomes part of the animated model:

weapon->Attach(model->FindBone("r_hand"));

If you have any hierarchy in that weapon model, like a pivot to indicate where the firing position is, it would be lost, so you will need to retrieve those values in your script and save them before attaching the weapon.

This also means bones won't appear in the map as an editable entity, which I would argue is a good thing, since they clog up the hierarchy with thousands of extra entities.

When you call an animation command, it will be sent to the animation thread the next time the game syncs in the World::Update() command. Animations are then performed on a copy of all the visible skeletons in the scene, and their 4x4 matrices are retrieved during the next call to World::Update(). Animation data is then passed to the rendering thread where it is fed into a float texture the animation shader reads to retrieve the bone orientations for each instance in the batch it is rendering.

This means there is latency in the system and everything is always one frame behind, but your animations will all be performed on a separate thread and thus have basically no cost. In fact with the simplified bone class, it might not even be necessary to use a separate thread, since carrying out the animations is just a lot of quaternion Slerps and matrix multiplications. I'll have to try it and just see what the results are.

The bottlenecks here are going to be the number of animations we can calculate, the passing of data from the game thread to the animation thread and back, and the passing of data from the rendering thread to the GPU. It's hard to predict what we will hit first, but those are the things I have in mind right now.

It would be possible to carry out the animation transforms entirely on the GPU, but that would result in us getting no feedback whatsoever on the orientation of limbs. So that's not really useful for anything but a fake tech demo. I don't know, maybe it's possible to get the results asynchronously with a pixel buffer object.

In addition to animation, having tons of characters also requires changes to the physics and navmesh system, which I am already planning. The end result will be a much more scalable system that always provides fast performance for VR. As we are seeing, the optimizations made for VR are having a beneficial effect on general performance across the board. As explained above, this may sometimes require a little more work on your part to accomplish specific things, but the benefits are well worth it, as we will easily be able to run games with more characters than the video below, in VR, perhaps even on Intel graphics.

 

 Share

8 Comments


Recommended Comments

For me the biggest thing would be getting the feedback to know what "frame" (I know it's not frame based) the animation is on as that's when you run certain logic to sync things to an animation. Would we get that if another thread was doing the animation from the main thread?

  • Like 1
Link to comment
8 minutes ago, Rick said:

For me the biggest thing would be getting the feedback to know what "frame" (I know it's not frame based) the animation is on as that's when you run certain logic to sync things to an animation. Would we get that if another thread was doing the animation from the main thread?

What if some kind of Actor function was called when a preset time is passed on a specified animation? Like this:

Skeleton::AddEvent(“attack”, 364, object)

And then it calls something like this when that frame is passed:

Actor::AnimationEvent( object )

  • Like 1
Link to comment

So you can't update bone position after the animation? How can you make a character look at the player?

  • Upvote 1
Link to comment
23 minutes ago, Genebris said:

So you can't update bone position after the animation? How can you make a character look at the player?

Good question. I suppose adjustments could be made to the skeleton after the results are received in the World::Update() call before the call to World::Render(), but this would only affect the main copy of the skeleton. The animation thread would already be working on the next frame. (This is probably okay.) Or maybe an Animate() actor function could be called inside the Workd::Update command, after the last frame is received and before the animation thread resumes.

You never had to even think about any of these issues in LE4 but to achieve massive scalability we have to make some restrictions.

The animation thread has 33 milliseconds to complete. How many characters can we animate in that time? :rolleyes: I wonder.

Link to comment
7 hours ago, Rick said:

For me the biggest thing would be getting the feedback to know what "frame" (I know it's not frame based) the animation is on as that's when you run certain logic to sync things to an animation. Would we get that if another thread was doing the animation from the main thread?

I don't code in Lua but can't you get this by getting how much time passed since the animation started (a variable you would store when you start the animation)?

Link to comment

Yes, in Le 4. I was asking about le 5 animation seeing as it seemed like our feedback of the animation was going to be more limited. Normally I use the animation timing not just a timer that isn’t synced with the animation. 

Link to comment

I hope to have some benchmarks later this week.  I think the changes I’ve made will move us forward to the next bottlenec. I am not sure yet what that will be.

My design approach that seems to be working really well so far is to optimize everything possible, identify the remaining bottleneck, and then come up with a creative solution that totally sidesteps that choke point. This typically results in an order of magnitude performance gain.

Link to comment
Guest
Add a comment...

×   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.

×
×
  • Create New...