Simulant  21.12-574
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<SceneBase> SceneBasePtr;
39 typedef std::function<SceneBasePtr (Window*)> SceneFactory;
40 
41 typedef sig::signal<void (std::string, SceneBase*)> SceneActivatedSignal;
42 typedef sig::signal<void (std::string, SceneBase*)> 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->_call_load();
90  }
91 
92  auto previous = current_scene_;
93 
94  if(previous) {
95  previous->_call_deactivate();
96  signal_scene_deactivated_(previous->name(), previous.get());
97  }
98 
99  std::swap(current_scene_, new_scene);
100  current_scene_->_call_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->_call_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->_call_load();
122  }
123 
124  std::swap(current_scene_, new_scene);
125  current_scene_->_call_activate();
126  }
127 
128  signal_scene_activated_(route, new_scene.get());
129  }
130  }
131 public:
132  SceneManager(Window* window);
133  ~SceneManager();
134 
135  bool has_scene(const std::string& route) const;
136  SceneBasePtr resolve_scene(const std::string& route);
137 
138  template<typename... Args>
139  void activate(
140  const std::string& route, ActivateBehaviour behaviour,
141  Args&& ...args
142  ) {
143  scene_activation_trigger_ = std::bind(
144  &SceneManager::do_activate<Args&...>,
145  this, route, behaviour, std::forward<Args>(args)...
146  );
147  }
148 
149  template<typename... Args>
150  void activate(const std::string& route, Args&& ...args) {
151  activate(route, ACTIVATE_BEHAVIOUR_UNLOAD_FIRST, std::forward<Args>(args)...);
152  }
153 
154  template<typename ...Args>
155  void preload(const std::string& route, Args&& ...args) {
156  auto scene = get_or_create_route(route);
157 
158  if(scene->is_loaded()) {
159  return;
160  }
161 
162  scene->load_args.clear();
163  unpack(scene->load_args, std::forward<Args>(args)...);
164 
165  scene->_call_load();
166  }
167 
168  template<typename ...Args>
169  void _preload_in_background(SceneBasePtr scene, Args&& ...args) {
170  if(scene->is_loaded()) {
171  return;
172  }
173 
174  scene->load_args.clear();
175  unpack(scene->load_args, std::forward<Args>(args)...);
176  scene->_call_load();
177  }
178 
179  template<typename ...Args>
180  Promise<void> preload_in_background(
181  const std::string& route,
182  Args&& ...args
183  ) {
184 
185  auto scene = get_or_create_route(route);
186 
187  return cr_async(
188  std::bind(
189  &SceneManager::_preload_in_background<Args&...>,
190  this,
191  scene,
192  std::forward<Args>(args)...
193  )
194  );
195  }
196 
197  void unload(const std::string& route);
198 
199  /* Unloads and destroys all scenes */
200  void destroy_all();
201 
202  bool is_loaded(const std::string& route) const;
203  void reset();
204 
205  SceneBasePtr active_scene() const;
206 
207  bool scene_queued_for_activation() const;
208 
209  template<typename T, typename... Args>
210  void register_scene(const std::string& name, Args&&... args) {
211  SceneFactory func = std::bind(
212  &T::template create<Window*, typename std::decay<Args>::type&...>,
213  std::placeholders::_1, std::forward<Args>(args)...
214  );
215 
216  _store_scene_factory(name, [=](Window* window) -> SceneBasePtr {
217  auto ret = func(window);
218  ret->set_name(name);
219  ret->scene_manager_ = this;
220  return ret;
221  });
222  }
223 
224  template<typename T>
225  void register_scene(const std::string& name) {
226  _store_scene_factory(name, [=](Window* window) -> SceneBasePtr {
227  auto ret = T::create(window);
228  ret->set_name(name);
229  ret->scene_manager_ = this;
230  return ret;
231  });
232  }
233 
234  template<typename T>
235  std::shared_ptr<T> resolve_scene_as(const std::string& route) {
236  return std::dynamic_pointer_cast<T>(resolve_scene(route));
237  }
238 private:
239  void _store_scene_factory(const std::string& name, SceneFactory func) {
240  scene_factories_[name] = func;
241  }
242 
243  Window* window_;
244 
245  std::unordered_map<std::string, SceneFactory> scene_factories_;
246  std::unordered_map<std::string, SceneBasePtr> routes_;
247 
248  SceneBasePtr current_scene_;
249  SceneBasePtr get_or_create_route(const std::string& route);
250 
251  struct BackgroundTask {
252  std::string route;
253  thread::Future<void> future;
254  };
255 
256  sig::connection step_conn_;
257  sig::connection update_conn_;
258  sig::connection late_update_conn_;
259 
260  void update(float dt);
261  void late_update(float dt);
262  void fixed_update(float step);
263 
264  std::function<void ()> scene_activation_trigger_;
265  std::set<std::string> routes_queued_for_destruction_;
266 };
267 
268 }
269 
270 #endif // SCENE_MANAGER_H
smlt::sig::Connection
Definition: signal.h:65
smlt::Application
Definition: application.h:162
smlt::RefCounted
Definition: managed.h:65
smlt
Definition: animation.cpp:25
smlt::Window
Definition: window.h:65
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