]> git.sesse.net Git - casparcg/commitdiff
Added basic timeline/keyframe support in scene_producer
authorHelge Norberg <helge.norberg@svt.se>
Tue, 13 Aug 2013 09:25:42 +0000 (11:25 +0200)
committerHelge Norberg <helge.norberg@svt.se>
Tue, 13 Aug 2013 09:25:42 +0000 (11:25 +0200)
core/producer/binding.h
core/producer/scene/scene_producer.cpp
core/producer/scene/scene_producer.h

index 0f1e08665b0042c92d138666fdf6e00343b14295..2b1df5ba30955b03af884cd1627f49b3c11f0ae3 100644 (file)
@@ -203,6 +203,11 @@ public:
                impl_->evaluate();
        }
 
+       void* identity() const
+       {
+               return impl_.get();
+       }
+
        T get() const
        {
                return impl_->get();
@@ -515,4 +520,39 @@ public:
        }
 };
 
+template<typename T, typename T2>
+binding<T> add_tween(
+               const binding<T>& to_tween,
+               const binding<T2>& counter,
+               T destination_value,
+               T2 duration,
+               const std::wstring& easing)
+{
+       tweener tween(easing);
+       
+       double start_val = to_tween.as<double>().get();
+       double destination_val = static_cast<double>(destination_value);
+       double start_time = counter.as<double>().get();
+       double dur = static_cast<double>(duration);
+
+       return when(counter < duration)
+               .then(counter.as<double>().transformed([=](double t)
+               {
+                       return tween(t - start_time, start_val, destination_val, dur);
+               }).as<T>())
+               .otherwise(destination_value);
+}
+
+template<typename T, typename T2>
+binding<T> delay(
+               const binding<T>& to_delay,
+               const binding<T>& after_delay,
+               const binding<T2>& counter,
+               T2 duration)
+{
+       return when(counter < duration)
+                       .then(to_delay)
+                       .otherwise(after_delay);
+}
+
 }}
index 081b3daf85347f5d146006129028f0827e48a287..d8f2df0dc1089610cfbfc89df53e10055c8c399b 100644 (file)
@@ -29,6 +29,7 @@
 
 #include "../../frame/draw_frame.h"
 #include "../../interaction/interaction_aggregator.h"
