This PR has become pretty chonky unfortunately, and ended up bundling a few unrelated changes (my hobby programming became a bit disorganized while I was changing jobs, but things have stabilized now). I'll stick to a quick overview of the changes here, the release notes will have more detailed explanations.
Independent time signals, or the animate everything update: This is a foundational change that was important to lay the groundwork for animation state machines. Conceptually, the animation graph offers a way of describing a way of processing data that changes with time, but in the previous version only pose data could change with time. That is, the backwards propagation of time deltas was inherently linked to the forward propagation of animation pose data. Other data, such as f32, vec3 or other edges could not change with time (unless they were changed by gameplay logic and passed as graph input parameters). This prevented legitimately useful constructs such as dynamic interpolation nodes between f32 or vec3 values, using for example a dampened spring-mass system to interpolate between the current and target values when the target can change dynamically. Such interpolation nodes would not output any animation pose data, but would need to receive a time delta in order to update the current value. Another example is event queues, which should be able to change over time (e.g. foot-down, foot-up events coming from an animation clip node): we wouldn't want to bundle this with animation pose data, because that would complicate the handling of events fed into the graph by gameplay logic (e.g., gameplay logic telling the graph that the character is now jumping via an event).
The solution we settled upon was to introduce separate time edges that control the backwards propagation of time deltas, and to stop considering animation pose data to be separate to other kinds of data. Now it's simple, "data" flows forward while "time" flows backwards. This is a bit unique among animation graph systems in popular engines and it does introduce some additional friction for simple operations (now you need to connect both a time and animation pose data edge rather than a single one) but it enables a lot of configurations that were difficult/impossible before. I'm looking forward to see how it works out.
Animation State Machines: This is a big one. There were certain things that were difficult to handle previously with the existing animation graph systems. For example, to switch between jumping and movement animations would require passing some sort of blend weight from the gameplay side of things. Now imagine you also have a aiming state and idle animations... you could end up managing an ad-hoc state machine in your gameplay logic, which would need to handle the transitions between states gracefully and without visual cuts. Ideally, the gameplay logic would indicate only which state the animation should go into via animation events fed into the graph, and the graph would handle states and transitions on its own. There might still be a state machine on the gameplay side, but only for gameplay state and it wouldn't care about animation states and transitions.
Well, this is exactly what we started to introduce here. State machines are represented as nodes within an animation graph. They have a separate editor tab with their own UI for editing. Each state is assigned an animation graph, and while that state is active the assigned animation graph will drive the output. Each transition is also assigned an animation graph, but this animation graph can query the respective assigned animation graphs for the transition's source and target states (e.g. to do blending). The transition animation graph is also fed data about the duration and elapsed percentage/time in the transition, which is useful for blending. Finally, the animation graph handling the transition is responsible for emitting events declaring the transition over.
Arbitrary inputs can be fed into a state machine node, and these inputs will be forwarded to all active states/transitions (a common application is feeding character speed down to the state that handles locomotion animations).
Improve editor UI: The previous editor UI did the job, but was not very enjoyable to use. While there is still a lot to improve, nodes now show runtime information (duration, logical time), active nodes are highlighted (i.e. nodes that were used in the current frame) and pins are divided into two colums (input and output columns) to improve readability at a glance.
This PR has become pretty chonky unfortunately, and ended up bundling a few unrelated changes (my hobby programming became a bit disorganized while I was changing jobs, but things have stabilized now). I'll stick to a quick overview of the changes here, the release notes will have more detailed explanations.
f32
,vec3
or other edges could not change with time (unless they were changed by gameplay logic and passed as graph input parameters). This prevented legitimately useful constructs such as dynamic interpolation nodes betweenf32
orvec3
values, using for example a dampened spring-mass system to interpolate between the current and target values when the target can change dynamically. Such interpolation nodes would not output any animation pose data, but would need to receive a time delta in order to update the current value. Another example is event queues, which should be able to change over time (e.g. foot-down, foot-up events coming from an animation clip node): we wouldn't want to bundle this with animation pose data, because that would complicate the handling of events fed into the graph by gameplay logic (e.g., gameplay logic telling the graph that the character is now jumping via an event). The solution we settled upon was to introduce separate time edges that control the backwards propagation of time deltas, and to stop considering animation pose data to be separate to other kinds of data. Now it's simple, "data" flows forward while "time" flows backwards. This is a bit unique among animation graph systems in popular engines and it does introduce some additional friction for simple operations (now you need to connect both a time and animation pose data edge rather than a single one) but it enables a lot of configurations that were difficult/impossible before. I'm looking forward to see how it works out.