Simulant  21.12-1292
A portable game engine for Windows, OSX, Linux, Dreamcast, and PSP
scene_manager.h
1 /* * Copyright (c) 2011-2017 Luke Benstead https://simulant-engine.appspot.com
2  *
3  * This file is part of Simulant.
4  *
5  * Simulant is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU Lesser General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * Simulant is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public License
16  * along with Simulant. If not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 #ifndef SCENE_MANAGER_H
20 #define SCENE_MANAGER_H
21 
22 #include <unordered_map>
23 #include <functional>
24 
25 #include "../threads/future.h"
26 
27 #include "scene.h"
28 #include "../generic/managed.h"
29 #include "../signals/signal.h"
30 
31 #include "../generic/static_if.h"
32 #include "../coroutines/helpers.h"
33 
34 namespace smlt {
35 
36 class Application;
37 
38 typedef std::shared_ptr<Scene> ScenePtr;
39 typedef std::function<ScenePtr (Window*)> SceneFactory;
40 
41 typedef sig::signal<void (std::string, Scene*)> SceneActivatedSignal;
42 typedef sig::signal<void (std::string, Scene*)> SceneDeactivatedSignal;
43 
44 
45 enum ActivateBehaviour {
46  ACTIVATE_BEHAVIOUR_UNLOAD_FIRST,
47  ACTIVATE_BEHAVIOUR_UNLOAD_AFTER
48 };
49 
50 
51 class SceneManager :
52  public RefCounted<SceneManager> {
53 
54  friend class Application;
55 
56  DEFINE_SIGNAL(SceneActivatedSignal, signal_scene_activated);
57  DEFINE_SIGNAL(SceneDeactivatedSignal, signal_scene_deactivated);
58 
59  template<typename T>
60  static void unpack(std::vector<any>& output, T&& arg) {
61  output.push_back(std::forward<T>(arg));
62  }
63 
64  template<typename T, typename... Args>
65  static void unpack(std::vector<any>& output, T&& t, Args&& ...args) {
66  output.push_back(std::forward<T>(t));
67  unpack(output, std::forward<Args>(args)...);
68  }
69 
70  template<typename... Args>
71  static void unpack(std::vector<any>&) {}
72 
73  struct ConnectionHolder {
74  sig::connection conn;
75  };
76 
77  template<typename... Args>
78  void do_activate(
79  std::string route,
80  ActivateBehaviour behaviour,
81  Args&&... args
82  ) {
83  auto new_scene = get_or_create_route(route);
84  if(new_scene != current_scene_) {
85  if(behaviour == ACTIVATE_BEHAVIOUR_UNLOAD_AFTER) {
86  if(!new_scene->is_loaded()) {
87  new_scene->load_args.clear();
88  unpack(new_scene->load_args, std::forward<Args>(args)...);
89  new_scene->load();
90  }
91 
92  auto previous = current_scene_;
93 
94  if(previous) {
95  previous->deactivate();
96  signal_scene_deactivated_(previous->name(), previous.get());
97  }
98 
99  std::swap(current_scene_, new_scene);
100  current_scene_->activate();
101 
102  if(previous && previous->unload_on_deactivate()) {
103  // If requested, we unload the previous scene once the new on is active
104  unload(previous->name());
105  }
106  } else {
107  /* Default behaviour - unload the current scene before activating the next one */
108  auto previous = current_scene_;
109 
110  if(previous) {
111  previous->deactivate();
112  signal_scene_deactivated_(previous->name(), previous.get());
113  if(previous->unload_on_deactivate()) {
114  unload(previous->name());
115  }
116  }
117 
118  if(!new_scene->is_loaded()) {
119  new_scene->load_args.clear();
120  unpack(new_scene->load_args, std::forward<Args>(args)...);
121  new_scene->load();
122  }
123 
124  std::swap(current_scene_, new_scene);
125  current_scene_->activate();
126  }
127 
128  signal_scene_activated_(route, new_scene.get());
129  }
130  }
131 
132  template<typename D, typename T, typename...Args>
133  static ScenePtr make_scene(const D& deleter, Args&&... args) {
134  return std::shared_ptr<T>(new T(std::forward<Args>(args)...), deleter);
135  }
136 
137  template<typename T>
138  static void deleter(T* obj) {
139  obj->clean_up();
140  delete obj;
141  }
142 
143 public:
144  SceneManager(Window* window);
145  ~SceneManager();
146 
147  bool has_scene(const std::string& route) const;
148  ScenePtr resolve_scene(const std::string& route);
149 
150  template<typename... Args>
151  void activate(
152  const std::string& route, ActivateBehaviour behaviour,
153  Args&& ...args
154  ) {
155  scene_activation_trigger_ = std::bind(
156  &SceneManager::do_activate<Args&...>,
157  this, route, behaviour, std::forward<Args>(args)...
158  );
159  }
160 
161  template<typename... Args>
162  void activate(const std::string& route, Args&& ...args) {
163  activate(route, ACTIVATE_BEHAVIOUR_UNLOAD_FIRST, std::forward<Args>(args)...);
164  }
165 
166  template<typename ...Args>
167  void preload(const std::string& route, Args&& ...args) {
168  auto scene = get_or_create_route(route);
169 
170  if(scene->is_loaded()) {
171  return;
172  }
173 
174  scene->load_args.clear();
175  unpack(scene->load_args, std::forward<Args>(args)...);
176 
177  scene->load();
178  }
179 
180  template<typename ...Args>
181  void _preload_in_background(ScenePtr scene, Args&& ...args) {
182  if(scene->is_loaded()) {
183  return;
184  }
185 
186  scene->load_args.clear();
187  unpack(scene->load_args, std::forward<Args>(args)...);
188  scene->load();
189  }
190 
191  template<typename ...Args>
192  Promise<void> preload_in_background(
193  const std::string& route,
194  Args&& ...args
195  ) {
196 
197  auto scene = get_or_create_route(route);
198 
199  return cr_async(
200  std::bind(
201  &SceneManager::_preload_in_background<Args&...>,
202  this,
203  scene,
204  std::forward<Args>(args)...
205  )
206  );
207  }
208 
209  void unload(const std::string& route);
210 
211  /* Unloads and destroys all scenes */
212  void destroy_all();
213  void clean_destroyed_scenes();
214 
215  bool is_loaded(const std::string& route) const;
216  void reset();
217 
218  ScenePtr active_scene() const;
219 
220  bool scene_queued_for_activation() const;
221 
222  template<typename T, typename... Args>
223  void register_scene(const std::string& name, Args&&... args) {
224  SceneFactory func = std::bind(
225  &SceneManager::make_scene<decltype(&SceneManager::deleter<T>), T, Window*, typename std::decay<Args>::type&...>,
226  &SceneManager::deleter<T>, std::placeholders::_1, std::forward<Args>(args)...
227  );
228 
229  _store_scene_factory(name, [=](Window* window) -> ScenePtr {
230  auto ret = func(window);
231  if(!ret->init()) {
232  S_ERROR("Failed to initialize the Scene");
233  return ScenePtr();
234  }
235 
236  ret->set_name(name);
237  ret->scene_manager_ = this;
238  return ret;
239  });
240  }
241 
242  template<typename T>
243  void register_scene(const std::string& name) {
244  _store_scene_factory(name, [=](Window* window) -> ScenePtr {
245  auto ret = SceneManager::make_scene<decltype(&SceneManager::deleter<T>), T>(&SceneManager::deleter, window);
246  ret->set_name(name);
247  ret->scene_manager_ = this;
248 
249  if(!ret->init()) {
250  S_ERROR("Failed to initialize the Scene");
251  return ScenePtr();
252  }
253 
254  return ret;
255  });
256  }
257 
258  template<typename T>
259  std::shared_ptr<T> resolve_scene_as(const std::string& route) {
260  return std::dynamic_pointer_cast<T>(resolve_scene(route));
261  }
262 private:
263  void _store_scene_factory(const std::string& name, SceneFactory func) {
264  scene_factories_[name] = func;
265  }
266 
267  Window* window_;
268 
269  std::unordered_map<std::string, SceneFactory> scene_factories_;
270  std::unordered_map<std::string, ScenePtr> routes_;
271 
272  ScenePtr current_scene_;
273  ScenePtr get_or_create_route(const std::string& route);
274 
275  struct BackgroundTask {
276  std::string route;
277  thread::Future<void> future;
278  };
279 
280  sig::connection step_conn_;
281  sig::connection update_conn_;
282  sig::connection late_update_conn_;
283 
284  void update(float dt);
285  void late_update(float dt);
286  void fixed_update(float step);
287 
288  std::function<void ()> scene_activation_trigger_;
289  std::set<std::string> routes_queued_for_destruction_;
290 };
291 
292 }
293 
294 #endif // SCENE_MANAGER_H
smlt::sig::Connection
Definition: signal.h:65
smlt::Application
Definition: application.h:160
smlt::RefCounted
Definition: managed.h:71
smlt
Definition: animation.cpp:25
smlt::Window
Definition: window.h:68
smlt::SceneManager
Definition: scene_manager.h:52
smlt::Promise< void >
Definition: helpers.h:141
smlt::thread::Future< void >
Definition: future.h:115
smlt::sig::signal
Definition: signal.h:330