Simulant  21.12-194
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 
43 
44 enum ActivateBehaviour {
45  ACTIVATE_BEHAVIOUR_UNLOAD_FIRST,
46  ACTIVATE_BEHAVIOUR_UNLOAD_AFTER
47 };
48 
49 
50 class SceneManager :
51  public RefCounted<SceneManager> {
52 
53  friend class Application;
54 
55  DEFINE_SIGNAL(SceneActivatedSignal, signal_scene_activated);
56 
57  template<typename T>
58  static void unpack(std::vector<any>& output, T&& arg) {
59  output.push_back(std::forward<T>(arg));
60  }
61 
62  template<typename T, typename... Args>
63  static void unpack(std::vector<any>& output, T&& t, Args&& ...args) {
64  output.push_back(std::forward<T>(t));
65  unpack(output, std::forward<Args>(args)...);
66  }
67 
68  template<typename... Args>
69  static void unpack(std::vector<any>&) {}
70 
71  struct ConnectionHolder {
72  sig::connection conn;
73  };
74 
75  template<typename... Args>
76  void do_activate(
77  const std::string& route,
78  ActivateBehaviour behaviour,
79  Args&&... args
80  ) {
81  auto new_scene = get_or_create_route(route);
82  if(new_scene != current_scene_) {
83  if(behaviour == ACTIVATE_BEHAVIOUR_UNLOAD_AFTER) {
84  if(!new_scene->is_loaded()) {
85  new_scene->load_args.clear();
86  unpack(new_scene->load_args, std::forward<Args>(args)...);
87  new_scene->_call_load();
88  }
89 
90  auto previous = current_scene_;
91 
92  if(previous) {
93  previous->_call_deactivate();
94  }
95 
96  std::swap(current_scene_, new_scene);
97  current_scene_->_call_activate();
98 
99  if(previous && previous->unload_on_deactivate()) {
100  // If requested, we unload the previous scene once the new on is active
101  unload(previous->name());
102  }
103  } else {
104  /* Default behaviour - unload the current scene before activating the next one */
105  auto previous = current_scene_;
106 
107  if(previous) {
108  previous->_call_deactivate();
109  if(previous->unload_on_deactivate()) {
110  unload(previous->name());
111  }
112  }
113 
114  if(!new_scene->is_loaded()) {
115  new_scene->load_args.clear();
116  unpack(new_scene->load_args, std::forward<Args>(args)...);
117  new_scene->_call_load();
118  }
119 
120  std::swap(current_scene_, new_scene);
121  current_scene_->_call_activate();
122  }
123 
124  signal_scene_activated_(route, new_scene.get());
125  }
126  };
127 public:
128  SceneManager(Window* window);
129  ~SceneManager();
130 
131  bool has_scene(const std::string& route) const;
132  SceneBasePtr resolve_scene(const std::string& route);
133 
134  template<typename... Args>
135  void activate(
136  const std::string& route, ActivateBehaviour behaviour,
137  Args&& ...args
138  ) {
139  scene_activation_trigger_ = std::bind(
140  &SceneManager::do_activate<Args&...>,
141  this, route, behaviour, std::forward<Args>(args)...
142  );
143  }
144 
145  template<typename... Args>
146  void activate(const std::string& route, Args&& ...args) {
147  activate(route, ACTIVATE_BEHAVIOUR_UNLOAD_FIRST, std::forward<Args>(args)...);
148  }
149 
150  template<typename ...Args>
151  void preload(const std::string& route, Args&& ...args) {
152  auto scene = get_or_create_route(route);
153 
154  if(scene->is_loaded()) {
155  return;
156  }
157 
158  scene->load_args.clear();
159  unpack(scene->load_args, std::forward<Args>(args)...);
160 
161  scene->_call_load();
162  }
163 
164  template<typename ...Args>
165  void _preload_in_background(SceneBasePtr scene, Args&& ...args) {
166  if(scene->is_loaded()) {
167  return;
168  }
169 
170  scene->load_args.clear();
171  unpack(scene->load_args, std::forward<Args>(args)...);
172  scene->_call_load();
173  }
174 
175  template<typename ...Args>
176  Promise<void> preload_in_background(
177  const std::string& route,
178  Args&& ...args
179  ) {
180 
181  auto scene = get_or_create_route(route);
182 
183  return cr_async(
184  std::bind(
185  &SceneManager::_preload_in_background<Args&...>,
186  this,
187  scene,
188  std::forward<Args>(args)...
189  )
190  );
191  }
192 
193  void unload(const std::string& route);
194 
195  /* Unloads and destroys all scenes */
196  void destroy_all();
197 
198  bool is_loaded(const std::string& route) const;
199  void reset();
200 
201  SceneBasePtr active_scene() const;
202 
203  bool scene_queued_for_activation() const;
204 
205  template<typename T, typename... Args>
206  void register_scene(const std::string& name, Args&&... args) {
207  SceneFactory func = std::bind(
208  &T::template create<Window*, typename std::decay<Args>::type&...>,
209  std::placeholders::_1, std::forward<Args>(args)...
210  );
211 
212  _store_scene_factory(name, [=](Window* window) -> SceneBasePtr {
213  auto ret = func(window);
214  ret->set_name(name);
215  ret->scene_manager_ = this;
216  return ret;
217  });
218  }
219 
220  template<typename T>
221  void register_scene(const std::string& name) {
222  _store_scene_factory(name, [=](Window* window) -> SceneBasePtr {
223  auto ret = T::create(window);
224  ret->set_name(name);
225  ret->scene_manager_ = this;
226  return ret;
227  });
228  }
229 
230  template<typename T>
231  std::shared_ptr<T> resolve_scene_as(const std::string& route) {
232  return std::dynamic_pointer_cast<T>(resolve_scene(route));
233  }
234 private:
235  void _store_scene_factory(const std::string& name, SceneFactory func) {
236  scene_factories_[name] = func;
237  }
238 
239  Window* window_;
240 
241  std::unordered_map<std::string, SceneFactory> scene_factories_;
242  std::unordered_map<std::string, SceneBasePtr> routes_;
243 
244  SceneBasePtr current_scene_;
245  SceneBasePtr get_or_create_route(const std::string& route);
246 
247  struct BackgroundTask {
248  std::string route;
249  thread::Future<void> future;
250  };
251 
252  sig::connection step_conn_;
253  sig::connection update_conn_;
254  sig::connection late_update_conn_;
255 
256  void update(float dt);
257  void late_update(float dt);
258  void fixed_update(float step);
259 
260  std::function<void ()> scene_activation_trigger_;
261  std::set<std::string> routes_queued_for_destruction_;
262 };
263 
264 }
265 
266 #endif // SCENE_MANAGER_H
smlt::sig::Connection
Definition: signal.h:65
smlt::Application
Definition: application.h:167
smlt::RefCounted
Definition: managed.h:65
smlt
Definition: animation.cpp:25
smlt::Window
Definition: window.h:65
smlt::SceneManager
Definition: scene_manager.h:51
smlt::Promise< void >
Definition: helpers.h:115
smlt::thread::Future< void >
Definition: future.h:115
smlt::sig::signal
Definition: signal.h:319