diff --git a/doxygen/_application_8h_source.html b/doxygen/_application_8h_source.html index afc99f6e..032fd3ab 100644 --- a/doxygen/_application_8h_source.html +++ b/doxygen/_application_8h_source.html @@ -88,50 +88,50 @@
19 
20 #include <chrono>
21 #include <list>
-
22 
-
23 namespace ROCKY_NAMESPACE
-
24 {
-
25  class ROCKY_EXPORT Application
-
26  {
-
27  public:
-
29  Application();
-
30 
-
34  Application(int& argc, char** argv);
-
35 
-
38  int run();
-
39 
-
43  bool frame();
-
44 
-
48  void onNextUpdate(std::function<void()> func);
-
49 
-
51  void setViewer(vsg::ref_ptr<vsg::Viewer> viewer);
-
52 
-
54  std::string about() const;
-
55 
-
57  bool debugLayerOn() const {
-
58  return _debuglayer;
-
59  }
-
60 
-
64  void realize();
-
65 
-
67  IOOptions& io() {
-
68  return instance.io();
-
69  }
-
70 
-
72  Runtime& runtime() {
-
73  return instance.runtime();
-
74  }
-
75 
-
76  bool active() const {
-
77  return _lastFrameOK;
-
78  }
-
79 
-
80  public: // public properties
-
81 
-
82  jobs::detail::semaphore handle;
-
83 
-
84  entt::registry registry;
-
85  entt::registry& entities = registry; // alias
+
22 #include <mutex>
+
23 
+
24 namespace ROCKY_NAMESPACE
+
25 {
+
26  class ROCKY_EXPORT Application
+
27  {
+
28  public:
+
30  Application();
+
31 
+
35  Application(int& argc, char** argv);
+
36 
+
39  int run();
+
40 
+
44  bool frame();
+
45 
+
49  void onNextUpdate(std::function<void()> func);
+
50 
+
52  void setViewer(vsg::ref_ptr<vsg::Viewer> viewer);
+
53 
+
55  std::string about() const;
+
56 
+
58  bool debugLayerOn() const {
+
59  return _debuglayer;
+
60  }
+
61 
+
65  void realize();
+
66 
+
68  IOOptions& io() {
+
69  return instance.io();
+
70  }
+
71 
+
73  Runtime& runtime() {
+
74  return instance.runtime();
+
75  }
+
76 
+
77  bool active() const {
+
78  return _lastFrameOK;
+
79  }
+
80 
+
81  public: // public properties
+
82 
+
83  jobs::detail::semaphore handle;
+
84 
+
85  ecs::Registry registry;
86 
87  rocky::InstanceVSG instance;
88  vsg::ref_ptr<rocky::MapNode> mapNode;
diff --git a/doxygen/_e_c_s_8h_source.html b/doxygen/_e_c_s_8h_source.html index aef4b97a..f64c54c3 100644 --- a/doxygen/_e_c_s_8h_source.html +++ b/doxygen/_e_c_s_8h_source.html @@ -94,577 +94,630 @@
25 {
27  namespace ecs
28  {
-
29  using time_point = std::chrono::steady_clock::time_point;
-
30 
-
31  // Template for a component with per-view data.
-
32  template<typename T, int NUM_VIEWS, T default_value = T{} >
-
33  struct PerView
-
34  {
-
35  PerView() { views.fill(default_value); }
-
36  std::array<T, NUM_VIEWS> views;
-
37  auto& operator[](int i) { return views[i]; }
-
38  const auto& operator[](int i) const { return views[i]; }
-
39  void setAll(const T& value) { views.fill(value); }
-
40  };
-
41 
-
45  struct RevisionedComponent
-
46  {
-
48  int revision = 0;
-
49  void dirty() { revision++; }
-
50 
-
52  entt::entity attach_point = entt::null;
-
53 
-
54  protected:
-
55  RevisionedComponent() = default;
-
56  };
-
57 
-
63  struct Renderable
-
64  {
-
65  using LockedNode = util::locked_value<vsg::ref_ptr<vsg::Node>>;
-
66  vsg::ref_ptr<vsg::Node> node;
-
67  std::unique_ptr<LockedNode> staged = std::make_unique<LockedNode>();
-
68  int revision = -1;
-
69  };
-
70  }
-
71 
-
75  struct Visibility : public ecs::PerView<bool, 4, true>
-
76  {
-
77  // setting this ties this component to another, ignoring the internal settings
-
78  Visibility* parent = nullptr;
-
79  };
-
80 
-
81  namespace ecs
-
82  {
-
87  class ROCKY_EXPORT System
-
88  {
-
89  public:
-
91  entt::registry& registry;
-
92 
-
94  Status status;
-
95 
-
97  virtual void initializeSystem(Runtime& runtime)
-
98  {
-
99  //nop
-
100  }
-
101 
-
103  virtual void update(Runtime& runtime)
-
104  {
-
105  //nop
-
106  }
-
107 
-
108  protected:
-
109  System(entt::registry& in_registry) :
-
110  registry(in_registry) { }
-
111  };
-
112 
-
122  class SystemNodeBase : public vsg::Inherit<vsg::Compilable, SystemNodeBase>
-
123  {
-
124  public:
-
125  class SystemsManagerGroup* manager = nullptr;
-
126 
-
127  struct CreateOrUpdateData
-
128  {
-
129  vsg::ref_ptr<vsg::Node> existing_node;
-
130  vsg::ref_ptr<vsg::Node> new_node;
-
131  bool new_node_needs_compilation = true;
-
132  };
-
133 
-
134  struct EntityCompileBatch
-
135  {
-
136  std::vector<entt::entity> entities;
-
137  SystemNodeBase* system;
-
138  Runtime* runtime;
-
139  };
+
29  class Registry : public entt::registry
+
30  {
+
31  public:
+
32  std::shared_mutex mutex;
+
33  };
+
34 
+
35 
+
36 
+
37  using time_point = std::chrono::steady_clock::time_point;
+
38 
+
42  template<typename T, int NUM_VIEWS, T default_value = T{} >
+
43  struct PerView
+
44  {
+
45  PerView() { views.fill(default_value); }
+
46  std::array<T, NUM_VIEWS> views;
+
47  auto& operator[](int i) { return views[i]; }
+
48  const auto& operator[](int i) const { return views[i]; }
+
49  void setAll(const T& value) { views.fill(value); }
+
50  };
+
51 
+
55  struct RevisionedComponent
+
56  {
+
58  int revision = 0;
+
59  void dirty() { revision++; }
+
60 
+
62  entt::entity attach_point = entt::null;
+
63 
+
64  protected:
+
65  RevisionedComponent() = default;
+
66  };
+
67 
+
73  struct Renderable
+
74  {
+
75  using LockedNode = util::locked_value<vsg::ref_ptr<vsg::Node>>;
+
76  vsg::ref_ptr<vsg::Node> node;
+
77  std::unique_ptr<LockedNode> staged = std::make_unique<LockedNode>();
+
78  int revision = -1;
+
79  };
+
80  }
+
81 
+
85  struct Visibility : public ecs::PerView<bool, 4, true>
+
86  {
+
87  // overall active state
+
88  bool active = true;
+
89 
+
90  // setting this ties this component to another, ignoring the internal settings
+
91  Visibility* parent = nullptr;
+
92  };
+
93 
+
94  namespace ecs
+
95  {
+
100  class ROCKY_EXPORT System
+
101  {
+
102  public:
+
104  Registry& registry;
+
105 
+
107  Status status;
+
108 
+
110  virtual void initializeSystem(Runtime& runtime)
+
111  {
+
112  //nop
+
113  }
+
114 
+
116  virtual void update(Runtime& runtime)
+
117  {
+
118  //nop
+
119  }
+
120 
+
121  protected:
+
122  System(Registry& in_registry) :
+
123  registry(in_registry) { }
+
124  };
+
125 
+
127  struct BuildInfo
+
128  {
+
129  vsg::ref_ptr<vsg::Node> existing_node;
+
130  vsg::ref_ptr<vsg::Node> new_node;
+
131  };
+
132 
+
133  // Internal record for a component that needs building
+
134  struct BuildItem : public BuildInfo
+
135  {
+
136  entt::entity entity;
+
137  std::uint16_t version;
+
138  ecs::RevisionedComponent* component = nullptr;
+
139  ~BuildItem() { if (component) delete component; }
140 
-
141  virtual void create_or_update(entt::entity entity, CreateOrUpdateData& data, Runtime& runtime) const = 0;
-
142  virtual void finish_update(entt::entity entity, CreateOrUpdateData& data) const = 0;
-
143  };
-
144 
-
145  template<class T>
-
146  class SystemNode : public vsg::Inherit<SystemNodeBase, SystemNode<T>>, public System
-
147  {
-
148  using super = SystemNodeBase;
-
149 
-
150  public:
-
152  virtual ~SystemNode<T>();
-
153 
-
154  // looks for any new components that need VSG initialization
-
155  void update(Runtime&) override;
+
141  // only permit default or move construction:
+
142  BuildItem() = default;
+
143  BuildItem(const BuildItem&) = delete;
+
144  BuildItem& operator=(const BuildItem& rhs) = delete;
+
145  BuildItem(BuildItem&& rhs) noexcept { *this = std::move(rhs); }
+
146  BuildItem& operator = (BuildItem&& rhs) noexcept {
+
147  entity = rhs.entity;
+
148  version = rhs.version;
+
149  existing_node = rhs.existing_node;
+
150  new_node = rhs.new_node;
+
151  component = rhs.component;
+
152  rhs.component = nullptr;
+
153  return *this;
+
154  }
+
155  };
156 
-
157  protected:
-
159  SystemNode<T>(entt::registry& in_registry);
-
160 
-
162  virtual int featureMask(const T& t) const { return 0; }
+
157  // Internal structure for a batch of BuildItems associated with a system
+
158  struct BuildBatch
+
159  {
+
160  std::vector<BuildItem> items;
+
161  class SystemNodeBase* system = nullptr;
+
162  Runtime* runtime = nullptr;
163 
-
164  // The configuration and command list for a graphics pipeline
-
165  // configured for a specific set of features. This setup
-
166  // supports the creation of a unique pipeline for a feature set
-
167  // that's stored in an integer mask.
-
168  struct Pipeline
-
169  {
-
170  vsg::ref_ptr<vsg::GraphicsPipelineConfigurator> config;
-
171  vsg::ref_ptr<vsg::Commands> commands;
-
172  };
-
173  std::vector<Pipeline> pipelines;
-
174 
-
175  // Hooks to expose systems and components to VSG visitors.
-
176  void compile(vsg::Context&) override;
-
177  void traverse(vsg::Visitor& v) override;
-
178  void traverse(vsg::ConstVisitor& v) const override;
-
179  void traverse(vsg::RecordTraversal&) const override;
-
180 
-
187  bool setReferencePoint(const GeoPoint& point, SRSOperation& out_xform, vsg::dvec3& out_offset) const;
-
188 
-
190  vsg::ref_ptr<vsg::PipelineLayout> getPipelineLayout(const T&) const;
-
191 
-
193  virtual void createOrUpdateNode(entt::entity entity, SystemNodeBase::CreateOrUpdateData& data, Runtime& runtime) const = 0;
-
194 
-
195  void create_or_update(entt::entity entity, SystemNodeBase::CreateOrUpdateData& data, Runtime& runtime) const override;
-
196  void finish_update(entt::entity entity, SystemNodeBase::CreateOrUpdateData& data) const override;
+
164  // only permit default or move construction:
+
165  BuildBatch() = default;
+
166  BuildBatch(BuildBatch&& rhs) noexcept = default;
+
167  BuildBatch& operator=(BuildBatch&& rhs) noexcept = default;
+
168  BuildBatch(const BuildBatch&) = delete;
+
169  BuildBatch& operator=(const BuildBatch& rhs) = delete;
+
170  };
+
171 
+
181  class SystemNodeBase : public vsg::Inherit<vsg::Compilable, SystemNodeBase>
+
182  {
+
183  public:
+
184  class SystemsManagerGroup* manager = nullptr;
+
185 
+
186  virtual void invokeCreateOrUpdate(BuildItem& ec, Runtime& runtime) const = 0;
+
187 
+
188  virtual void mergeCreateOrUpdateResults(BuildItem& ec) = 0;
+
189  };
+
190 
+
191  template<class T>
+
192  class SystemNode : public vsg::Inherit<SystemNodeBase, SystemNode<T>>, public System
+
193  {
+
194  public:
+
196  virtual ~SystemNode<T>();
197 
-
198  private:
-
199 
-
200  // list of entities whose components are out of date and need updating
-
201  mutable std::vector<entt::entity> entities_to_update;
-
202 
-
203  // internal structure used when sorting components (by pipeline) for rendering
-
204  struct RenderLeaf
-
205  {
-
206  Renderable& renderable;
-
207  Transform* transform;
-
208  };
-
209 
-
210  // re-usable collection to minimize re-allocation
-
211  mutable std::vector<std::vector<RenderLeaf>> pipelineRenderLeaves;
-
212  };
-
213 
-
218  class ROCKY_EXPORT SystemsManagerGroup : public vsg::Inherit<vsg::Group, SystemsManagerGroup>
-
219  {
-
220  public:
-
221  SystemsManagerGroup(BackgroundServices& bg);
-
222 
-
225  template<class T>
-
226  void add(vsg::ref_ptr<SystemNode<T>> system)
-
227  {
-
228  addChild(system);
-
229  systems.emplace_back(system.get());
-
230  }
-
231 
-
235  template<class T>
-
236  vsg::ref_ptr<T> add(entt::registry& entities)
-
237  {
-
238  auto system = T::create(entities);
-
239  if (system)
-
240  {
-
241  addChild(system);
-
242  systems.emplace_back(system);
-
243  }
-
244  return system;
-
245  }
-
246 
-
250  void add(std::shared_ptr<System> system)
-
251  {
-
252  non_node_systems.emplace_back(system);
-
253  systems.emplace_back(system.get());
-
254  }
-
255 
-
259  void initialize(Runtime& runtime)
-
260  {
-
261  for (auto& child : children)
-
262  {
-
263  auto systemNode = child->cast<SystemNodeBase>();
-
264  if (systemNode)
-
265  {
-
266  systemNode->manager = this;
-
267  }
-
268  }
-
269 
-
270  for (auto& system : systems)
-
271  {
-
272  system->initializeSystem(runtime);
-
273  }
-
274  }
-
275 
-
278  void update(Runtime& runtime)
-
279  {
-
280  for (auto& system : systems)
-
281  {
-
282  system->update(runtime);
-
283  }
-
284  }
-
285 
-
286  void traverse(vsg::RecordTraversal& rt) const override
-
287  {
-
288  vsg::Group::traverse(rt);
-
289  }
-
290 
-
291  public:
-
292  // the data structure holding the queued jobs. 16 might be overkill :)
-
293  util::ring_buffer<SystemNodeBase::EntityCompileBatch> entityCompileJobs{ 16 };
-
294 
-
295  private:
-
296  std::vector<System*> systems;
-
297  std::vector<std::shared_ptr<System>> non_node_systems;
-
298  // NB: SystmeNode instances are group children
-
299  };
+
198  // looks for any new components that need VSG initialization
+
199  void update(Runtime&) override;
+
200 
+
201  protected:
+
203  SystemNode<T>(Registry& in_registry);
+
204 
+
206  virtual int featureMask(const T& t) const { return 0; }
+
207 
+
208  // The configuration and command list for a graphics pipeline
+
209  // configured for a specific set of features. This setup
+
210  // supports the creation of a unique pipeline for a feature set
+
211  // that's stored in an integer mask.
+
212  struct Pipeline
+
213  {
+
214  vsg::ref_ptr<vsg::GraphicsPipelineConfigurator> config;
+
215  vsg::ref_ptr<vsg::Commands> commands;
+
216  };
+
217  std::vector<Pipeline> pipelines;
+
218 
+
219  // Hooks to expose systems and components to VSG visitors.
+
220  void compile(vsg::Context&) override;
+
221  void traverse(vsg::Visitor& v) override;
+
222  void traverse(vsg::ConstVisitor& v) const override;
+
223  void traverse(vsg::RecordTraversal&) const override;
+
224 
+
231  bool parseReferencePoint(const GeoPoint& point, SRSOperation& out_xform, vsg::dvec3& out_offset) const;
+
232 
+
234  vsg::ref_ptr<vsg::PipelineLayout> getPipelineLayout(const T&) const;
+
235 
+
237  virtual void createOrUpdateNode(const T&, BuildInfo&, Runtime&) const = 0;
+
238 
+
239  void invokeCreateOrUpdate(BuildItem& ec, Runtime& runtime) const override;
+
240 
+
241  void mergeCreateOrUpdateResults(BuildItem& ec) override;
+
242 
+
243  private:
+
244 
+
245  // list of entities whose components are out of date and need updating
+
246  mutable std::vector<entt::entity> entities_to_update;
+
247 
+
248  // internal structure used when sorting components (by pipeline) for rendering
+
249  struct RenderLeaf
+
250  {
+
251  Renderable& renderable;
+
252  Transform* transform;
+
253  };
+
254 
+
255  // re-usable collection to minimize re-allocation
+
256  mutable std::vector<std::vector<RenderLeaf>> pipelineRenderLeaves;
+
257  };
+
258 
+
263  class ROCKY_EXPORT SystemsManagerGroup : public vsg::Inherit<vsg::Group, SystemsManagerGroup>
+
264  {
+
265  public:
+
266  SystemsManagerGroup(BackgroundServices& bg);
+
267 
+
270  template<class T>
+
271  void add(vsg::ref_ptr<SystemNode<T>> system)
+
272  {
+
273  addChild(system);
+
274  systems.emplace_back(system.get());
+
275  }
+
276 
+
280  template<class T>
+
281  vsg::ref_ptr<T> add(Registry& registry)
+
282  {
+
283  auto system = T::create(registry);
+
284  if (system)
+
285  {
+
286  addChild(system);
+
287  systems.emplace_back(system);
+
288  }
+
289  return system;
+
290  }
+
291 
+
295  void add(std::shared_ptr<System> system)
+
296  {
+
297  non_node_systems.emplace_back(system);
+
298  systems.emplace_back(system.get());
+
299  }
300 
-
305  inline bool visible(Visibility& vis, int view_index)
-
306  {
-
307  return vis.parent != nullptr ? visible(*vis.parent, view_index) : vis[view_index];
-
308  }
-
309 
-
315  inline void setVisible(entt::registry& r, entt::entity e, bool value, int view_index = -1)
-
316  {
-
317  auto& visibility = r.get<Visibility>(e);
-
318  if (visibility.parent == nullptr)
-
319  {
-
320  if (view_index > 0)
-
321  visibility[view_index] = value;
-
322  else
-
323  visibility.setAll(value);
-
324  }
-
325  }
-
326 
-
332  inline bool visible(entt::registry& r, entt::entity e, int view_index = 0)
-
333  {
-
334  return visible(r.get<Visibility>(e), view_index);
-
335  }
-
336  }
-
337 
-
338 
-
339  //........................................................................
+
304  void initialize(Runtime& runtime)
+
305  {
+
306  for (auto& child : children)
+
307  {
+
308  auto systemNode = child->cast<SystemNodeBase>();
+
309  if (systemNode)
+
310  {
+
311  systemNode->manager = this;
+
312  }
+
313  }
+
314 
+
315  for (auto& system : systems)
+
316  {
+
317  system->initializeSystem(runtime);
+
318  }
+
319  }
+
320 
+
323  void update(Runtime& runtime);
+
324 
+
325  void traverse(vsg::RecordTraversal& rt) const override
+
326  {
+
327  vsg::Group::traverse(rt);
+
328  }
+
329 
+
330  public:
+
331  // the data structure holding the queued jobs. 16 might be overkill :)
+
332  util::ring_buffer<ecs::BuildBatch> buildInput{ 16 };
+
333  util::ring_buffer<ecs::BuildBatch> buildOutput{ 16 };
+
334 
+
335  private:
+
336  std::vector<System*> systems;
+
337  std::vector<std::shared_ptr<System>> non_node_systems;
+
338  // NB: SystmeNode<T> instances are group children
+
339  };
340 
-
341 
-
342  namespace ecs
-
343  {
-
344  namespace detail
-
345  {
-
346  // called by registry.emplace<T>()
-
347  template<typename T>
-
348  inline void SystemNode_on_construct(entt::registry& r, entt::entity e)
-
349  {
-
350  T& new_component = r.get<T>(e);
-
351 
-
352  // Add a visibility tag (if first time dealing with this component)
-
353  // I am not sure yet how to remove this in the end.
-
354  if (!r.try_get<Visibility>(e))
-
355  {
-
356  r.emplace<Visibility>(e);
-
357  }
-
358 
-
359  // Create a Renderable component and attach it to the new component.
-
360  new_component.attach_point = r.create();
-
361  r.emplace<ecs::Renderable>(new_component.attach_point);
-
362 
-
363  new_component.revision++;
-
364  }
-
365 
-
366  // invoked by registry.replace<T>(), emplace_or_replace<T>(), or patch<T>()
-
367  template<typename T>
-
368  inline void SystemNode_on_update(entt::registry& r, entt::entity e)
-
369  {
-
370  T& updated_component = r.get<T>(e);
-
371 
-
372  if (updated_component.attach_point == entt::null)
-
373  {
-
374  updated_component.attach_point = r.create();
-
375  r.emplace<ecs::Renderable>(updated_component.attach_point);
-
376  }
-
377 
-
378  updated_component.revision++;
-
379  }
+
345  inline bool visible(Visibility& vis, int view_index)
+
346  {
+
347  return vis.parent != nullptr ? visible(*vis.parent, view_index) : (vis.active && vis[view_index]);
+
348  }
+
349 
+
355  inline void setVisible(entt::registry& r, entt::entity e, bool value, int view_index = -1)
+
356  {
+
357  ROCKY_SOFT_ASSERT_AND_RETURN(e != entt::null, void());
+
358  auto& visibility = r.get<Visibility>(e);
+
359  if (visibility.parent == nullptr)
+
360  {
+
361  if (view_index >= 0)
+
362  visibility[view_index] = value;
+
363  else
+
364  visibility.setAll(value);
+
365  }
+
366  }
+
367 
+
373  inline bool visible(entt::registry& r, entt::entity e, int view_index = 0)
+
374  {
+
375  ROCKY_SOFT_ASSERT_AND_RETURN(e != entt::null, false);
+
376  return visible(r.get<Visibility>(e), view_index);
+
377  }
+
378  }
+
379 
380 
-
381  // invoked by registry.erase<T>(), remove<T>(), or registry.destroy(e)
-
382  template<typename T>
-
383  inline void SystemNode_on_destroy(entt::registry& r, entt::entity e)
-
384  {
-
385  T& component_being_destroyed = r.get<T>(e);
-
386  r.destroy(component_being_destroyed.attach_point);
-
387  }
-
388  }
-
389  }
-
390 
-
391  template<class T>
-
392  ecs::SystemNode<T>::SystemNode(entt::registry& in_registry) :
-
393  System(in_registry)
-
394  {
-
395  registry.on_construct<T>().template connect<&detail::SystemNode_on_construct<T>>();
-
396  registry.on_update<T>().template connect<&detail::SystemNode_on_update<T>>();
-
397  registry.on_destroy<T>().template connect<&detail::SystemNode_on_destroy<T>>();
-
398  }
-
399 
-
400  template<class T>
-
401  ecs::SystemNode<T>::~SystemNode()
-
402  {
-
403  registry.on_construct<T>().template disconnect<&detail::SystemNode_on_construct<T>>();
-
404  registry.on_update<T>().template disconnect<&detail::SystemNode_on_update<T>>();
-
405  registry.on_destroy<T>().template disconnect<&detail::SystemNode_on_destroy<T>>();
-
406  }
+
381  //........................................................................
+
382 
+
383 
+
384  namespace ecs
+
385  {
+
386  namespace detail
+
387  {
+
388  // called by registry.emplace<T>()
+
389  template<typename T>
+
390  inline void SystemNode_on_construct(entt::registry& r, entt::entity e)
+
391  {
+
392  T& new_component = r.get<T>(e);
+
393 
+
394  // Add a visibility tag (if first time dealing with this component)
+
395  // I am not sure yet how to remove this in the end.
+
396  if (!r.try_get<Visibility>(e))
+
397  {
+
398  r.emplace<Visibility>(e);
+
399  }
+
400 
+
401  // Create a Renderable component and attach it to the new component.
+
402  new_component.attach_point = r.create();
+
403  r.emplace<ecs::Renderable>(new_component.attach_point);
+
404 
+
405  new_component.revision++;
+
406  }
407 
-
408  template<class T>
-
409  inline void ecs::SystemNode<T>::traverse(vsg::Visitor& v)
-
410  {
-
411  for (auto& pipeline : pipelines)
-
412  {
-
413  pipeline.commands->accept(v);
-
414  }
-
415 
-
416  registry.view<T>().each([&](auto& c)
-
417  {
-
418  auto& renderable = registry.get<Renderable>(c.attach_point);
-
419  if (renderable.node)
-
420  renderable.node->accept(v);
-
421  });
+
408  // invoked by registry.replace<T>(), emplace_or_replace<T>(), or patch<T>()
+
409  template<typename T>
+
410  inline void SystemNode_on_update(entt::registry& r, entt::entity e)
+
411  {
+
412  T& updated_component = r.get<T>(e);
+
413 
+
414  if (updated_component.attach_point == entt::null)
+
415  {
+
416  updated_component.attach_point = r.create();
+
417  r.emplace<ecs::Renderable>(updated_component.attach_point);
+
418  }
+
419 
+
420  updated_component.revision++;
+
421  }
422 
-
423  super::traverse(v);
-
424  }
-
425 
-
427  template<class T>
-
428  inline void ecs::SystemNode<T>::traverse(vsg::ConstVisitor& v) const
-
429  {
-
430  for (auto& pipeline : pipelines)
-
431  {
-
432  pipeline.commands->accept(v);
-
433  }
-
434 
-
435  registry.view<T>().each([&](auto& c)
-
436  {
-
437  auto& renderable = registry.get<Renderable>(c.attach_point);
-
438  if (renderable.node)
-
439  renderable.node->accept(v);
-
440  });
+
423  // invoked by registry.erase<T>(), remove<T>(), or registry.destroy(e)
+
424  template<typename T>
+
425  inline void SystemNode_on_destroy(entt::registry& r, entt::entity e)
+
426  {
+
427  T& component_being_destroyed = r.get<T>(e);
+
428  r.destroy(component_being_destroyed.attach_point);
+
429  }
+
430  }
+
431  }
+
432 
+
433  template<class T>
+
434  ecs::SystemNode<T>::SystemNode(Registry& in_registry) :
+
435  System(in_registry)
+
436  {
+
437  registry.on_construct<T>().template connect<&detail::SystemNode_on_construct<T>>();
+
438  registry.on_update<T>().template connect<&detail::SystemNode_on_update<T>>();
+
439  registry.on_destroy<T>().template connect<&detail::SystemNode_on_destroy<T>>();
+
440  }
441 
-
442  super::traverse(v);
-
443  }
-
444 
-
445  template<class T>
-
446  inline void ecs::SystemNode<T>::compile(vsg::Context& context)
-
447  {
-
448  // Compile the pipelines
-
449  for (auto& pipeline : pipelines)
-
450  {
-
451  pipeline.commands->compile(context);
-
452  }
-
453 
-
454  // Compile the components
-
455  util::SimpleCompiler compiler(context);
-
456 
-
457  registry.view<T>().each([&](auto& c)
-
458  {
-
459  auto& renderable = registry.get<Renderable>(c.attach_point);
-
460  if (renderable.node)
-
461  renderable.node->accept(compiler);
-
462  });
-
463  }
+
442  template<class T>
+
443  ecs::SystemNode<T>::~SystemNode()
+
444  {
+
445  registry.on_construct<T>().template disconnect<&detail::SystemNode_on_construct<T>>();
+
446  registry.on_update<T>().template disconnect<&detail::SystemNode_on_update<T>>();
+
447  registry.on_destroy<T>().template disconnect<&detail::SystemNode_on_destroy<T>>();
+
448  }
+
449 
+
450  template<class T>
+
451  inline void ecs::SystemNode<T>::traverse(vsg::Visitor& v)
+
452  {
+
453  for (auto& pipeline : pipelines)
+
454  {
+
455  pipeline.commands->accept(v);
+
456  }
+
457 
+
458  registry.view<T>().each([&](auto& c)
+
459  {
+
460  auto& renderable = registry.get<Renderable>(c.attach_point);
+
461  if (renderable.node)
+
462  renderable.node->accept(v);
+
463  });
464 
-
465  template<class T>
-
466  inline void ecs::SystemNode<T>::traverse(vsg::RecordTraversal& rt) const
-
467  {
-
468  const vsg::dmat4 identity_matrix = vsg::dmat4(1.0);
-
469  auto viewID = rt.getState()->_commandBuffer->viewID;
-
470 
-
471  // Sort components into render sets by pipeline. If this system doesn't support
-
472  // multiple pipelines, just store them all together in renderSet[0].
-
473  if (pipelineRenderLeaves.empty())
-
474  {
-
475  pipelineRenderLeaves.resize(!pipelines.empty() ? pipelines.size() : 1);
-
476  }
-
477 
-
478  // Get an optimized view of all this system's components:
-
479  registry.view<T, Visibility>().each([&](const entt::entity entity, const T& component, auto& visibility)
-
480  {
-
481  auto& renderable = registry.get<Renderable>(component.attach_point);
-
482  if (renderable.node)
-
483  {
-
484  auto& leaves = !pipelines.empty() ? pipelineRenderLeaves[featureMask(component)] : pipelineRenderLeaves[0];
-
485  auto* transform = registry.try_get<Transform>(entity);
+
465  Inherit::traverse(v);
+
466  }
+
467 
+
469  template<class T>
+
470  inline void ecs::SystemNode<T>::traverse(vsg::ConstVisitor& v) const
+
471  {
+
472  for (auto& pipeline : pipelines)
+
473  {
+
474  pipeline.commands->accept(v);
+
475  }
+
476 
+
477  registry.view<T>().each([&](auto& c)
+
478  {
+
479  auto& renderable = registry.get<Renderable>(c.attach_point);
+
480  if (renderable.node)
+
481  renderable.node->accept(v);
+
482  });
+
483 
+
484  Inherit::traverse(v);
+
485  }
486 
-
487  // if it's visible, queue it up for rendering
-
488  if (visible(visibility, viewID))
-
489  {
-
490  leaves.emplace_back(RenderLeaf{ renderable, transform });
-
491  }
-
492 
-
493  // otherwise if it's invisible but still has a transform, process that transform
-
494  // so it can calculate its screen-space information (for things like decluttering
-
495  // and intersection)
-
496  else if (transform)
-
497  {
-
498  transform->push(rt, identity_matrix, false);
-
499  }
-
500  }
-
501 
-
502  // If our renderable has a new node waiting to be merged, queue it up
-
503  if (renderable.staged->has_value())
-
504  {
-
505  entities_to_update.emplace_back(entity);
-
506  }
-
507 
-
508  // Or if out renderabe is stale, queue it up for regeneration
-
509  else if (renderable.revision != component.revision)
-
510  {
-
511  entities_to_update.emplace_back(entity);
-
512  renderable.revision = component.revision;
-
513  }
-
514  });
-
515 
-
516  // Time to record all visible components. For each pipeline:
-
517  for (int p = 0; p < pipelineRenderLeaves.size(); ++p)
-
518  {
-
519  if (!pipelineRenderLeaves[p].empty())
-
520  {
-
521  // Bind the Graphics Pipeline for this render set, if there is one:
-
522  if (!pipelines.empty())
-
523  {
-
524  pipelines[p].commands->accept(rt);
-
525  }
-
526 
-
527  // Them record each component. If the component has a transform apply it too.
-
528  for (auto& leaf : pipelineRenderLeaves[p])
-
529  {
-
530  if (leaf.transform)
-
531  {
-
532  if (leaf.transform->push(rt, identity_matrix, true))
+
487  template<class T>
+
488  inline void ecs::SystemNode<T>::compile(vsg::Context& context)
+
489  {
+
490  // Compile the pipelines
+
491  for (auto& pipeline : pipelines)
+
492  {
+
493  pipeline.commands->compile(context);
+
494  }
+
495 
+
496  // Compile the components
+
497  util::SimpleCompiler compiler(context);
+
498 
+
499  registry.view<T>().each([&](auto& c)
+
500  {
+
501  auto& renderable = registry.get<Renderable>(c.attach_point);
+
502  if (renderable.node)
+
503  renderable.node->accept(compiler);
+
504  });
+
505  }
+
506 
+
507  template<class T>
+
508  inline void ecs::SystemNode<T>::traverse(vsg::RecordTraversal& rt) const
+
509  {
+
510  const vsg::dmat4 identity_matrix = vsg::dmat4(1.0);
+
511  auto viewID = rt.getState()->_commandBuffer->viewID;
+
512 
+
513  // Sort components into render sets by pipeline. If this system doesn't support
+
514  // multiple pipelines, just store them all together in renderSet[0].
+
515  if (pipelineRenderLeaves.empty())
+
516  {
+
517  pipelineRenderLeaves.resize(!pipelines.empty() ? pipelines.size() : 1);
+
518  }
+
519 
+
520  // Get an optimized view of all this system's components:
+
521  registry.view<T, Visibility>().each([&](const entt::entity entity, const T& component, auto& visibility)
+
522  {
+
523  if (visibility.active)
+
524  {
+
525  auto& renderable = registry.get<Renderable>(component.attach_point);
+
526  if (renderable.node)
+
527  {
+
528  auto& leaves = !pipelines.empty() ? pipelineRenderLeaves[featureMask(component)] : pipelineRenderLeaves[0];
+
529  auto* transform = registry.try_get<Transform>(entity);
+
530 
+
531  // if it's visible, queue it up for rendering
+
532  if (visible(visibility, viewID))
533  {
-
534  leaf.renderable.node->accept(rt);
-
535  leaf.transform->pop(rt);
-
536  }
-
537  }
-
538  else
-
539  {
-
540  leaf.renderable.node->accept(rt);
-
541  }
-
542  }
-
543 
-
544  // clear out for next time around.
-
545  pipelineRenderLeaves[p].clear();
-
546  }
-
547  }
-
548  }
-
549 
-
550 
-
551  template<class T>
-
552  inline void ecs::SystemNode<T>::update(Runtime& runtime)
-
553  {
-
554  std::vector<entt::entity> entities_to_create;
-
555 
-
556  for (auto& entity : entities_to_update)
-
557  {
-
558  T& component = registry.get<T>(entity);
-
559  Renderable& renderable = registry.get<Renderable>(component.attach_point);
+
534  leaves.emplace_back(RenderLeaf{ renderable, transform });
+
535  }
+
536 
+
537  // otherwise if it's invisible but still has a transform, process that transform
+
538  // so it can calculate its screen-space information (for things like decluttering
+
539  // and intersection)
+
540  else if (transform)
+
541  {
+
542  transform->push(rt, identity_matrix, false);
+
543  }
+
544  }
+
545 
+
546  // If our renderable has a new node waiting to be merged, queue it up
+
547  if (renderable.staged->has_value())
+
548  {
+
549  entities_to_update.emplace_back(entity);
+
550  }
+
551 
+
552  // Or if out renderabe is stale, queue it up for regeneration
+
553  else if (renderable.revision != component.revision)
+
554  {
+
555  entities_to_update.emplace_back(entity);
+
556  renderable.revision = component.revision;
+
557  }
+
558  }
+
559  });
560 
-
561  // if a staged node exists, swap it in.
-
562  vsg::ref_ptr<vsg::Node> new_node;
-
563  if (renderable.staged->get_and_clear(new_node))
-
564  {
-
565  if (renderable.node)
-
566  runtime.dispose(renderable.node);
-
567 
-
568  renderable.node = new_node;
-
569  }
-
570  else
-
571  {
-
572  // either the node doesn't exist yet, or the revision changed.
-
573  // Queue it up for creation.
-
574  entities_to_create.emplace_back(entity);
-
575  }
-
576  }
-
577 
-
578  // If we detected any entities that need new nodes, create them now.
-
579  if (!entities_to_create.empty())
-
580  {
-
581  if (SystemNodeBase::manager)
-
582  {
-
583  bool ok = SystemNodeBase::manager->entityCompileJobs.emplace(SystemNodeBase::EntityCompileBatch{
-
584  std::move(entities_to_create),
-
585  this,
-
586  &runtime });
-
587 
-
588  if (!ok)
-
589  {
-
590  Log()->warn("Failed to enqueue entity compile job - queue overflow");
-
591  }
-
592  }
-
593  }
+
561  // Time to record all visible components. For each pipeline:
+
562  for (int p = 0; p < pipelineRenderLeaves.size(); ++p)
+
563  {
+
564  if (!pipelineRenderLeaves[p].empty())
+
565  {
+
566  // Bind the Graphics Pipeline for this render set, if there is one:
+
567  if (!pipelines.empty())
+
568  {
+
569  pipelines[p].commands->accept(rt);
+
570  }
+
571 
+
572  // Them record each component. If the component has a transform apply it too.
+
573  for (auto& leaf : pipelineRenderLeaves[p])
+
574  {
+
575  if (leaf.transform)
+
576  {
+
577  if (leaf.transform->push(rt, identity_matrix, true))
+
578  {
+
579  leaf.renderable.node->accept(rt);
+
580  leaf.transform->pop(rt);
+
581  }
+
582  }
+
583  else
+
584  {
+
585  leaf.renderable.node->accept(rt);
+
586  }
+
587  }
+
588 
+
589  // clear out for next time around.
+
590  pipelineRenderLeaves[p].clear();
+
591  }
+
592  }
+
593  }
594 
-
595  entities_to_update.clear();
-
596  }
-
597 
-
598  template<class T>
-
599  void ecs::SystemNode<T>::create_or_update(entt::entity entity, SystemNodeBase::CreateOrUpdateData& data, Runtime& runtime) const
-
600  {
-
601  T& component = registry.get<T>(entity);
-
602  auto& renderable = registry.get<Renderable>(component.attach_point);
-
603  data.existing_node = renderable.node;
-
604  createOrUpdateNode(entity, data, runtime);
-
605  }
-
606 
-
607  template<class T>
-
608  void ecs::SystemNode<T>::finish_update(entt::entity entity, SystemNodeBase::CreateOrUpdateData& data) const
-
609  {
-
610  T& component = registry.get<T>(entity);
-
611  auto& renderable = registry.get<Renderable>(component.attach_point);
-
612  if (data.new_node)
-
613  {
-
614  renderable.staged->set(data.new_node);
-
615  }
-
616  }
-
617 
-
618  template<class T>
-
619  bool ecs::SystemNode<T>::setReferencePoint(const GeoPoint& point, SRSOperation& out_xform, vsg::dvec3& out_offset) const
-
620  {
-
621  if (point.srs.valid())
-
622  {
-
623  SRS worldSRS = point.srs;
-
624 
-
625  if (point.srs.isGeodetic())
-
626  {
-
627  worldSRS = point.srs.geocentricSRS();
-
628  GeoPoint world = point.transform(worldSRS);
-
629  if (world.valid())
-
630  {
-
631  out_offset = vsg::dvec3{ world.x, world.y, world.z };
-
632  }
-
633  }
-
634  else
-
635  {
-
636  out_offset = vsg::dvec3{ point.x, point.y, point.z };
-
637  }
-
638 
-
639  out_xform = SRSOperation(point.srs, worldSRS);
-
640  return true;
-
641  }
-
642  return false;
-
643  }
-
644 
-
645  template<class T>
-
646  vsg::ref_ptr<vsg::PipelineLayout> ecs::SystemNode<T>::getPipelineLayout(const T& t) const
-
647  {
-
648  return pipelines.empty() ? nullptr : pipelines[featureMask(t)].config->layout;
-
649  }
-
650 }
+
595 
+
596  template<class T>
+
597  inline void ecs::SystemNode<T>::update(Runtime& runtime)
+
598  {
+
599  std::vector<ecs::BuildItem> entities_to_build;
+
600 
+
601  for (auto& entity : entities_to_update)
+
602  {
+
603  T& component = registry.get<T>(entity);
+
604  Renderable& renderable = registry.get<Renderable>(component.attach_point);
+
605 
+
606  // if a staged node exists, swap it in.
+
607  vsg::ref_ptr<vsg::Node> new_node;
+
608  if (renderable.staged->get_and_clear(new_node))
+
609  {
+
610  if (renderable.node)
+
611  runtime.dispose(renderable.node);
+
612 
+
613  renderable.node = new_node;
+
614  }
+
615  else
+
616  {
+
617  // either the node doesn't exist yet, or the revision changed.
+
618  // Queue it up for creation.
+
619  ecs::BuildItem item;
+
620  item.entity = entity;
+
621  item.version = registry.current(entity);
+
622  item.component = new T(component);
+
623  item.existing_node = renderable.node;
+
624  item.new_node = {};
+
625 
+
626  entities_to_build.emplace_back(std::move(item));
+
627  }
+
628  }
+
629 
+
630  // If we detected any entities that need new nodes, create them now.
+
631  if (!entities_to_build.empty())
+
632  {
+
633  if (SystemNodeBase::manager) // gcc makes me do this :(
+
634  {
+
635  ecs::BuildBatch batch;
+
636  batch.items = std::move(entities_to_build);
+
637  batch.system = this;
+
638  batch.runtime = &runtime;
+
639 
+
640  bool ok = SystemNodeBase::manager->buildInput.emplace(std::move(batch));
+
641 
+
642  if (!ok)
+
643  {
+
644  Log()->warn("Failed to enqueue entity compile job - queue overflow");
+
645  }
+
646  }
+
647  }
+
648 
+
649  entities_to_update.clear();
+
650  }
+
651 
+
652  template<typename T>
+
653  void ecs::SystemNode<T>::invokeCreateOrUpdate(BuildItem& item, Runtime& runtime) const
+
654  {
+
655  createOrUpdateNode(*static_cast<T*>(item.component), item, runtime);
+
656  }
+
657 
+
658  template<typename T>
+
659  void ecs::SystemNode<T>::mergeCreateOrUpdateResults(BuildItem& item)
+
660  {
+
661  // if there's a new node AND the entity isn't outdated or deleted,
+
662  // swap it in. This method is called from SystemsManagerGroup::update().
+
663  if (item.new_node && registry.valid(item.entity) && registry.current(item.entity) == item.version)
+
664  {
+
665  T& component = registry.get<T>(item.entity);
+
666  auto& renderable = registry.get<Renderable>(component.attach_point);
+
667  if (item.new_node)
+
668  {
+
669  renderable.staged->set(item.new_node);
+
670  }
+
671  }
+
672  }
+
673 
+
674  template<class T>
+
675  bool ecs::SystemNode<T>::parseReferencePoint(const GeoPoint& point, SRSOperation& out_xform, vsg::dvec3& out_offset) const
+
676  {
+
677  if (point.srs.valid())
+
678  {
+
679  SRS worldSRS = point.srs;
+
680 
+
681  if (point.srs.isGeodetic())
+
682  {
+
683  worldSRS = point.srs.geocentricSRS();
+
684  GeoPoint world = point.transform(worldSRS);
+
685  if (world.valid())
+
686  {
+
687  out_offset = vsg::dvec3{ world.x, world.y, world.z };
+
688  }
+
689  }
+
690  else
+
691  {
+
692  out_offset = vsg::dvec3{ point.x, point.y, point.z };
+
693  }
+
694 
+
695  out_xform = SRSOperation(point.srs, worldSRS);
+
696  return true;
+
697  }
+
698  return false;
+
699  }
+
700 
+
701  template<class T>
+
702  vsg::ref_ptr<vsg::PipelineLayout> ecs::SystemNode<T>::getPipelineLayout(const T& t) const
+
703  {
+
704  return pipelines.empty() ? nullptr : pipelines[featureMask(t)].config->layout;
+
705  }
+
706 }
ROCKY_NAMESPACE::Runtime
Definition: Runtime.h:39
-
ROCKY_NAMESPACE::ecs::System
Definition: ECS.h:88
-
ROCKY_NAMESPACE::ecs::System::update
virtual void update(Runtime &runtime)
Update the ECS system (once per frame)
Definition: ECS.h:103
-
ROCKY_NAMESPACE::ecs::System::registry
entt::registry & registry
ECS entity registry.
Definition: ECS.h:91
-
ROCKY_NAMESPACE::ecs::System::status
Status status
Status.
Definition: ECS.h:94
-
ROCKY_NAMESPACE::ecs::System::initializeSystem
virtual void initializeSystem(Runtime &runtime)
Initialize the ECS system (once at startup)
Definition: ECS.h:97
-
ROCKY_NAMESPACE::ecs::SystemNodeBase
Definition: ECS.h:123
-
ROCKY_NAMESPACE::ecs::SystemsManagerGroup
Definition: ECS.h:219
-
ROCKY_NAMESPACE::ecs::SystemsManagerGroup::add
vsg::ref_ptr< T > add(entt::registry &entities)
Definition: ECS.h:236
-
ROCKY_NAMESPACE::ecs::SystemsManagerGroup::add
void add(std::shared_ptr< System > system)
Definition: ECS.h:250
-
ROCKY_NAMESPACE::ecs::SystemsManagerGroup::update
void update(Runtime &runtime)
Definition: ECS.h:278
-
ROCKY_NAMESPACE::ecs::SystemsManagerGroup::add
void add(vsg::ref_ptr< SystemNode< T >> system)
Definition: ECS.h:226
-
ROCKY_NAMESPACE::ecs::SystemsManagerGroup::initialize
void initialize(Runtime &runtime)
Definition: ECS.h:259
-
ROCKY_NAMESPACE::ecs::setVisible
void setVisible(entt::registry &r, entt::entity e, bool value, int view_index=-1)
Definition: ECS.h:315
-
ROCKY_NAMESPACE::ecs::visible
bool visible(Visibility &vis, int view_index)
Definition: ECS.h:305
+
ROCKY_NAMESPACE::ecs::System
Definition: ECS.h:101
+
ROCKY_NAMESPACE::ecs::System::registry
Registry & registry
ECS entity registry.
Definition: ECS.h:104
+
ROCKY_NAMESPACE::ecs::System::update
virtual void update(Runtime &runtime)
Update the ECS system (once per frame)
Definition: ECS.h:116
+
ROCKY_NAMESPACE::ecs::System::status
Status status
Status.
Definition: ECS.h:107
+
ROCKY_NAMESPACE::ecs::System::initializeSystem
virtual void initializeSystem(Runtime &runtime)
Initialize the ECS system (once at startup)
Definition: ECS.h:110
+
ROCKY_NAMESPACE::ecs::SystemNodeBase
Definition: ECS.h:182
+
ROCKY_NAMESPACE::ecs::SystemsManagerGroup
Definition: ECS.h:264
+
ROCKY_NAMESPACE::ecs::SystemsManagerGroup::add
vsg::ref_ptr< T > add(Registry &registry)
Definition: ECS.h:281
+
ROCKY_NAMESPACE::ecs::SystemsManagerGroup::add
void add(std::shared_ptr< System > system)
Definition: ECS.h:295
+
ROCKY_NAMESPACE::ecs::SystemsManagerGroup::add
void add(vsg::ref_ptr< SystemNode< T >> system)
Definition: ECS.h:271
+
ROCKY_NAMESPACE::ecs::SystemsManagerGroup::initialize
void initialize(Runtime &runtime)
Definition: ECS.h:304
+
ROCKY_NAMESPACE::ecs::setVisible
void setVisible(entt::registry &r, entt::entity e, bool value, int view_index=-1)
Definition: ECS.h:355
+
ROCKY_NAMESPACE::ecs::visible
bool visible(Visibility &vis, int view_index)
Definition: ECS.h:345
ROCKY_NAMESPACE
Definition: Callbacks.h:16
ROCKY_NAMESPACE::Status
Definition: Status.h:24
-
ROCKY_NAMESPACE::Visibility
Definition: ECS.h:76
-
ROCKY_NAMESPACE::ecs::Renderable
Definition: ECS.h:64
-
ROCKY_NAMESPACE::ecs::RevisionedComponent
Definition: ECS.h:46
-
ROCKY_NAMESPACE::ecs::RevisionedComponent::revision
int revision
Revision, for synchronizing this component with another.
Definition: ECS.h:48
-
ROCKY_NAMESPACE::ecs::RevisionedComponent::attach_point
entt::entity attach_point
Attach point for additional components, as needed.
Definition: ECS.h:52
+
ROCKY_NAMESPACE::Visibility
Definition: ECS.h:86
+
ROCKY_NAMESPACE::ecs::BuildInfo
Information passed to a system when creating or updating a node.
Definition: ECS.h:128
+
ROCKY_NAMESPACE::ecs::PerView
Definition: ECS.h:44
+
ROCKY_NAMESPACE::ecs::Renderable
Definition: ECS.h:74
+
ROCKY_NAMESPACE::ecs::RevisionedComponent
Definition: ECS.h:56
+
ROCKY_NAMESPACE::ecs::RevisionedComponent::revision
int revision
Revision, for synchronizing this component with another.
Definition: ECS.h:58
+
ROCKY_NAMESPACE::ecs::RevisionedComponent::attach_point
entt::entity attach_point
Attach point for additional components, as needed.
Definition: ECS.h:62