[Impeller Scene] Animation binding and playback (flutter/engine#38595)

This commit is contained in:
Brandon DeRosier 2023-01-02 17:07:20 -08:00 committed by GitHub
parent 25975b721f
commit ef04e885ab
13 changed files with 877 additions and 16 deletions

View File

@ -1607,6 +1607,14 @@ ORIGIN: ../../../flutter/impeller/runtime_stage/runtime_stage_playground.cc + ..
ORIGIN: ../../../flutter/impeller/runtime_stage/runtime_stage_playground.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/runtime_stage/runtime_types.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/runtime_stage/runtime_types.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/scene/animation/animation.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/scene/animation/animation.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/scene/animation/animation_clip.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/scene/animation/animation_clip.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/scene/animation/animation_player.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/scene/animation/animation_player.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/scene/animation/property_resolver.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/scene/animation/property_resolver.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/scene/camera.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/scene/camera.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/scene/geometry.cc + ../../../flutter/LICENSE
@ -4067,6 +4075,14 @@ FILE: ../../../flutter/impeller/runtime_stage/runtime_stage_playground.cc
FILE: ../../../flutter/impeller/runtime_stage/runtime_stage_playground.h
FILE: ../../../flutter/impeller/runtime_stage/runtime_types.cc
FILE: ../../../flutter/impeller/runtime_stage/runtime_types.h
FILE: ../../../flutter/impeller/scene/animation/animation.cc
FILE: ../../../flutter/impeller/scene/animation/animation.h
FILE: ../../../flutter/impeller/scene/animation/animation_clip.cc
FILE: ../../../flutter/impeller/scene/animation/animation_clip.h
FILE: ../../../flutter/impeller/scene/animation/animation_player.cc
FILE: ../../../flutter/impeller/scene/animation/animation_player.h
FILE: ../../../flutter/impeller/scene/animation/property_resolver.cc
FILE: ../../../flutter/impeller/scene/animation/property_resolver.h
FILE: ../../../flutter/impeller/scene/camera.cc
FILE: ../../../flutter/impeller/scene/camera.h
FILE: ../../../flutter/impeller/scene/geometry.cc

View File

@ -6,6 +6,14 @@ import("//flutter/impeller/tools/impeller.gni")
impeller_component("scene") {
sources = [
"animation/animation.cc",
"animation/animation.h",
"animation/animation_clip.cc",
"animation/animation_clip.h",
"animation/animation_player.cc",
"animation/animation_player.h",
"animation/property_resolver.cc",
"animation/property_resolver.h",
"camera.cc",
"camera.h",
"geometry.cc",

View File

@ -0,0 +1,125 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "impeller/scene/animation/animation.h"
#include <algorithm>
#include <cstring>
#include <memory>
#include <vector>
#include "impeller/geometry/quaternion.h"
#include "impeller/scene/importer/scene_flatbuffers.h"
#include "impeller/scene/node.h"
namespace impeller {
namespace scene {
std::shared_ptr<Animation> Animation::MakeFromFlatbuffer(
const fb::Animation& animation,
const std::vector<std::shared_ptr<Node>>& scene_nodes) {
auto result = std::shared_ptr<Animation>(new Animation());
result->name_ = animation.name()->str();
for (auto channel : *animation.channels()) {
if (channel->node() < 0 ||
static_cast<size_t>(channel->node()) >= scene_nodes.size() ||
!channel->timeline()) {
continue;
}
Animation::Channel out_channel;
out_channel.bind_target.node_name = scene_nodes[channel->node()]->GetName();
auto* times = channel->timeline();
std::vector<Scalar> out_times;
out_times.resize(channel->timeline()->size());
std::copy(times->begin(), times->end(), out_times.begin());
// TODO(bdero): Why are the entries in the keyframe value arrays not
// contiguous in the flatbuffer? We should be able to get rid
// of the subloops below and just memcpy instead.
switch (channel->keyframes_type()) {
case fb::Keyframes::TranslationKeyframes: {
out_channel.bind_target.property = Animation::Property::kTranslation;
auto* keyframes = channel->keyframes_as_TranslationKeyframes();
if (!keyframes->values()) {
continue;
}
std::vector<Vector3> out_values;
out_values.resize(keyframes->values()->size());
for (size_t value_i = 0; value_i < keyframes->values()->size();
value_i++) {
auto val = (*keyframes->values())[value_i];
out_values[value_i] = Vector3(val->x(), val->y(), val->z());
}
out_channel.resolver = PropertyResolver::MakeTranslationTimeline(
std::move(out_times), std::move(out_values));
break;
}
case fb::Keyframes::RotationKeyframes: {
out_channel.bind_target.property = Animation::Property::kRotation;
auto* keyframes = channel->keyframes_as_RotationKeyframes();
if (!keyframes->values()) {
continue;
}
std::vector<Quaternion> out_values;
out_values.resize(keyframes->values()->size());
for (size_t value_i = 0; value_i < keyframes->values()->size();
value_i++) {
auto val = (*keyframes->values())[value_i];
out_values[value_i] =
Quaternion(val->x(), val->y(), val->z(), val->w());
}
out_channel.resolver = PropertyResolver::MakeRotationTimeline(
std::move(out_times), std::move(out_values));
break;
}
case fb::Keyframes::ScaleKeyframes: {
out_channel.bind_target.property = Animation::Property::kScale;
auto* keyframes = channel->keyframes_as_ScaleKeyframes();
if (!keyframes->values()) {
continue;
}
std::vector<Vector3> out_values;
out_values.resize(keyframes->values()->size());
for (size_t value_i = 0; value_i < keyframes->values()->size();
value_i++) {
auto val = (*keyframes->values())[value_i];
out_values[value_i] = Vector3(val->x(), val->y(), val->z());
}
out_channel.resolver = PropertyResolver::MakeScaleTimeline(
std::move(out_times), std::move(out_values));
break;
}
case fb::Keyframes::NONE:
continue;
}
result->end_time_ =
std::max(result->end_time_, out_channel.resolver->GetEndTime());
result->channels_.push_back(std::move(out_channel));
}
return result;
}
Animation::Animation() = default;
Animation::~Animation() = default;
const std::string& Animation::GetName() const {
return name_;
}
const std::vector<Animation::Channel>& Animation::GetChannels() const {
return channels_;
}
Scalar Animation::GetEndTime() const {
return end_time_;
}
} // namespace scene
} // namespace impeller

View File

@ -0,0 +1,76 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#pragma once
#include <memory>
#include <string>
#include <vector>
#include "flutter/fml/hash_combine.h"
#include "flutter/fml/macros.h"
#include "impeller/geometry/quaternion.h"
#include "impeller/geometry/scalar.h"
#include "impeller/geometry/vector.h"
#include "impeller/scene/animation/property_resolver.h"
#include "impeller/scene/importer/scene_flatbuffers.h"
namespace impeller {
namespace scene {
class Node;
class Animation final {
public:
static std::shared_ptr<Animation> MakeFromFlatbuffer(
const fb::Animation& animation,
const std::vector<std::shared_ptr<Node>>& scene_nodes);
enum class Property {
kTranslation,
kRotation,
kScale,
};
struct BindKey {
std::string node_name;
Property property = Property::kTranslation;
struct Hash {
std::size_t operator()(const BindKey& o) const {
return fml::HashCombine(o.node_name, o.property);
}
};
struct Equal {
bool operator()(const BindKey& lhs, const BindKey& rhs) const {
return lhs.node_name == rhs.node_name && lhs.property == rhs.property;
}
};
};
struct Channel {
BindKey bind_target;
std::unique_ptr<PropertyResolver> resolver;
};
~Animation();
const std::string& GetName() const;
const std::vector<Channel>& GetChannels() const;
Scalar GetEndTime() const;
private:
Animation();
std::string name_;
std::vector<Channel> channels_;
Scalar end_time_ = 0;
FML_DISALLOW_COPY_AND_ASSIGN(Animation);
};
} // namespace scene
} // namespace impeller

View File

@ -0,0 +1,136 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "impeller/scene/animation/animation_clip.h"
#include <algorithm>
#include <cmath>
#include <memory>
#include <valarray>
#include "impeller/scene/node.h"
namespace impeller {
namespace scene {
AnimationClip::AnimationClip(std::shared_ptr<Animation> animation,
Node* bind_target)
: animation_(std::move(animation)) {
BindToTarget(bind_target);
}
AnimationClip::~AnimationClip() = default;
AnimationClip::AnimationClip(AnimationClip&&) = default;
AnimationClip& AnimationClip::operator=(AnimationClip&&) = default;
bool AnimationClip::IsPlaying() const {
return playing_;
}
void AnimationClip::SetPlaying(bool playing) {
playing_ = playing;
}
void AnimationClip::Play() {
SetPlaying(true);
}
void AnimationClip::Pause() {
SetPlaying(false);
}
void AnimationClip::Stop() {
SetPlaying(false);
Seek(0);
}
bool AnimationClip::GetLoop() const {
return loop_;
}
void AnimationClip::SetLoop(bool looping) {
loop_ = looping;
}
Scalar AnimationClip::GetPlaybackTimeScale() const {
return playback_time_scale_;
}
void AnimationClip::SetPlaybackTimeScale(Scalar playback_speed) {
playback_time_scale_ = playback_speed;
}
Scalar AnimationClip::GetWeight() const {
return weight_;
}
void AnimationClip::SetWeight(Scalar weight) {
weight_ = weight;
}
Scalar AnimationClip::GetPlaybackTime() const {
return playback_time_;
}
void AnimationClip::Seek(Scalar time) {
playback_time_ = std::clamp(time, 0.0f, animation_->GetEndTime());
}
void AnimationClip::Advance(Scalar delta_time) {
if (!playing_ || delta_time <= 0) {
return;
}
delta_time *= playback_time_scale_;
playback_time_ += delta_time;
/// Handle looping behavior.
Scalar end_time = animation_->GetEndTime();
if (end_time == 0) {
playback_time_ = 0;
return;
}
if (!loop_ && (playback_time_ < 0 || playback_time_ > end_time)) {
// If looping is disabled, clamp to the end (or beginning, if playing in
// reverse) and pause.
Pause();
playback_time_ = std::clamp(playback_time_, 0.0f, end_time);
} else if (/* loop && */ playback_time_ > end_time) {
// If looping is enabled and we ran off the end, loop to the beginning.
playback_time_ = std::fmod(std::abs(playback_time_), end_time);
} else if (/* loop && */ playback_time_ < 0) {
// If looping is enabled and we ran off the beginning, loop to the end.
playback_time_ = end_time - std::fmod(std::abs(playback_time_), end_time);
}
}
void AnimationClip::ApplyToBindings() const {
for (auto& binding : bindings_) {
binding.channel.resolver->Apply(*binding.node, playback_time_, weight_);
}
}
void AnimationClip::BindToTarget(Node* node) {
const auto& channels = animation_->GetChannels();
bindings_.clear();
bindings_.reserve(channels.size());
for (const auto& channel : channels) {
Node* channel_target;
if (channel.bind_target.node_name == node->GetName()) {
channel_target = node;
} else if (auto result =
node->FindChildByName(channel.bind_target.node_name, true)) {
channel_target = result.get();
} else {
continue;
}
bindings_.push_back(
ChannelBinding{.channel = channel, .node = channel_target});
}
}
} // namespace scene
} // namespace impeller

View File

@ -0,0 +1,88 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#pragma once
#include <memory>
#include <vector>
#include "flutter/fml/macros.h"
#include "impeller/scene/animation/animation.h"
namespace impeller {
namespace scene {
class Node;
class AnimationPlayer;
class AnimationClip final {
public:
AnimationClip(std::shared_ptr<Animation> animation, Node* bind_target);
~AnimationClip();
AnimationClip(AnimationClip&&);
AnimationClip& operator=(AnimationClip&&);
bool IsPlaying() const;
void SetPlaying(bool playing);
void Play();
void Pause();
void Stop();
bool GetLoop() const;
void SetLoop(bool looping);
Scalar GetPlaybackTimeScale() const;
/// @brief Sets the animation playback speed. Negative values make the clip
/// play in reverse.
void SetPlaybackTimeScale(Scalar playback_speed);
Scalar GetWeight() const;
void SetWeight(Scalar weight);
/// @brief Get the current playback time of the animation.
Scalar GetPlaybackTime() const;
/// @brief Move the animation to the specified time. The given `time` is
/// clamped to the animation's playback range.
void Seek(Scalar time);
/// @brief Advance the animation by `delta_time` seconds. Negative
/// `delta_time` values do nothing.
void Advance(Scalar delta_time);
/// @brief Applies the animation to all binded properties in the scene.
void ApplyToBindings() const;
private:
void BindToTarget(Node* node);
struct ChannelBinding {
const Animation::Channel& channel;
Node* node;
};
std::shared_ptr<Animation> animation_;
std::vector<ChannelBinding> bindings_;
Scalar playback_time_ = 0;
Scalar playback_time_scale_ = 1; // Seconds multiplier, can be negative.
Scalar weight_ = 1;
bool playing_ = false;
bool loop_ = false;
FML_DISALLOW_COPY_AND_ASSIGN(AnimationClip);
friend AnimationPlayer;
};
} // namespace scene
} // namespace impeller

View File

@ -0,0 +1,61 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "impeller/scene/animation/animation_player.h"
#include <memory>
#include "flutter/fml/time/time_point.h"
#include "impeller/scene/node.h"
namespace impeller {
namespace scene {
AnimationPlayer::AnimationPlayer() = default;
AnimationPlayer::~AnimationPlayer() = default;
AnimationPlayer::AnimationPlayer(AnimationPlayer&&) = default;
AnimationPlayer& AnimationPlayer::operator=(AnimationPlayer&&) = default;
AnimationClip& AnimationPlayer::AddAnimation(
std::shared_ptr<Animation> animation,
Node* bind_target) {
AnimationClip clip(std::move(animation), bind_target);
// Record all of the unique default transforms that this AnimationClip
// will mutate.
for (const auto& binding : clip.bindings_) {
default_target_transforms_.insert(
{binding.node, binding.node->GetLocalTransform()});
}
clips_.push_back(std::move(clip));
return clips_.back();
}
void AnimationPlayer::Update() {
if (!previous_time_.has_value()) {
previous_time_ = fml::TimePoint::Now().ToEpochDelta();
}
auto new_time = fml::TimePoint::Now().ToEpochDelta();
Scalar delta_time = (new_time - previous_time_.value()).ToSecondsF();
previous_time_ = new_time;
Reset();
// Update and apply all clips.
for (auto& clip : clips_) {
clip.Advance(delta_time);
clip.ApplyToBindings();
}
}
void AnimationPlayer::Reset() {
for (auto& [node, transform] : default_target_transforms_) {
node->SetLocalTransform(transform);
}
}
} // namespace scene
} // namespace impeller

View File

@ -0,0 +1,51 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#pragma once
#include <memory>
#include <optional>
#include <unordered_set>
#include <vector>
#include "flutter/fml/hash_combine.h"
#include "flutter/fml/macros.h"
#include "flutter/fml/time/time_delta.h"
#include "impeller/geometry/matrix.h"
#include "impeller/scene/animation/animation_clip.h"
namespace impeller {
namespace scene {
class Node;
class AnimationPlayer final {
public:
AnimationPlayer();
~AnimationPlayer();
AnimationPlayer(AnimationPlayer&&);
AnimationPlayer& operator=(AnimationPlayer&&);
AnimationClip& AddAnimation(std::shared_ptr<Animation> animation,
Node* bind_target);
/// @brief Advanced all clips and updates animated properties in the scene.
void Update();
/// @brief Reset all bound animation target transforms.
void Reset();
private:
std::unordered_map<Node*, Matrix> default_target_transforms_;
std::vector<AnimationClip> clips_;
std::optional<fml::TimeDelta> previous_time_;
FML_DISALLOW_COPY_AND_ASSIGN(AnimationPlayer);
};
} // namespace scene
} // namespace impeller

View File

@ -0,0 +1,131 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "impeller/scene/animation/property_resolver.h"
#include <algorithm>
#include <iterator>
#include <memory>
#include "impeller/geometry/point.h"
#include "impeller/scene/node.h"
namespace impeller {
namespace scene {
std::unique_ptr<TranslationTimelineResolver>
PropertyResolver::MakeTranslationTimeline(std::vector<Scalar> times,
std::vector<Vector3> values) {
FML_DCHECK(times.size() == values.size());
auto result = std::unique_ptr<TranslationTimelineResolver>(
new TranslationTimelineResolver());
result->times_ = std::move(times);
result->values_ = std::move(values);
return result;
}
std::unique_ptr<RotationTimelineResolver>
PropertyResolver::MakeRotationTimeline(std::vector<Scalar> times,
std::vector<Quaternion> values) {
FML_DCHECK(times.size() == values.size());
auto result =
std::unique_ptr<RotationTimelineResolver>(new RotationTimelineResolver());
result->times_ = std::move(times);
result->values_ = std::move(values);
return result;
}
std::unique_ptr<ScaleTimelineResolver> PropertyResolver::MakeScaleTimeline(
std::vector<Scalar> times,
std::vector<Vector3> values) {
FML_DCHECK(times.size() == values.size());
auto result =
std::unique_ptr<ScaleTimelineResolver>(new ScaleTimelineResolver());
result->times_ = std::move(times);
result->values_ = std::move(values);
return result;
}
PropertyResolver::~PropertyResolver() = default;
TimelineResolver::~TimelineResolver() = default;
Scalar TimelineResolver::GetEndTime() {
if (times_.empty()) {
return 0;
}
return times_.back();
}
TimelineResolver::TimelineKey TimelineResolver::GetTimelineKey(Scalar time) {
if (times_.size() <= 1 || time <= times_.front()) {
return {.index = 0, .lerp = 1};
}
if (time >= times_.back()) {
return {.index = times_.size() - 1, .lerp = 1};
}
auto it = std::lower_bound(times_.begin(), times_.end(), time);
size_t index = std::distance(times_.begin(), it);
Scalar previous_time = *(it - 1);
Scalar next_time = *it;
return {.index = index,
.lerp = (time - previous_time) / (next_time - previous_time)};
}
TranslationTimelineResolver::TranslationTimelineResolver() = default;
TranslationTimelineResolver::~TranslationTimelineResolver() = default;
void TranslationTimelineResolver::Apply(Node& target,
Scalar time,
Scalar weight) {
if (values_.empty()) {
return;
}
auto key = GetTimelineKey(time);
auto value = values_[key.index];
if (key.lerp < 1) {
value = values_[key.index - 1].Lerp(value, key.lerp);
}
target.SetLocalTransform(Matrix::MakeTranslation(value * weight) *
target.GetLocalTransform());
}
RotationTimelineResolver::RotationTimelineResolver() = default;
RotationTimelineResolver::~RotationTimelineResolver() = default;
void RotationTimelineResolver::Apply(Node& target, Scalar time, Scalar weight) {
if (values_.empty()) {
return;
}
auto key = GetTimelineKey(time);
auto value = values_[key.index];
if (key.lerp < 1) {
value = values_[key.index - 1].Slerp(value, key.lerp);
}
target.SetLocalTransform(Matrix::MakeRotation(value * weight) *
target.GetLocalTransform());
}
ScaleTimelineResolver::ScaleTimelineResolver() = default;
ScaleTimelineResolver::~ScaleTimelineResolver() = default;
void ScaleTimelineResolver::Apply(Node& target, Scalar time, Scalar weight) {
if (values_.empty()) {
return;
}
auto key = GetTimelineKey(time);
auto value = values_[key.index];
if (key.lerp < 1) {
value = values_[key.index - 1].Lerp(value, key.lerp);
}
target.SetLocalTransform(Matrix::MakeScale(value * weight) *
target.GetLocalTransform());
}
} // namespace scene
} // namespace impeller

View File

@ -0,0 +1,123 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#pragma once
#include <memory>
#include <string>
#include <vector>
#include "flutter/fml/hash_combine.h"
#include "flutter/fml/macros.h"
#include "impeller/geometry/quaternion.h"
#include "impeller/geometry/scalar.h"
#include "impeller/geometry/vector.h"
namespace impeller {
namespace scene {
class Node;
class TranslationTimelineResolver;
class RotationTimelineResolver;
class ScaleTimelineResolver;
class PropertyResolver {
public:
static std::unique_ptr<TranslationTimelineResolver> MakeTranslationTimeline(
std::vector<Scalar> times,
std::vector<Vector3> values);
static std::unique_ptr<RotationTimelineResolver> MakeRotationTimeline(
std::vector<Scalar> times,
std::vector<Quaternion> values);
static std::unique_ptr<ScaleTimelineResolver> MakeScaleTimeline(
std::vector<Scalar> times,
std::vector<Vector3> values);
virtual ~PropertyResolver();
virtual Scalar GetEndTime() = 0;
/// @brief Resolve and apply the property value to a target node. This
/// operation is additive; a given node property may be amended by
/// many different PropertyResolvers prior to rendering. For example,
/// an AnimationPlayer may blend multiple Animations together by
/// applying several AnimationClips.
virtual void Apply(Node& target, Scalar time, Scalar weight) = 0;
};
class TimelineResolver : public PropertyResolver {
public:
virtual ~TimelineResolver();
// |Resolver|
Scalar GetEndTime();
protected:
struct TimelineKey {
/// The index of the closest previous keyframe.
size_t index = 0;
/// Used to interpolate between the resolved values for `timeline_index - 1`
/// and `timeline_index`. The range of this value should always be `0>N>=1`.
Scalar lerp = 1;
};
TimelineKey GetTimelineKey(Scalar time);
std::vector<Scalar> times_;
};
class TranslationTimelineResolver final : public TimelineResolver {
public:
~TranslationTimelineResolver();
// |Resolver|
void Apply(Node& target, Scalar time, Scalar weight) override;
private:
TranslationTimelineResolver();
std::vector<Vector3> values_;
FML_DISALLOW_COPY_AND_ASSIGN(TranslationTimelineResolver);
friend PropertyResolver;
};
class RotationTimelineResolver final : public TimelineResolver {
public:
~RotationTimelineResolver();
// |Resolver|
void Apply(Node& target, Scalar time, Scalar weight) override;
private:
RotationTimelineResolver();
std::vector<Quaternion> values_;
FML_DISALLOW_COPY_AND_ASSIGN(RotationTimelineResolver);
friend PropertyResolver;
};
class ScaleTimelineResolver final : public TimelineResolver {
public:
~ScaleTimelineResolver();
// |Resolver|
void Apply(Node& target, Scalar time, Scalar weight) override;
private:
ScaleTimelineResolver();
std::vector<Vector3> values_;
FML_DISALLOW_COPY_AND_ASSIGN(ScaleTimelineResolver);
friend PropertyResolver;
};
} // namespace scene
} // namespace impeller

View File

@ -12,6 +12,7 @@
#include "impeller/base/strings.h"
#include "impeller/base/validation.h"
#include "impeller/geometry/matrix.h"
#include "impeller/scene/animation/animation_player.h"
#include "impeller/scene/importer/conversions.h"
#include "impeller/scene/importer/scene_flatbuffers.h"
#include "impeller/scene/mesh.h"
@ -144,7 +145,6 @@ std::shared_ptr<Node> Node::MakeFromFlatbuffer(const fb::Scene& scene,
}
result->AddChild(scene_nodes[child]);
}
// TODO(bdero): Unpack animations.
// Unpack each node.
for (size_t node_i = 0; node_i < scene.nodes()->size(); node_i++) {
@ -152,6 +152,16 @@ std::shared_ptr<Node> Node::MakeFromFlatbuffer(const fb::Scene& scene,
scene_nodes, textures, allocator);
}
// Unpack animations.
if (scene.animations()) {
for (const auto animation : *scene.animations()) {
if (auto out_animation =
Animation::MakeFromFlatbuffer(*animation, scene_nodes)) {
result->animations_.push_back(out_animation);
}
}
}
return result;
}
@ -202,10 +212,6 @@ Mesh::Mesh(Mesh&& mesh) = default;
Mesh& Mesh::operator=(Mesh&& mesh) = default;
Node::Node(Node&& node) = default;
Node& Node::operator=(Node&& node) = default;
const std::string& Node::GetName() const {
return name_;
}
@ -214,18 +220,40 @@ void Node::SetName(const std::string& new_name) {
name_ = new_name;
}
std::shared_ptr<Node> Node::FindNodeByName(const std::string& name) const {
std::shared_ptr<Node> Node::FindChildByName(
const std::string& name,
bool exclude_animation_players) const {
for (auto& child : children_) {
if (exclude_animation_players && child->animation_player_.has_value()) {
continue;
}
if (child->GetName() == name) {
return child;
}
if (auto found = child->FindNodeByName(name)) {
if (auto found = child->FindChildByName(name)) {
return found;
}
}
return nullptr;
}
std::shared_ptr<Animation> Node::FindAnimationByName(
const std::string& name) const {
for (const auto& animation : animations_) {
if (animation->GetName() == name) {
return animation;
}
}
return nullptr;
}
AnimationClip& Node::AddAnimation(const std::shared_ptr<Animation>& animation) {
if (!animation_player_.has_value()) {
animation_player_ = AnimationPlayer();
}
return animation_player_->AddAnimation(animation, this);
}
void Node::SetLocalTransform(Matrix transform) {
local_transform_ = transform;
}
@ -274,8 +302,11 @@ Mesh& Node::GetMesh() {
}
bool Node::Render(SceneEncoder& encoder, const Matrix& parent_transform) const {
Matrix transform = parent_transform * local_transform_;
if (animation_player_.has_value()) {
animation_player_->Update();
}
Matrix transform = parent_transform * local_transform_;
mesh_.Render(encoder, transform);
for (auto& child : children_) {

View File

@ -9,10 +9,12 @@
#include <vector>
#include "flutter/fml/macros.h"
#include "impeller/geometry/matrix.h"
#include "impeller/renderer/render_target.h"
#include "impeller/renderer/texture.h"
#include "impeller/scene/animation/animation.h"
#include "impeller/scene/animation/animation_clip.h"
#include "impeller/scene/animation/animation_player.h"
#include "impeller/scene/camera.h"
#include "impeller/scene/mesh.h"
#include "impeller/scene/scene_encoder.h"
@ -31,13 +33,15 @@ class Node final {
Node();
~Node();
Node(Node&& node);
Node& operator=(Node&& node);
const std::string& GetName() const;
void SetName(const std::string& new_name);
std::shared_ptr<Node> FindNodeByName(const std::string& name) const;
std::shared_ptr<Node> FindChildByName(
const std::string& name,
bool exclude_animation_players = false) const;
std::shared_ptr<Animation> FindAnimationByName(const std::string& name) const;
AnimationClip& AddAnimation(const std::shared_ptr<Animation>& animation);
void SetLocalTransform(Matrix transform);
Matrix GetLocalTransform() const;
@ -53,9 +57,6 @@ class Node final {
bool Render(SceneEncoder& encoder, const Matrix& parent_transform) const;
protected:
Matrix local_transform_;
private:
void UnpackFromFlatbuffer(
const fb::Node& node,
@ -63,12 +64,18 @@ class Node final {
const std::vector<std::shared_ptr<Texture>>& textures,
Allocator& allocator);
Matrix local_transform_;
std::string name_;
bool is_root_ = false;
Node* parent_ = nullptr;
std::vector<std::shared_ptr<Node>> children_;
Mesh mesh_;
// For convenience purposes, deserialized nodes hang onto an animation library
std::vector<std::shared_ptr<Animation>> animations_;
mutable std::optional<AnimationPlayer> animation_player_;
FML_DISALLOW_COPY_AND_ASSIGN(Node);
friend Scene;

View File

@ -17,6 +17,7 @@
#include "impeller/playground/playground.h"
#include "impeller/playground/playground_test.h"
#include "impeller/renderer/formats.h"
#include "impeller/scene/animation/animation_clip.h"
#include "impeller/scene/camera.h"
#include "impeller/scene/geometry.h"
#include "impeller/scene/importer/scene_flatbuffers.h"
@ -122,6 +123,13 @@ TEST_P(SceneTest, TwoTriangles) {
Node::MakeFromFlatbuffer(*mapping, *allocator);
ASSERT_NE(gltf_scene, nullptr);
auto animation = gltf_scene->FindAnimationByName("Metronome");
ASSERT_NE(animation, nullptr);
AnimationClip& metronome_clip = gltf_scene->AddAnimation(animation);
metronome_clip.SetLoop(true);
metronome_clip.Play();
auto scene_context = std::make_shared<SceneContext>(GetContext());
auto scene = Scene(scene_context);
scene.GetRoot().AddChild(std::move(gltf_scene));