+#include "../text/text_producer.h"
 
 namespace caspar { namespace core { namespace scene {
 
@@ -46,6 +47,45 @@ adjustments::adjustments()
 {
 }
 
+struct timeline
+{
+       std::map<int64_t, keyframe> keyframes;
+
+       void on_frame(int64_t frame)
+       {
+               auto before = --keyframes.upper_bound(frame);
+               bool found_before = before != keyframes.end() && before->first < frame;
+               auto after = keyframes.upper_bound(frame);
+               bool found_after = after != keyframes.end() && after->first > frame;
+               auto exact_frame = keyframes.find(frame);
+               bool found_exact_frame = exact_frame != keyframes.end();
+
+               if (found_exact_frame)
+               {
+                       exact_frame->second.on_destination_frame();
+
+                       auto next_frame = ++exact_frame;
+
+                       if (next_frame != keyframes.end() && next_frame->second.on_start_animate)
+                               next_frame->second.on_start_animate();
+               }
+               else if (found_after)
+               {
+                       int64_t start_frame = 0;
+
+                       if (found_before)
+                       {
+                               start_frame = before->first;
+                       }
+
+                       if (after->second.on_start_animate && frame == 0)
+                               after->second.on_start_animate();
+                       else if (after->second.on_animate_to)
+                               after->second.on_animate_to(start_frame, frame);
+               }
+       }
+};
+
 struct scene_producer::impl
 {
        constraints pixel_constraints_;
@@ -53,6 +93,7 @@ struct scene_producer::impl
        interaction_aggregator aggregator_;
        binding<int64_t> frame_number_;
        std::map<std::wstring, std::shared_ptr<parameter_holder_base>> parameters_;
+       std::map<void*, timeline> timelines_;
 
        impl(int width, int height)
                : pixel_constraints_(width, height)
@@ -96,6 +137,11 @@ struct scene_producer::impl
                parameters_.insert(std::make_pair(boost::to_lower_copy(name), param));
        }
 
+       void store_keyframe(void* timeline_identity, const keyframe& k)
+       {
+               timelines_[timeline_identity].keyframes.insert(std::make_pair(k.destination_frame, k));
+       }
+
        binding<int64_t> frame()
        {
                return frame_number_;
@@ -123,6 +169,9 @@ struct scene_producer::impl
 
        draw_frame render_frame()
        {
+               BOOST_FOREACH(auto& timeline, timelines_)
+                       timeline.second.on_frame(frame_number_.get());
+
                std::vector<draw_frame> frames;
 
                BOOST_FOREACH(auto& layer, layers_)
@@ -298,6 +347,11 @@ void scene_producer::store_parameter(
        impl_->store_parameter(name, param);
 }
 
+void scene_producer::store_keyframe(void* timeline_identity, const keyframe& k)
+{
+       impl_->store_keyframe(timeline_identity, k);
+}
+
 spl::shared_ptr<frame_producer> create_dummy_scene_producer(const spl::shared_ptr<class frame_factory>& frame_factory, const video_format_desc& format_desc, const std::vector<std::wstring>& params)
 {
        if (params.size() < 1 || !boost::iequals(params.at(0), L"[SCENE]"))
@@ -305,20 +359,26 @@ spl::shared_ptr<frame_producer> create_dummy_scene_producer(const spl::shared_pt
 
        auto scene = spl::make_shared<scene_producer>(format_desc.width, format_desc.height);
 
-       binding<double> text_width(10);
+       text::text_info text_info;
+       text_info.font = L"Arial";
+       text_info.size = 62;
+       text_info.color.r = 1;
+       text_info.color.g = 1;
+       text_info.color.b = 1;
+       text_info.color.a = 0.5;
+       auto text_area = text_producer::create(frame_factory, 0, 0, L"a", text_info, 1280, 720, false);
+
+       auto text_width = text_area->pixel_constraints().width;
        binding<double> padding(1);
        binding<double> panel_width = padding + text_width + padding;
-       binding<double> panel_height(50);
+       binding<double> panel_height = padding + text_area->pixel_constraints().height + padding;
 
        auto subscription = panel_width.on_change([&]
        {
                CASPAR_LOG(info) << "Panel width: " << panel_width.get();
        });
 
-       text_width.set(20);
-       text_width.set(10);
        padding.set(2);
-       text_width.set(20);
 
        auto create_param = [](std::wstring elem) -> std::vector<std::wstring>
        {
@@ -327,8 +387,8 @@ spl::shared_ptr<frame_producer> create_dummy_scene_producer(const spl::shared_pt
                return result;
        };
 
-       auto& car_layer = scene->create_layer(create_producer(frame_factory, format_desc, create_param(L"car")));
-       car_layer.hidden = scene->frame() % 50 > 25 || !(scene->frame() < 1000);
+       //auto& car_layer = scene->create_layer(create_producer(frame_factory, format_desc, create_param(L"car")));
+       //car_layer.hidden = scene->frame() % 50 > 25 || !(scene->frame() < 1000);
        std::vector<std::wstring> sub_params;
        sub_params.push_back(L"[FREEHAND]");
        sub_params.push_back(L"640");
@@ -344,6 +404,7 @@ spl::shared_ptr<frame_producer> create_dummy_scene_producer(const spl::shared_pt
        auto& upper_right = scene->create_layer(create_producer(frame_factory, format_desc, create_param(L"scene/upper_right")));
        auto& lower_left = scene->create_layer(create_producer(frame_factory, format_desc, create_param(L"scene/lower_left")));
        auto& lower_right = scene->create_layer(create_producer(frame_factory, format_desc, create_param(L"scene/lower_right")));
+       auto& text_layer = scene->create_layer(text_area);
 
        /*
        binding<double> panel_x = (scene->frame()
@@ -353,12 +414,21 @@ spl::shared_ptr<frame_producer> create_dummy_scene_producer(const spl::shared_pt
                        + 40.0)
                        .transformed([](double v) { return std::floor(v); }); // snap to pixels instead of subpixels
                        */
-       tweener tween(L"easeinoutsine");
-       binding<double> panel_x = when(scene->frame() < 50)
+       tweener tween(L"easeoutbounce");
+       binding<double> panel_x(0);
+
+       scene->add_keyframe(panel_x, -panel_width, 0);
+       scene->add_keyframe(panel_x, 300.0, 50, L"easeinoutsine");
+       scene->add_keyframe(panel_x, 300.0, 50 * 4);
+       scene->add_keyframe(panel_x, 1000.0, 50 * 5, L"easeinoutsine");
+       //panel_x = delay(panel_x, add_tween(panel_x, scene->frame(), 200.0, int64_t(50), L"linear"), scene->frame(), int64_t(100));
+       /*binding<double> panel_x = when(scene->frame() < 50)
                .then(scene->frame().as<double>().transformed([tween](double t) { return tween(t, 0.0, 200, 50); }))
-               .otherwise(200.0);
+               .otherwise(200.0);*/
        //binding<double> panel_y = when(car_layer.hidden).then(500.0).otherwise(-panel_x + 300);
        binding<double> panel_y(500.0);
+       scene->add_keyframe(panel_y, panel_y.get(), 50 * 4);
+       scene->add_keyframe(panel_y, 720.0, 50 * 5, L"easeinexpo");
        upper_left.position.x = panel_x;
        upper_left.position.y = panel_y;
        upper_right.position.x = upper_left.position.x + upper_left.producer.get()->pixel_constraints().width + panel_width;
@@ -367,9 +437,15 @@ spl::shared_ptr<frame_producer> create_dummy_scene_producer(const spl::shared_pt
        lower_left.position.y = upper_left.position.y + upper_left.producer.get()->pixel_constraints().height + panel_height;
        lower_right.position.x = upper_right.position.x;
        lower_right.position.y = lower_left.position.y;
+       text_layer.position.x = upper_left.position.x + upper_left.producer.get()->pixel_constraints().width + padding;
+       text_layer.position.y = upper_left.position.y + upper_left.producer.get()->pixel_constraints().height + padding + text_area->current_bearing_y().as<double>();
+
+       text_area->text().bind(scene->create_parameter<std::wstring>(L"text"));
+
+       auto params2 = params;
+       params2.erase(params2.begin());
 
-       text_width.set(500);
-       panel_height.set(50);
+       scene->call(params2);
 
        return scene;
 }
index c60f9a8b5705aaf4aa6197aab52f823da1495ef9..530a6f5036394453b22abac1cbab77ab699f8e2d 100644 (file)
@@ -53,6 +53,19 @@ struct layer
        layer(const std::wstring& name, const spl::shared_ptr<frame_producer>& producer);
 };
 
+struct keyframe
+{
+       std::function<void ()> on_start_animate;
+       std::function<void (int64_t start_frame, int64_t current_frame)> on_animate_to;
+       std::function<void ()> on_destination_frame;
+       int64_t destination_frame;
+public:
+       keyframe(int64_t destination_frame)
+               : destination_frame(destination_frame)
+       {
+       }
+};
+
 template<typename T> class parameter_holder;
 
 class parameter_holder_base
@@ -123,10 +136,82 @@ public:
 
                return param->value();
        }
+
+       template<typename T>
+       void add_keyframe(
+                       binding<T>& to_affect,
+                       T destination_value,
+                       int64_t at_frame,
+                       const std::wstring& easing)
+       {
+               add_keyframe(to_affect, binding<T>(destination_value), at_frame, easing);
+       }
+
+       template<typename T>
+       void add_keyframe(
+                       binding<T>& to_affect,
+                       const binding<T>& destination_value,
+                       int64_t at_frame,
+                       const std::wstring& easing)
+       {
+               tweener tween(easing);
+               keyframe k(at_frame);
+
+               std::shared_ptr<T> start_value(new T);
+
+               k.on_start_animate = [=]() mutable
+               {
+                       *start_value = to_affect.get();
+                       to_affect.unbind();
+               };
+
+               k.on_destination_frame = [=]() mutable
+               {
+                       to_affect.bind(destination_value);
+               };
+
+               k.on_animate_to =
+                               [=](int64_t start_frame, int64_t current_frame) mutable
+                               {
+                                       auto relative_frame = current_frame - start_frame;
+                                       auto duration = at_frame - start_frame;
+                                       auto tweened = static_cast<T>(tween(
+                                                       static_cast<double>(relative_frame),
+                                                       *start_value,
+                                                       destination_value.get() - *start_value,
+                                                       static_cast<double>(duration)));
+
+                                       to_affect.set(tweened);
+                                       
+                                       CASPAR_LOG(info) << relative_frame << L" " << *start_value << L" " << duration << L" " << tweened;
+                               };
+
+               store_keyframe(to_affect.identity(), k);
+       }
+
+       template<typename T>
+       void add_keyframe(binding<T>& to_affect, T set_value, int64_t at_frame)
+       {
+               add_keyframe(to_affect, binding<T>(set_value), at_frame);
+       }
+
+       template<typename T>
+       void add_keyframe(binding<T>& to_affect, const binding<T>& set_value, int64_t at_frame)
+       {
+               keyframe k(at_frame);
+
+               k.on_destination_frame = [=]() mutable
+               {
+                       to_affect.bind(set_value);
+               };
+
+               store_keyframe(to_affect.identity(), k);
+       }
 private:
        void store_parameter(
                        const std::wstring& name,
                        const std::shared_ptr<parameter_holder_base>& param);
+       void store_keyframe(void* timeline_identity, const keyframe& k);
 
        struct impl;
        std::unique_ptr<impl> impl_;