blob: 9fcf53306879f1f4855528bc886f349a567b640d [file] [log] [blame]
// 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 <unordered_map>
#include "flutter/fml/time/time_point.h"
#include "impeller/base/timing.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(
const std::shared_ptr<Animation>& animation,
Node* bind_target) {
if (!animation) {
VALIDATION_LOG << "Cannot add null animation.";
return nullptr;
}
AnimationClip clip(animation, bind_target);
// Record all of the unique default transforms that this AnimationClip
// will mutate.
for (const auto& binding : clip.bindings_) {
auto decomp = binding.node->GetLocalTransform().Decompose();
if (!decomp.has_value()) {
continue;
}
target_transforms_.insert(
{binding.node, AnimationTransforms{.bind_pose = decomp.value()}});
}
auto result = clips_.insert({animation->GetName(), std::move(clip)});
return &result.first->second;
}
AnimationClip* AnimationPlayer::GetClip(const std::string& name) const {
auto result = clips_.find(name);
if (result == clips_.end()) {
return nullptr;
}
return const_cast<AnimationClip*>(&result->second);
}
void AnimationPlayer::Update() {
if (!previous_time_.has_value()) {
previous_time_ = Clock::now();
}
auto new_time = Clock::now();
auto delta_time = new_time - previous_time_.value();
previous_time_ = new_time;
// Reset the animated pose state.
for (auto& [node, transforms] : target_transforms_) {
transforms.animated_pose = transforms.bind_pose;
}
// Compute a weight multiplier for normalizing the animation.
Scalar total_weight = 0;
for (auto& [_, clip] : clips_) {
total_weight += clip.GetWeight();
}
Scalar weight_multiplier = total_weight > 1 ? 1 / total_weight : 1;
// Update and apply all clips to the animation pose state.
for (auto& [_, clip] : clips_) {
clip.Advance(delta_time);
clip.ApplyToBindings(target_transforms_, weight_multiplier);
}
// Apply the animated pose to the bound joints.
for (auto& [node, transforms] : target_transforms_) {
node->SetLocalTransform(Matrix(transforms.animated_pose));
}
}
} // namespace scene
} // namespace impeller