sandbox
Loading...
Searching...
No Matches
scene_graph.hpp
1// SPDX-License-Identifier: MIT
2#ifndef LIBSBX_SCENES_SCENE_GRAPH_HPP_
3#define LIBSBX_SCENES_SCENE_GRAPH_HPP_
4
5#include <string>
6#include <unordered_map>
7
8#include <easy/profiler.h>
9
10#include <libsbx/utility/iterator.hpp>
11#include <libsbx/utility/logger.hpp>
12
13#include <libsbx/memory/tracking_allocator.hpp>
14
15#include <libsbx/ecs/registry.hpp>
16#include <libsbx/ecs/entity.hpp>
17
18#include <libsbx/math/uuid.hpp>
19#include <libsbx/math/vector3.hpp>
20#include <libsbx/math/quaternion.hpp>
21#include <libsbx/math/matrix4x4.hpp>
22
23#include <libsbx/scenes/node.hpp>
24#include <libsbx/scenes/components/id.hpp>
25#include <libsbx/scenes/components/tag.hpp>
26#include <libsbx/scenes/components/relationship.hpp>
27#include <libsbx/scenes/components/transform.hpp>
28#include <libsbx/scenes/components/global_transform.hpp>
29
30namespace sbx::scenes {
31
33
34public:
35
37
38 template<typename... Exclude>
39 inline static constexpr auto query_filter = ecs::exclude<Exclude...>;
40
42 : _registry{},
43 _root{_registry.create()} {
44 const auto& root_id = _registry.emplace<scenes::id>(_root);
45
46 _nodes.insert({root_id, _root});
47
48 _registry.emplace<scenes::relationship>(_root, scenes::node::null);
49 _registry.emplace<scenes::transform>(_root);
50 _registry.emplace<scenes::tag>(_root, "ROOT");
51 _registry.emplace<scenes::global_transform>(_root);
52 }
53
54 auto root() const -> scenes::node {
55 return _root;
56 }
57
58 auto node_count() const -> std::size_t {
59 return _nodes.size();
60 }
61
62 auto is_valid(const scenes::node node) const -> bool {
63 return _registry.is_valid(node);
64 }
65
66 auto find_node(const scenes::id& id) -> scenes::node {
67 if (auto entry = _nodes.find(id); entry != _nodes.end()) {
68 return entry->second;
69 }
70
71 return scenes::node::null;
72 }
73
74 auto create_node(const std::string& tag = "Node", const scenes::transform& transform = scenes::transform{}) -> scenes::node {
75 return create_child_node(_root, tag, transform);
76 }
77
78 auto create_child_node(const scenes::node parent, const std::string& tag = "Node", const scenes::transform& transform = scenes::transform{}) -> scenes::node {
79 auto node = _registry.create();
80
81 const auto& id = _registry.emplace<scenes::id>(node);
82
83 _nodes.insert({id, node});
84
85 _registry.emplace<scenes::relationship>(node, parent);
86 _registry.get<scenes::relationship>(parent).add_child(node);
87
88 _registry.emplace<scenes::global_transform>(node);
89 _registry.emplace<scenes::transform>(node, transform);
90 _registry.emplace<scenes::tag>(node, !tag.empty() ? tag : scenes::tag{"Node"});
91
92 return node;
93 }
94
95 auto destroy_node(const scenes::node node) -> void {
96 const auto& relationship = _registry.get<scenes::relationship>(node);
97
98 if (relationship.parent() != scenes::node::null) {
99 _registry.get<scenes::relationship>(relationship.parent()).remove_child(node);
100 }
101
102 auto stack = std::vector<std::pair<scenes::node, bool>>{};
103 stack.reserve(32u);
104
105 stack.emplace_back(node, false);
106
107 while (!stack.empty()) {
108 auto [current_node, visited] = stack.back();
109 stack.pop_back();
110
111 if (current_node == scenes::node::null) {
112 continue;
113 }
114
115 if (!visited) {
116 stack.emplace_back(current_node, true);
117
118 const auto& current_relationship = _registry.get<scenes::relationship>(current_node);
119
120 for (auto child : current_relationship.children()) {
121 if (child != node::null) {
122 stack.emplace_back(child, false);
123 }
124 }
125 } else {
126 const auto& id = _registry.get<scenes::id>(current_node);
127
128 _nodes.erase(id);
129 _registry.destroy(current_node);
130 }
131 }
132 }
133
134 auto make_child_of(const scenes::node node, const scenes::node parent) -> void {
135 if (node == parent) {
136 return;
137 }
138
139 auto& node_relationship = _registry.get<scenes::relationship>(node);
140
141 if (node_relationship.parent() == parent) {
142 return;
143 }
144
145 auto& new_parent_relationship = _registry.get<scenes::relationship>(parent);
146
147 if (node_relationship.parent() != scenes::node::null) {
148 auto& old_parent_relationship = _registry.get<scenes::relationship>(node_relationship.parent());
149
150 old_parent_relationship.remove_child(node);
151 }
152
153 node_relationship.set_parent(parent);
154 new_parent_relationship.add_child(node);
155 }
156
157 auto reassign_node_id(const scenes::node node, const math::uuid new_id) -> void {
158 _nodes.erase(_registry.get<scenes::id>(node));
159 _registry.get<scenes::id>(node) = scenes::id{new_id};
160 _nodes.insert({new_id, node});
161 }
162
163 template<typename Type, typename... Other, typename... Exclude>
164 auto query(ecs::exclude_t<Exclude...> = ecs::exclude_t{}) -> decltype(auto) {
165 return _registry.view<Type, Other...>(ecs::exclude<Exclude...>);
166 }
167
168 template<typename Type, typename... Other, typename... Exclude>
169 auto query(ecs::exclude_t<Exclude...> = ecs::exclude_t{}) const -> decltype(auto) {
170 return _registry.view<Type, Other...>(ecs::exclude<Exclude...>);
171 }
172
173 template<typename Type, typename Compare, typename Sort = utility::std_sort, typename... Args>
174 auto sort(Compare compare, Sort sort = Sort{}, Args&&... args) -> void {
175 _registry.sort<Type>(std::move(compare), std::move(sort), std::forward<Args>(args)...);
176 }
177
178 template<typename Component>
179 auto has_component(const scenes::node node) const -> bool {
180 return _registry.all_of<Component>(node);
181 }
182
183 template<typename Component, typename... Args>
184 auto add_component(const scenes::node node, Args&&... args) -> decltype(auto) {
185 return _registry.emplace<Component>(node, std::forward<Args>(args)...);
186 }
187
188 template<typename Component, typename... Args>
189 auto add_or_update_component(const scenes::node node, Args&&... args) -> decltype(auto) {
190 return _registry.emplace_or_replace<Component>(node, std::forward<Args>(args)...);
191 }
192
193 template<typename Component>
194 auto get_component(const scenes::node node) const -> const Component& {
195 return _registry.get<Component>(node);
196 }
197
198 template<typename Component>
199 auto get_component(const scenes::node node) -> Component& {
200 return _registry.get<Component>(node);
201 }
202
203 template<typename Component, typename... Args>
204 auto get_or_add_component(const scenes::node node, Args&&... args) -> Component& {
205 return _registry.get_or_emplace<Component>(node, std::forward<Args>(args)...);
206 }
207
208 template<typename Component>
209 auto try_get_component(const scenes::node node) -> memory::observer_ptr<Component> {
210 return _registry.try_get<Component>(node);
211 }
212
213 auto registry() -> registry_type& {
214 return _registry;
215 }
216
217 auto registry() const -> const registry_type& {
218 return _registry;
219 }
220
221 auto world_transform(const scenes::node node) -> math::matrix4x4 {
222 EASY_BLOCK("scene_graph::world_transform");
223 return _ensure_world(node).model;
224 }
225
226 auto world_normal(const scenes::node node) -> math::matrix4x4 {
227 EASY_BLOCK("scene_graph::world_normal");
228 return _ensure_world(node).normal;
229 }
230
231 auto parent_transform(const scenes::node node) -> math::matrix4x4 {
232 EASY_BLOCK("scene_graph::parent_transform");
233
234 const auto& relationship = _registry.get<scenes::relationship>(node);
235
236 if (relationship.parent() != scenes::node::null) {
237 return world_transform(relationship.parent());
238 }
239
240 return math::matrix4x4::identity;
241 }
242
243 auto world_position(const scenes::node node) -> math::vector3 {
244 EASY_BLOCK("scene_graph::world_position");
245 return math::vector3{world_transform(node)[3]};
246 }
247
248 auto world_rotation(const scenes::node node) -> math::quaternion {
249 EASY_BLOCK("scene_graph::world_rotation");
250 return math::quaternion{math::matrix4x4::rotation_basis(world_transform(node))};
251 }
252
253 auto world_scale(const scenes::node node) -> math::vector3 {
254 EASY_BLOCK("scene_graph::world_scale");
255
256 const auto world = world_transform(node);
257
258 return math::vector3{
259 math::vector3{world[0]}.length(),
260 math::vector3{world[1]}.length(),
261 math::vector3{world[2]}.length()
262 };
263 }
264
265private:
266
267 auto _ensure_world(const scenes::node node) -> const scenes::global_transform& {
268 auto chain = utility::make_array<scenes::node, 32u>(scenes::node::null);
269 auto chain_size = std::uint32_t{0};
270
271 for (auto current = node; current != scenes::node::null;) {
272 chain[chain_size++] = current;
273
274 const auto& relationship = _registry.get<scenes::relationship>(current);
275
276 current = relationship.parent();
277 }
278
279 auto parent_world = math::matrix4x4::identity;
280 auto parent_world_version = std::uint64_t{0};
281
282 for (auto i = chain_size - 1u; (i >= 0u && i < chain_size); --i) {
283 const auto current = chain[i];
284
285 auto& local = _registry.get<scenes::transform>(current);
286 auto& world = _registry.get<scenes::global_transform>(current);
287
288 if (world.local_seen != local.version() || world.parent_seen != parent_world_version) {
289 world.model = parent_world * local.local_transform();
290 world.normal = math::matrix4x4::transposed(math::matrix4x4::inverted(world.model));
291 world.local_seen = local.version();
292 world.parent_seen = parent_world_version;
293
294 ++world.version;
295 }
296
297 parent_world = world.model;
298 parent_world_version = world.version;
299 }
300
301 return _registry.get<scenes::global_transform>(node);
302 }
303
304 registry_type _registry;
305 scenes::node _root;
306 std::unordered_map<math::uuid, scenes::node> _nodes;
307
308}; // class scene_graph
309
310} // namespace sbx::scenes
311
312#endif // LIBSBX_SCENES_SCENE_GRAPH_HPP_
Definition: tests.cpp:6
Definition: matrix4x4.hpp:26
Definition: quaternion.hpp:25
Definition: uuid.hpp:160
Definition: vector3.hpp:23
A non-owning pointer that can be used to observe the value of a pointer.
Definition: observer_ptr.hpp:29
Definition: tag.hpp:12
Definition: id.hpp:9
Definition: relationship.hpp:14
Definition: scene_graph.hpp:32
Definition: transform.hpp:16
Definition: view.hpp:310
Definition: global_transform.hpp:9
Definition: algorithm.hpp:13