Skip to content

Commit ec6ec68

Browse files
bderoloic-sharma
authored andcommitted
[Impeller Scene] Animation binding and playback (flutter#38595)
1 parent 1a83071 commit ec6ec68

13 files changed

+877
-16
lines changed

ci/licenses_golden/licenses_flutter

+16
Original file line numberDiff line numberDiff line change
@@ -1607,6 +1607,14 @@ ORIGIN: ../../../flutter/impeller/runtime_stage/runtime_stage_playground.cc + ..
16071607
ORIGIN: ../../../flutter/impeller/runtime_stage/runtime_stage_playground.h + ../../../flutter/LICENSE
16081608
ORIGIN: ../../../flutter/impeller/runtime_stage/runtime_types.cc + ../../../flutter/LICENSE
16091609
ORIGIN: ../../../flutter/impeller/runtime_stage/runtime_types.h + ../../../flutter/LICENSE
1610+
ORIGIN: ../../../flutter/impeller/scene/animation/animation.cc + ../../../flutter/LICENSE
1611+
ORIGIN: ../../../flutter/impeller/scene/animation/animation.h + ../../../flutter/LICENSE
1612+
ORIGIN: ../../../flutter/impeller/scene/animation/animation_clip.cc + ../../../flutter/LICENSE
1613+
ORIGIN: ../../../flutter/impeller/scene/animation/animation_clip.h + ../../../flutter/LICENSE
1614+
ORIGIN: ../../../flutter/impeller/scene/animation/animation_player.cc + ../../../flutter/LICENSE
1615+
ORIGIN: ../../../flutter/impeller/scene/animation/animation_player.h + ../../../flutter/LICENSE
1616+
ORIGIN: ../../../flutter/impeller/scene/animation/property_resolver.cc + ../../../flutter/LICENSE
1617+
ORIGIN: ../../../flutter/impeller/scene/animation/property_resolver.h + ../../../flutter/LICENSE
16101618
ORIGIN: ../../../flutter/impeller/scene/camera.cc + ../../../flutter/LICENSE
16111619
ORIGIN: ../../../flutter/impeller/scene/camera.h + ../../../flutter/LICENSE
16121620
ORIGIN: ../../../flutter/impeller/scene/geometry.cc + ../../../flutter/LICENSE
@@ -4067,6 +4075,14 @@ FILE: ../../../flutter/impeller/runtime_stage/runtime_stage_playground.cc
40674075
FILE: ../../../flutter/impeller/runtime_stage/runtime_stage_playground.h
40684076
FILE: ../../../flutter/impeller/runtime_stage/runtime_types.cc
40694077
FILE: ../../../flutter/impeller/runtime_stage/runtime_types.h
4078+
FILE: ../../../flutter/impeller/scene/animation/animation.cc
4079+
FILE: ../../../flutter/impeller/scene/animation/animation.h
4080+
FILE: ../../../flutter/impeller/scene/animation/animation_clip.cc
4081+
FILE: ../../../flutter/impeller/scene/animation/animation_clip.h
4082+
FILE: ../../../flutter/impeller/scene/animation/animation_player.cc
4083+
FILE: ../../../flutter/impeller/scene/animation/animation_player.h
4084+
FILE: ../../../flutter/impeller/scene/animation/property_resolver.cc
4085+
FILE: ../../../flutter/impeller/scene/animation/property_resolver.h
40704086
FILE: ../../../flutter/impeller/scene/camera.cc
40714087
FILE: ../../../flutter/impeller/scene/camera.h
40724088
FILE: ../../../flutter/impeller/scene/geometry.cc

impeller/scene/BUILD.gn

+8
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,14 @@ import("//flutter/impeller/tools/impeller.gni")
66

77
impeller_component("scene") {
88
sources = [
9+
"animation/animation.cc",
10+
"animation/animation.h",
11+
"animation/animation_clip.cc",
12+
"animation/animation_clip.h",
13+
"animation/animation_player.cc",
14+
"animation/animation_player.h",
15+
"animation/property_resolver.cc",
16+
"animation/property_resolver.h",
917
"camera.cc",
1018
"camera.h",
1119
"geometry.cc",

impeller/scene/animation/animation.cc

+125
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
#include "impeller/scene/animation/animation.h"
6+
7+
#include <algorithm>
8+
#include <cstring>
9+
#include <memory>
10+
#include <vector>
11+
12+
#include "impeller/geometry/quaternion.h"
13+
#include "impeller/scene/importer/scene_flatbuffers.h"
14+
#include "impeller/scene/node.h"
15+
16+
namespace impeller {
17+
namespace scene {
18+
19+
std::shared_ptr<Animation> Animation::MakeFromFlatbuffer(
20+
const fb::Animation& animation,
21+
const std::vector<std::shared_ptr<Node>>& scene_nodes) {
22+
auto result = std::shared_ptr<Animation>(new Animation());
23+
24+
result->name_ = animation.name()->str();
25+
for (auto channel : *animation.channels()) {
26+
if (channel->node() < 0 ||
27+
static_cast<size_t>(channel->node()) >= scene_nodes.size() ||
28+
!channel->timeline()) {
29+
continue;
30+
}
31+
32+
Animation::Channel out_channel;
33+
out_channel.bind_target.node_name = scene_nodes[channel->node()]->GetName();
34+
35+
auto* times = channel->timeline();
36+
std::vector<Scalar> out_times;
37+
out_times.resize(channel->timeline()->size());
38+
std::copy(times->begin(), times->end(), out_times.begin());
39+
40+
// TODO(bdero): Why are the entries in the keyframe value arrays not
41+
// contiguous in the flatbuffer? We should be able to get rid
42+
// of the subloops below and just memcpy instead.
43+
switch (channel->keyframes_type()) {
44+
case fb::Keyframes::TranslationKeyframes: {
45+
out_channel.bind_target.property = Animation::Property::kTranslation;
46+
auto* keyframes = channel->keyframes_as_TranslationKeyframes();
47+
if (!keyframes->values()) {
48+
continue;
49+
}
50+
std::vector<Vector3> out_values;
51+
out_values.resize(keyframes->values()->size());
52+
for (size_t value_i = 0; value_i < keyframes->values()->size();
53+
value_i++) {
54+
auto val = (*keyframes->values())[value_i];
55+
out_values[value_i] = Vector3(val->x(), val->y(), val->z());
56+
}
57+
out_channel.resolver = PropertyResolver::MakeTranslationTimeline(
58+
std::move(out_times), std::move(out_values));
59+
break;
60+
}
61+
case fb::Keyframes::RotationKeyframes: {
62+
out_channel.bind_target.property = Animation::Property::kRotation;
63+
auto* keyframes = channel->keyframes_as_RotationKeyframes();
64+
if (!keyframes->values()) {
65+
continue;
66+
}
67+
std::vector<Quaternion> out_values;
68+
out_values.resize(keyframes->values()->size());
69+
for (size_t value_i = 0; value_i < keyframes->values()->size();
70+
value_i++) {
71+
auto val = (*keyframes->values())[value_i];
72+
out_values[value_i] =
73+
Quaternion(val->x(), val->y(), val->z(), val->w());
74+
}
75+
out_channel.resolver = PropertyResolver::MakeRotationTimeline(
76+
std::move(out_times), std::move(out_values));
77+
break;
78+
}
79+
case fb::Keyframes::ScaleKeyframes: {
80+
out_channel.bind_target.property = Animation::Property::kScale;
81+
auto* keyframes = channel->keyframes_as_ScaleKeyframes();
82+
if (!keyframes->values()) {
83+
continue;
84+
}
85+
std::vector<Vector3> out_values;
86+
out_values.resize(keyframes->values()->size());
87+
for (size_t value_i = 0; value_i < keyframes->values()->size();
88+
value_i++) {
89+
auto val = (*keyframes->values())[value_i];
90+
out_values[value_i] = Vector3(val->x(), val->y(), val->z());
91+
}
92+
out_channel.resolver = PropertyResolver::MakeScaleTimeline(
93+
std::move(out_times), std::move(out_values));
94+
break;
95+
}
96+
case fb::Keyframes::NONE:
97+
continue;
98+
}
99+
100+
result->end_time_ =
101+
std::max(result->end_time_, out_channel.resolver->GetEndTime());
102+
result->channels_.push_back(std::move(out_channel));
103+
}
104+
105+
return result;
106+
}
107+
108+
Animation::Animation() = default;
109+
110+
Animation::~Animation() = default;
111+
112+
const std::string& Animation::GetName() const {
113+
return name_;
114+
}
115+
116+
const std::vector<Animation::Channel>& Animation::GetChannels() const {
117+
return channels_;
118+
}
119+
120+
Scalar Animation::GetEndTime() const {
121+
return end_time_;
122+
}
123+
124+
} // namespace scene
125+
} // namespace impeller

impeller/scene/animation/animation.h

+76
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
#pragma once
6+
7+
#include <memory>
8+
#include <string>
9+
#include <vector>
10+
11+
#include "flutter/fml/hash_combine.h"
12+
#include "flutter/fml/macros.h"
13+
#include "impeller/geometry/quaternion.h"
14+
#include "impeller/geometry/scalar.h"
15+
#include "impeller/geometry/vector.h"
16+
#include "impeller/scene/animation/property_resolver.h"
17+
#include "impeller/scene/importer/scene_flatbuffers.h"
18+
19+
namespace impeller {
20+
namespace scene {
21+
22+
class Node;
23+
24+
class Animation final {
25+
public:
26+
static std::shared_ptr<Animation> MakeFromFlatbuffer(
27+
const fb::Animation& animation,
28+
const std::vector<std::shared_ptr<Node>>& scene_nodes);
29+
30+
enum class Property {
31+
kTranslation,
32+
kRotation,
33+
kScale,
34+
};
35+
36+
struct BindKey {
37+
std::string node_name;
38+
Property property = Property::kTranslation;
39+
40+
struct Hash {
41+
std::size_t operator()(const BindKey& o) const {
42+
return fml::HashCombine(o.node_name, o.property);
43+
}
44+
};
45+
46+
struct Equal {
47+
bool operator()(const BindKey& lhs, const BindKey& rhs) const {
48+
return lhs.node_name == rhs.node_name && lhs.property == rhs.property;
49+
}
50+
};
51+
};
52+
53+
struct Channel {
54+
BindKey bind_target;
55+
std::unique_ptr<PropertyResolver> resolver;
56+
};
57+
~Animation();
58+
59+
const std::string& GetName() const;
60+
61+
const std::vector<Channel>& GetChannels() const;
62+
63+
Scalar GetEndTime() const;
64+
65+
private:
66+
Animation();
67+
68+
std::string name_;
69+
std::vector<Channel> channels_;
70+
Scalar end_time_ = 0;
71+
72+
FML_DISALLOW_COPY_AND_ASSIGN(Animation);
73+
};
74+
75+
} // namespace scene
76+
} // namespace impeller
+136
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
#include "impeller/scene/animation/animation_clip.h"
6+
7+
#include <algorithm>
8+
#include <cmath>
9+
#include <memory>
10+
#include <valarray>
11+
12+
#include "impeller/scene/node.h"
13+
14+
namespace impeller {
15+
namespace scene {
16+
17+
AnimationClip::AnimationClip(std::shared_ptr<Animation> animation,
18+
Node* bind_target)
19+
: animation_(std::move(animation)) {
20+
BindToTarget(bind_target);
21+
}
22+
23+
AnimationClip::~AnimationClip() = default;
24+
25+
AnimationClip::AnimationClip(AnimationClip&&) = default;
26+
AnimationClip& AnimationClip::operator=(AnimationClip&&) = default;
27+
28+
bool AnimationClip::IsPlaying() const {
29+
return playing_;
30+
}
31+
32+
void AnimationClip::SetPlaying(bool playing) {
33+
playing_ = playing;
34+
}
35+
36+
void AnimationClip::Play() {
37+
SetPlaying(true);
38+
}
39+
40+
void AnimationClip::Pause() {
41+
SetPlaying(false);
42+
}
43+
44+
void AnimationClip::Stop() {
45+
SetPlaying(false);
46+
Seek(0);
47+
}
48+
49+
bool AnimationClip::GetLoop() const {
50+
return loop_;
51+
}
52+
53+
void AnimationClip::SetLoop(bool looping) {
54+
loop_ = looping;
55+
}
56+
57+
Scalar AnimationClip::GetPlaybackTimeScale() const {
58+
return playback_time_scale_;
59+
}
60+
61+
void AnimationClip::SetPlaybackTimeScale(Scalar playback_speed) {
62+
playback_time_scale_ = playback_speed;
63+
}
64+
65+
Scalar AnimationClip::GetWeight() const {
66+
return weight_;
67+
}
68+
69+
void AnimationClip::SetWeight(Scalar weight) {
70+
weight_ = weight;
71+
}
72+
73+
Scalar AnimationClip::GetPlaybackTime() const {
74+
return playback_time_;
75+
}
76+
77+
void AnimationClip::Seek(Scalar time) {
78+
playback_time_ = std::clamp(time, 0.0f, animation_->GetEndTime());
79+
}
80+
81+
void AnimationClip::Advance(Scalar delta_time) {
82+
if (!playing_ || delta_time <= 0) {
83+
return;
84+
}
85+
delta_time *= playback_time_scale_;
86+
playback_time_ += delta_time;
87+
88+
/// Handle looping behavior.
89+
90+
Scalar end_time = animation_->GetEndTime();
91+
if (end_time == 0) {
92+
playback_time_ = 0;
93+
return;
94+
}
95+
if (!loop_ && (playback_time_ < 0 || playback_time_ > end_time)) {
96+
// If looping is disabled, clamp to the end (or beginning, if playing in
97+
// reverse) and pause.
98+
Pause();
99+
playback_time_ = std::clamp(playback_time_, 0.0f, end_time);
100+
} else if (/* loop && */ playback_time_ > end_time) {
101+
// If looping is enabled and we ran off the end, loop to the beginning.
102+
playback_time_ = std::fmod(std::abs(playback_time_), end_time);
103+
} else if (/* loop && */ playback_time_ < 0) {
104+
// If looping is enabled and we ran off the beginning, loop to the end.
105+
playback_time_ = end_time - std::fmod(std::abs(playback_time_), end_time);
106+
}
107+
}
108+
109+
void AnimationClip::ApplyToBindings() const {
110+
for (auto& binding : bindings_) {
111+
binding.channel.resolver->Apply(*binding.node, playback_time_, weight_);
112+
}
113+
}
114+
115+
void AnimationClip::BindToTarget(Node* node) {
116+
const auto& channels = animation_->GetChannels();
117+
bindings_.clear();
118+
bindings_.reserve(channels.size());
119+
120+
for (const auto& channel : channels) {
121+
Node* channel_target;
122+
if (channel.bind_target.node_name == node->GetName()) {
123+
channel_target = node;
124+
} else if (auto result =
125+
node->FindChildByName(channel.bind_target.node_name, true)) {
126+
channel_target = result.get();
127+
} else {
128+
continue;
129+
}
130+
bindings_.push_back(
131+
ChannelBinding{.channel = channel, .node = channel_target});
132+
}
133+
}
134+
135+
} // namespace scene
136+
} // namespace impeller

0 commit comments

Comments
 (0)