RFC: Hiearchical Animation Timeline in luma.gl
April 16, 2020 ยท View on GitHub
- Author: Tarek Sherif
- Date: April, 2019
- Status: Implemented
Summary
This RFC specifies a timeline management system to facilitate coordination of disparate animation systems in a way that is easily controlled by the application.
Background
luma.gl and deck.gl currently support a variety of animation types such as deck.gl attribute transitions and glTF asset animations. Currently, these animations are all driven by the luma.gl AnimationLoop's time animation property, which essentially maps to wall time. The problem with this architecture is twofold:
- Applications have no control over animations.
- There is no way to coordinate multiple animations from these various systems. Application control would further exacerbate this, as all animations would have to independently track the current "animation time".
Animation systems generally consist of the of the following components, either implicitly or explicitly:
- A
timevalue that is used as input to the system. - A
targetvalue that is updated based ontime, such as a uniform value or transform parameter. The update can be a simple simulation based directly ontimeor a more complex system involving interpolation between key frames using a function such as linear or bezier interpolation.
The timeline management system proposed in this RFC addresses the problem of coordinating animations solely by manipulating the first component, input time. A simple system that introduces application control of this value and allows it to be remapped using simple offset and scaling operations would allow for significantly richer animations to be used in deck.gl and luma.gl.
Customers
The Elevate team has requested the ability to control transitions in deck.gl, specifically the ability to pause, play and scrub through an attribute transition.
Overview
A timeline manager that can provide time values to be used in animations that are independent of wall time. The timeline manager should support the following features:
- play: provide a
timevalue that elapses at the same rate as wall time - pause:
timeremains constant at the current value - set: set
timeto a specific value - multiple
channelsthat providechannelTimevalues, related totime, but with the following additional properties (all optional):rate: (default1) a scaling factor that indicates how quicklychannelTimeelapses relative totimedelay: (default0) an offset intotimeat whichchannelTimebegins elapsing (in units oftime)duration: (defaultInfinity) how longchannelTimeruns for (in units oftime)repeat: (default1) Number of times to repeatchannelTime(only meaningful ifdurationhas been set)
The channels provide a mechanism for orchestrating complex animations that elapse differently but all relative to the same base timeline.
Example
A timeline with two channels:
- Channel 1:
- rate: 0.5
- delay: 1
- duration: 4
- repeat: 3
- Channel 2:
- rate: 2
- delay: 5
- duration: 5
- repeat: 1
pause play
| |
Wall time: 0----5----10----15----20
time: 0----5 5----10----15
channelTime 1: 0---2 2---2---2
channelTime 2: 0----10
Implementation
AnimatonLoop will have a new timeline property which is an instance of the class Timeline. The Timeline class will provide the following methods:
play: elapsetimeautomatically with wall timepause: stop elapsingtimeautomatically with wall timereset: settimeto 0setTime(time): settimeto a specific valuegetTime(handle): get the currentchannelTimefrom thechannelindicated byhandle. If no handle provided, get currenttimeaddChannel(props): create a new channel with given properties and return a handle to itremoveChannel(handle): remove a channel from the timeline
The AnimationLoop will update timeline each frame with the current engineTime (time since startup), which the timeline can use to update time if it is playing.
The time property provided in animationProps will be the value returned by AnimationLoop.timeline.getTime(). This will ensure that all animations tracking animationProps.time will follow timeline controls rather than wall time.
AnimationLoop.timeline will also be passed in animatonProps so that applications can easily manipulate it.
Integration with GLTFAnimaton
Integration with GLTFAnimationshould amount to simply passing the timeline object, and optionally a channel handle to the constructor. Then the animate method would simply use timeline.getTime() to update rather than receiving the timeMs argument.
Significant advantages of this approach over the current one are that animation become controlable by the application and it becomes straightforward to orchestrate multiple glTF animations of arbitrary duration into customizable application-defined animation sequences.