]> git.sesse.net Git - casparcg/blob - core/producer/scene/scene_producer.cpp
* added geometry to frames for custom rendering
[casparcg] / core / producer / scene / scene_producer.cpp
1 /*
2 * Copyright (c) 2011 Sveriges Television AB <info@casparcg.com>
3 *
4 * This file is part of CasparCG (www.casparcg.com).
5 *
6 * CasparCG is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * CasparCG is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with CasparCG. If not, see <http://www.gnu.org/licenses/>.
18 *
19 * Author: Helge Norberg, helge.norberg@svt.se
20 */
21
22 #include "../../stdafx.h"
23
24 #include <common/future.h>
25 #include <boost/algorithm/string/split.hpp>
26 #include <boost/algorithm/string.hpp>
27
28 #include "scene_producer.h"
29
30 #include "../../frame/draw_frame.h"
31 #include "../../interaction/interaction_aggregator.h"
32
33 namespace caspar { namespace core { namespace scene {
34
35 layer::layer(const spl::shared_ptr<frame_producer>& producer)
36         : producer(producer)
37 {
38 }
39 layer::layer(const std::wstring& name, const spl::shared_ptr<frame_producer>& producer)
40         : name(name), producer(producer)
41 {
42 }
43
44 adjustments::adjustments()
45         : opacity(1.0)
46 {
47 }
48
49 struct scene_producer::impl
50 {
51         constraints pixel_constraints_;
52         std::list<layer> layers_;
53         interaction_aggregator aggregator_;
54         binding<int64_t> frame_number_;
55
56         impl(int width, int height)
57                 : pixel_constraints_(width, height)
58                 , aggregator_([=] (double x, double y) { return collission_detect(x, y); })
59         {
60         }
61
62         layer& create_layer(
63                         const spl::shared_ptr<frame_producer>& producer, int x, int y, const std::wstring& name)
64         {
65                 layer& layer = create_layer(producer, x, y);
66                 layer.name.set(name);
67
68                 return layer;
69         }
70
71         layer& create_layer(
72                         const spl::shared_ptr<frame_producer>& producer, int x, int y)
73         {
74                 layer& layer = create_layer(producer);
75
76                 layer.position.x.set(x);
77                 layer.position.y.set(y);
78
79                 return layer;
80         }
81
82         layer& create_layer(const spl::shared_ptr<frame_producer>& producer)
83         {
84                 layer layer(producer);
85
86                 layers_.push_back(layer);
87
88                 return layers_.back();
89         }
90
91         binding<int64_t> frame()
92         {
93                 return frame_number_;
94         }
95
96         frame_transform get_transform(const layer& layer) const
97         {
98                 frame_transform transform;
99
100                 auto& pos = transform.image_transform.fill_translation;
101                 auto& scale = transform.image_transform.fill_scale;
102
103                 pos[0] = static_cast<double>(layer.position.x.get()) / static_cast<double>(pixel_constraints_.width.get());
104                 pos[1] = static_cast<double>(layer.position.y.get()) / static_cast<double>(pixel_constraints_.height.get());
105                 scale[0] = static_cast<double>(layer.producer.get()->pixel_constraints().width.get())
106                                 / static_cast<double>(pixel_constraints_.width.get());
107                 scale[1] = static_cast<double>(layer.producer.get()->pixel_constraints().height.get())
108                                 / static_cast<double>(pixel_constraints_.height.get());
109
110                 transform.image_transform.opacity = layer.adjustments.opacity.get();
111                 transform.image_transform.is_key = layer.is_key.get();
112
113                 return transform;
114         }
115
116         draw_frame render_frame()
117         {
118                 std::vector<draw_frame> frames;
119
120                 BOOST_FOREACH(auto& layer, layers_)
121                 {
122                         if (layer.hidden.get())
123                                 continue;
124
125                         draw_frame frame(layer.producer.get()->receive());
126                         frame.transform() = get_transform(layer);;
127                         frames.push_back(frame);
128                 }
129
130                 frame_number_.set(frame_number_.get() + 1);
131
132                 return draw_frame(frames);
133         }
134
135         void on_interaction(const interaction_event::ptr& event)
136         {
137                 aggregator_.translate_and_send(event);
138         }
139
140         bool collides(double x, double y) const
141         {
142                 return collission_detect(x, y);
143         }
144
145         boost::optional<interaction_target> collission_detect(double x, double y) const
146         {
147                 BOOST_FOREACH(auto& layer, layers_ | boost::adaptors::reversed)
148                 {
149                         if (layer.hidden.get())
150                                 continue;
151
152                         auto transform = get_transform(layer);
153                         auto translated = translate(x, y, transform);
154
155                         if (translated.first >= 0.0
156                                 && translated.first <= 1.0
157                                 && translated.second >= 0.0
158                                 && translated.second <= 1.0
159                                 && layer.producer.get()->collides(translated.first, translated.second))
160                         {
161                                 return std::make_pair(transform, layer.producer.get().get());
162                         }
163                 }
164
165                 return boost::optional<interaction_target>();
166         }
167
168         boost::unique_future<std::wstring> call(const std::wstring& params) 
169         {
170                 std::wstring result;
171                 
172                 std::vector<std::wstring> words;
173                 boost::split(words, params, [](wchar_t c) { return c == L' '; }, boost::token_compress_on);
174
175                 if(words.size() >= 2)
176                 {
177                         struct layer_comparer
178                         {
179                                 const std::wstring& str;
180                                 explicit layer_comparer(const std::wstring& s) : str(s) {}
181                                 bool operator()(const layer& val) { return boost::iequals(val.name.get(), str); }
182                         };
183
184                         auto it = std::find_if(layers_.begin(), layers_.end(), layer_comparer(words[0]));
185                         if(it != layers_.end())
186                                 (*it).producer.get()->call(words[1]);
187                 }
188
189                 return async(launch::deferred, [=]{return result;});
190         }
191
192         std::wstring print() const
193         {
194                 return L"scene[]";
195         }
196
197         std::wstring name() const
198         {
199                 return L"scene";
200         }
201         
202         boost::property_tree::wptree info() const
203         {
204                 boost::property_tree::wptree info;
205                 info.add(L"type", L"scene");
206                 return info;
207         }
208
209         void subscribe(const monitor::observable::observer_ptr& o)
210         {
211         }
212
213         void unsubscribe(const monitor::observable::observer_ptr& o)
214         {
215         }
216 };
217
218 scene_producer::scene_producer(int width, int height)
219         : impl_(new impl(width, height))
220 {
221 }
222
223 scene_producer::~scene_producer()
224 {
225 }
226
227 layer& scene_producer::create_layer(
228                 const spl::shared_ptr<frame_producer>& producer, int x, int y)
229 {
230         return impl_->create_layer(producer, x, y);
231 }
232
233 layer& scene_producer::create_layer(
234                 const spl::shared_ptr<frame_producer>& producer, int x, int y, const std::wstring& name)
235 {
236         return impl_->create_layer(producer, x, y, name);
237 }
238
239 layer& scene_producer::create_layer(
240                 const spl::shared_ptr<frame_producer>& producer)
241 {
242         return impl_->create_layer(producer);
243 }
244
245 binding<int64_t> scene_producer::frame()
246 {
247         return impl_->frame();
248 }
249
250 draw_frame scene_producer::receive_impl()
251 {
252         return impl_->render_frame();
253 }
254
255 constraints& scene_producer::pixel_constraints() { return impl_->pixel_constraints_; }
256
257 void scene_producer::on_interaction(const interaction_event::ptr& event)
258 {
259         impl_->on_interaction(event);
260 }
261
262 bool scene_producer::collides(double x, double y) const
263 {
264         return impl_->collides(x, y);
265 }
266
267 std::wstring scene_producer::print() const
268 {
269         return impl_->print();
270 }
271
272 std::wstring scene_producer::name() const
273 {
274         return impl_->name();
275 }
276
277 boost::property_tree::wptree scene_producer::info() const
278 {
279         return impl_->info();
280 }
281
282 boost::unique_future<std::wstring> scene_producer::call(const std::wstring& params) 
283 {
284         return impl_->call(params);
285 }
286
287 void scene_producer::subscribe(const monitor::observable::observer_ptr& o)
288 {
289         impl_->subscribe(o);
290 }
291
292 void scene_producer::unsubscribe(const monitor::observable::observer_ptr& o)
293 {
294         impl_->unsubscribe(o);
295 }
296
297 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)
298 {
299         if (params.size() < 1 || params.at(0) != L"[SCENE]")
300                 return core::frame_producer::empty();
301
302         auto scene = spl::make_shared<scene_producer>(format_desc.width, format_desc.height);
303
304         binding<double> text_width(10);
305         binding<double> padding(1);
306         binding<double> panel_width = padding + text_width + padding;
307         binding<double> panel_height(50);
308
309         auto subscription = panel_width.on_change([&]
310         {
311                 CASPAR_LOG(info) << "Panel width: " << panel_width.get();
312         });
313
314         text_width.set(20);
315         text_width.set(10);
316         padding.set(2);
317         text_width.set(20);
318
319         auto create_param = [](std::wstring elem) -> std::vector<std::wstring>
320         {
321                 std::vector<std::wstring> result;
322                 result.push_back(elem);
323                 return result;
324         };
325
326         auto& car_layer = scene->create_layer(create_producer(frame_factory, format_desc, create_param(L"car")));
327         car_layer.hidden = scene->frame() % 50 > 25 || !(scene->frame() < 1000);
328         std::vector<std::wstring> sub_params;
329         sub_params.push_back(L"[FREEHAND]");
330         sub_params.push_back(L"640");
331         sub_params.push_back(L"360");
332         scene->create_layer(create_producer(frame_factory, format_desc, sub_params), 10, 10);
333         sub_params.clear();
334
335         scene->create_layer(create_producer(frame_factory, format_desc, create_param(L"BLUE")), 110, 10);
336
337         //scene->create_layer(create_producer(frame_factory, format_desc, create_param(L"SP")), 50, 50);
338
339         auto& upper_left = scene->create_layer(create_producer(frame_factory, format_desc, create_param(L"scene/upper_left")));
340         auto& upper_right = scene->create_layer(create_producer(frame_factory, format_desc, create_param(L"scene/upper_right")));
341         auto& lower_left = scene->create_layer(create_producer(frame_factory, format_desc, create_param(L"scene/lower_left")));
342         auto& lower_right = scene->create_layer(create_producer(frame_factory, format_desc, create_param(L"scene/lower_right")));
343
344         binding<double> panel_x = (scene->frame()
345                         .as<double>()
346                         .transformed([](double v) { return std::sin(v / 20.0); })
347                         * 20.0
348                         + 40.0)
349                         .transformed([](double v) { return std::floor(v); }); // snap to pixels instead of subpixels
350         binding<double> panel_y = when(car_layer.hidden).then(500.0).otherwise(-panel_x + 300);
351         upper_left.position.x = panel_x;
352         upper_left.position.y = panel_y;
353         upper_right.position.x = upper_left.position.x + upper_left.producer.get()->pixel_constraints().width + panel_width;
354         upper_right.position.y = upper_left.position.y;
355         lower_left.position.x = upper_left.position.x;
356         lower_left.position.y = upper_left.position.y + upper_left.producer.get()->pixel_constraints().height + panel_height;
357         lower_right.position.x = upper_right.position.x;
358         lower_right.position.y = lower_left.position.y;
359
360         text_width.set(500);
361         panel_height.set(50);
362
363         return scene;
364 }
365
366 }}}