Skip to main content

SceneNode.ixx File

Scene graph node representing a transform and optional renderable. More...

Included Headers

#include <cassert> #include <memory> #include <vector> #include <helios.math.TransformType> #include <helios.math.types> #include <helios.core.spatial.Transform> #include <helios.math.transform> #include <helios.util.Guid> #include <helios.rendering.Renderable> #include <helios.scene.SceneNode:SceneFwd>

Namespaces Index

namespacehelios
namespacescene

Scene graph and camera management. More...

Classes Index

structSceneGraphKey

SceneGraphKey as passkey idiom for accessing `setWorldTransform()`. All friend classes are able to construct the SceneGraphKey for accessing pass key guarded methods like `setWorldTransform`. More...

classSceneNode

Represents a SceneNode within a SceneGraph. More...

Description

Scene graph node representing a transform and optional renderable.

File Listing

The file content with the documentation metadata removed is:

1/**
2 * @file SceneNode.ixx
3 *
4 * @brief Scene graph node representing a transform and optional renderable
5 */
6module;
7
8#include <cassert>
9#include <memory>
10#include <vector>
11
12export module helios.scene.SceneNode;
13
14import :SceneFwd;
15
16import helios.rendering.Renderable;
17import helios.util.Guid;
18import helios.core.spatial.Transform;
19import helios.math.types;
20import helios.math.transform;
21import helios.math.TransformType;
22
23using namespace helios::core::spatial;
24
25export namespace helios::scene {
26
27 /**
28 * @brief SceneGraphKey as passkey idiom for accessing `setWorldTransform()`.
29 * All friend classes are able to construct the SceneGraphKey for accessing
30 * pass key guarded methods like `setWorldTransform`.
31 *
32 * @see `setWorldTransform`
33 *
34 * @see https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2024/p2893r3.html#passkey-idiom
35 */
36 struct SceneGraphKey {
37 private:
38 friend class helios::scene::Scene;
39 SceneGraphKey() = default;
40 };
41
42 /**
43 * @brief Represents a SceneNode within a SceneGraph.
44 *
45 * A SceneNode holds its local transformation and a (shared) pointer to
46 * a Renderable object (the representative of a visible element).
47 * Furthermore, a SceneNode can have multiple child nodes uniquely owned by
48 * **this** SceneNode.
49 *
50 * A SceneNode holds a global unique identifier and tracks a dirty state (`needsUpdate_`)
51 * related to any transformations that occurred on the SceneNode.
52 * Ownership is managed by a collection of unique pointers of the child nodes.
53 * SceneNodes are designed to be not copyable and not assignable to prevent duplication
54 * and to enforce the hierarchical tree structure.
55 *
56 * The `worldTransform_` is derived from a SceneNode's `localTransform_` and the SceneNode's
57 * parent's `worldTransform`s. A call to `worldTransform()` will update the particular subtree
58 * of the SceneNode bottom-up to make sure the actual values are available with the SceneNode's
59 * `worldTransform`. A Scene might also propagate changes top-bottom - see `Scene::updateNodes()`.
60 *
61 */
62 class SceneNode {
63
64 private:
65 /**
66 * @brief A unique identifier for this SceneNode, computed automatically
67 * during instantiation.
68 */
69 const util::Guid guid_;
70
71 protected:
72 /**
73 * @brief Boolean flag to represent the dirty-state of this node.
74 * Should be set to false if the current `worldTransform_`
75 * for this node was computed, otherwise true, e.g. if the node's
76 * local transform has been changed.
77 */
78 bool needsUpdate_ = true;
79
80 /**
81 * @brief True if this node is the root of the scene graph.
82 */
83 bool isRoot_ = false;
84
85 /**
86 * @brief A shared pointer to the Renderable this node represents in the
87 * SceneGraph. May be nullptr if this node does not represent a
88 */
89 std::shared_ptr<helios::rendering::Renderable> renderable_ = nullptr;
90
91 /**
92 * @brief The parent node of **this** node.
93 * This will be nullptr for any root node. Implementing APIs must
94 * take care of properly assigning parent nodes when adding
95 * child nodes.
96 */
97 SceneNode* parent_ = nullptr;
98
99 /**
100 * @brief A list of unique pointers to this node's children.
101 * Ensures ownership of the child nodes withing the graph hierarchy.
102 */
103 std::vector<std::unique_ptr<SceneNode>> children_;
104
105 /**
106 * @brief The 4x4 worldTransform-Matrix of this node.
107 * This matrix is initially set to the identity matrix.
108 * Implementing APIs MUST make sure to recompute the worldTransform
109 * before each rendering pass.
110 */
112
113 /**
114 * @brief A local transformation representing affine transformation
115 * information for this SceneNode. Encapsulates rotation, scaling and
116 * translation.
117 */
119
120 /**
121 * @brief Boolean flag indicating whether this SceneNode should be considered for
122 * rendering.
123 */
124 bool isActive_ = true;
125
126 /**
127 * @brief The axis-aligned-bounding-box for this SceneNode in world-coordinates.
128 */
130
131 /**
132 * @brief Flags controlling which transform components are inherited from the parent node.
133 *
134 * @details By default set to `TransformType::All`, meaning translation, rotation,
135 * and scale are all inherited from the parent's world transform. Use `setInheritance()`
136 * to selectively disable inheritance of specific components.
137 *
138 * @see setInheritance()
139 * @see inheritWorldTransform()
140 */
142
143 /**
144 * @brief Sets the parent of this SceneNode.
145 * Internally used once a child was added to this SceneNode.
146 *
147 * @param parentNode The parent of this SceneNode
148 *
149 * @see addNode()
150 */
151 void setParent(SceneNode* parentNode) {
152 parent_ = parentNode;
153 }
154
155 public:
156 virtual ~SceneNode() = default;
157
158 /**
159 * @brief Delete copy constructor.
160 * A SceneNode is not copyable.
161 */
162 SceneNode(const SceneNode &) = delete;
163
164 /**
165 * @brief Delete copy assignment operator.
166 *
167 * A SceneNode is not intended to be copied.
168 */
169 SceneNode& operator=(const SceneNode&) = delete;
170
171 /**
172 * @todo explicitly implement move (assignment) constructor if required,
173 * since we have deleted the copy constructors
174 */
175
176 /**
177 * @brief Constructs a new SceneNode that represents no renderable object.
178 * Nodes constructed in this way should be treated as transformation
179 * nodes.
180 */
181 SceneNode() noexcept :
182 guid_(util::Guid::generate())
183 {}
184
185 /**
186 * @brief Constructs a new SceneNode that represents no renderable object.
187 * Nodes constructed in this way should be treated as transformation
188 * nodes.
189 *
190 * @param transform The initial transformation for this node.
191 */
192 explicit SceneNode(const Transform& transform) noexcept :
193 guid_(util::Guid::generate()),
194 localTransform_(transform)
195 {}
196
197 /**
198 * @brief Constructs a new SceneNode representing a renderable object.
199 *
200 * @param renderable A shared_ptr to the Renderable this SceneNode
201 * represents in a Scene.
202 * @param transform The initial local transform for this node.
203 */
204 explicit SceneNode(
205 std::shared_ptr<helios::rendering::Renderable> renderable,
206 const Transform& transform
207 ) noexcept :
208 guid_(util::Guid::generate()),
209 renderable_(std::move(renderable)),
210 localTransform_(transform)
211 {}
212
213 /**
214 * @brief Constructs a new SceneNode representing a renderable object.
215 *
216 * @param renderable A shared_ptr to the Renderable this SceneNode
217 * represents in a Scene.
218 */
219 explicit SceneNode(
220 std::shared_ptr<helios::rendering::Renderable> renderable
221 ) noexcept :
222 guid_(util::Guid::generate()),
223 renderable_(std::move(renderable))
224 {}
225
226 /**
227 * @brief Returns the globally unique identifier for this SceneNode.
228 *
229 * @return The unique identifier for this SceneNode.
230 */
231 [[nodiscard]] const helios::util::Guid& guid() const noexcept {
232 return guid_;
233 }
234
235 /**
236 * @brief Adds a new child node to this SceneNode.
237 * The node's parent is automatically set to **this** node
238 *
239 * @param sceneNode The node to add as a child.
240 *
241 * @return The raw pointer to the newly added node, or nullptr if
242 * adding failed
243 */
244 [[nodiscard]] virtual SceneNode* addNode(std::unique_ptr<SceneNode> sceneNode) {
245 auto& ref = *children_.emplace_back(std::move(sceneNode));
246 ref.setParent(this);
247 return &ref;
248 }
249
250 /**
251 * @brief Returns a const ref to the list of this node's children.
252 *
253 * @return A const ref to the list of children of this node.
254 */
255 [[nodiscard]] const std::vector<std::unique_ptr<SceneNode>>& children() const noexcept {
256 return children_;
257 }
258
259 /**
260 * @brief Returns a raw pointer to the Renderable of this SceneNode.
261 *
262 * @return A raw pointer to the Renderable, may be nullptr if none is set.
263 */
264 [[nodiscard]] helios::rendering::Renderable* renderable() noexcept {
265 return renderable_.get();
266 }
267
268 /**
269 * @brief Returns true if this SceneNode was configured with a Renderable.
270 *
271 * @return true if the SceneNode was configured with a Renderable, otherwise false.
272 */
273 [[nodiscard]] bool hasRenderable() const noexcept {
274 return renderable() != nullptr;
275 }
276
277 /**
278 * @brief Returns a const pointer to the non-const Renderable of this SceneNode.
279 *
280 * @return A shared_ptr to the Renderable, may be nullptr if none is set.
281 */
282 [[nodiscard]] const helios::rendering::Renderable* renderable() const noexcept {
283 return renderable_.get();
284 }
285
286 /**
287 * @brief Returns a shared pointer to the non-const Renderable of this SceneNode.
288 *
289 * @return A shared_ptr to the Renderable, may be nullptr if none is set.
290 */
291 [[nodiscard]] std::shared_ptr<helios::rendering::Renderable> shareRenderable() noexcept {
292 return renderable_;
293 }
294
295 /**
296 * @brief Applies a scaling transformation to this node's **local** transform.
297 * Marks the node dirty. Implementing APIs should consider updating the
298 * worldTransform of this node before rendering.
299 *
300 * @param scale The helios::math::vec3f representing the node's scaling factors.
301 *
302 * @return A reference to this SceneNode.
303 */
304 SceneNode& setScale(const helios::math::vec3f& scale) noexcept {
305 localTransform_.setScale(scale);
306 needsUpdate_ = true;
307 return *this;
308 }
309
310 /**
311 * @brief Applies rotation to this node's **local** transform.
312 * Marks the node dirty. Implementing APIs should consider updating the
313 * worldTransform of this node before rendering.
314 *
315 * @param rotation The helios::math::mat4f rotation matrix.
316 *
317 * @return A reference to this SceneNode.
318 */
319 SceneNode& setRotation(const helios::math::mat4f& rotation) noexcept {
320 localTransform_.setRotation(rotation);
321 needsUpdate_ = true;
322 return *this;
323 }
324
325 /**
326 * @brief Applies translation to this node's **local** transform.
327 * Marks the node dirty. Implementing APIs should consider updating the
328 * worldTransform of this node before rendering.
329 *
330 * @param translation The helios::math::vec3f representing the node's
331 * translation.
332 *
333 * @return A reference to this SceneNode.
334 */
335 SceneNode& setTranslation(const helios::math::vec3f& translation) noexcept {
336 localTransform_.setTranslation(translation);
337 needsUpdate_ = true;
338 return *this;
339 }
340
341 /**
342 * @brief Returns this SceneNode's localTransform.
343 *
344 * @return A const reference to this SceneNode's `Transform` object.
345 */
346 [[nodiscard]] const Transform& localTransform() const noexcept {
347 return localTransform_;
348 }
349
350 /**
351 * @brief Returns this SceneNode's localTransform.
352 *
353 * @return A reference to this SceneNode's `Transform` object.
354 */
355 [[nodiscard]] Transform& localTransform() noexcept {
356 return localTransform_;
357 }
358
359 /**
360 * @brief Returns a pointer to this node's parent node.
361 *
362 * @return The pointer to this node's parent, or nullptr if no parent exists,
363 * e.g. for the root node.
364 */
365 [[nodiscard]] SceneNode* parent() const noexcept {
366 return parent_;
367 }
368
369 /**
370 * @brief Sets the world transform for this SceneNode.
371 *
372 * @details
373 * Does nothing if the current SceneNode's world transform is considered
374 * equal to the specified world transform.
375 *
376 * @param wf The worldTransform to use for this SceneNode.
377 * @param sceneGraphKey The sceneGraphKey as constructed by the `Scene`. Serves
378 * as the passkey so this method is not callable from outside.
379 *
380 * @return true if the world transform was updated, otherwise false.
381 */
382 [[nodiscard]] bool applyWorldTransform(
383 const helios::math::mat4f& parentWorldTransform, helios::scene::SceneGraphKey sceneGraphKey
384 ) noexcept {
385
386 const auto newWt = inheritWorldTransform(parentWorldTransform);
387
388 if (!needsUpdate() && worldTransform_.same(newWt)) {
389 return false;
390 }
391
392 worldTransform_ = newWt;
393 needsUpdate_ = false;
394
395
397 return true;
398 }
399
400 /**
401 * @brief Marks this node as the root of the scene graph.
402 *
403 * @param sceneGraphKey The passkey for scene-graph-only access.
404 */
405 void setIsRoot(helios::scene::SceneGraphKey sceneGraphKey) noexcept {
406 isRoot_ = true;
407 }
408
409 /**
410 * @brief Checks whether this node is the root of the scene graph.
411 *
412 * @return True if this is the root node.
413 */
414 [[nodiscard]] bool isRoot() const noexcept {
415 return isRoot_;
416 }
417
418 /**
419 * @brief Computes and returns the world transform for this SceneNode.
420 * Will request the current world transform from the
421 * parent node to calculate this SceneNode's world transform.
422 * For efficiency, this method should cache the result
423 * and only trigger a recompute if this SceneNode is dirty (`needsUpdate_ == true`).
424 *
425 * @return The current world transform matrix for this scene node.
426 */
428 if (needsUpdate()) {
429 update();
430 }
431
432 return worldTransform_;
433 }
434
435 /**
436 * @brief Returns the current worldTransform matrix of this SceneNode
437 * **without triggering any recomputation**.
438 * This method is efficient for access when the world transform is
439 * known to be up-to-date.
440 *
441 * @return The current worldTransform matrix of this SceneNode.
442 */
443 [[nodiscard]] const helios::math::mat4f& cachedWorldTransform() const noexcept {
444 return worldTransform_;
445 }
446
447 /**
448 * @brief Checks whether this SceneNode needs to be updated.
449 *
450 * The SceneNode is considered "dirty" if either the `localTransform_` needs an
451 * update, or if the `needsUpdate_` property is set to true.
452 *
453 * @return true if this SceneNode is considered to be dirty, otherwise false.
454 */
455 [[nodiscard]] bool needsUpdate() const noexcept {
456 return needsUpdate_ || localTransform_.needsUpdate();
457 }
458
459
460 /**
461 * @brief Returns the axis-aligned bounding box for this SceneNode in world coordinates.
462 *
463 * @return The AABB transformed to world space.
464 */
465 [[nodiscard]] helios::math::aabbf aabb() noexcept {
466 if (needsUpdate()) {
467 update();
468 }
469
470 return aabb_;
471 }
472
473 /**
474 * @brief Updates the world transform and axis-aligned bounding box of this SceneNode.
475 *
476 * Recomputes the `worldTransform_` based on the parent's world transform and
477 * this node's `localTransform_`. Also updates the `aabb_` to reflect the
478 * current world-space bounds.
479 */
480 void update() noexcept {
481 needsUpdate_ = false;
482
483 if (parent_ == nullptr) {
484 worldTransform_ = localTransform_.transform();
485 } else {
487 }
488
490 }
491
492 /**
493 * @brief Virtual callback invoked after the world transform has been updated.
494 *
495 * Derived classes can override this method to perform additional processing
496 * when the node's world transform changes, such as updating dependent
497 * resources or triggering view matrix recalculations in camera nodes.
498 */
499 virtual void onWorldTransformUpdate() noexcept {
500 if (renderable_) {
501 const auto& localAABB = renderable_->localAABB();
502 aabb_ = localAABB.applyTransform(worldTransform_);
503 }
504 }
505
506 /**
507 * @brief Filters a parent world transform based on the node's inheritance flags.
508 *
509 * This method extracts only the transform components (Translation, Rotation, Scale)
510 * that this node is configured to inherit from its parent. Components not included
511 * in `inheritance_` are replaced with identity values.
512 *
513 * @param parentWorldTransform The parent's world transform matrix.
514 *
515 * @return A filtered transform matrix containing only the inherited components.
516 *
517 * @see setInheritance()
518 * @see helios::math::TransformType
519 */
520 helios::math::mat4f inheritWorldTransform(const helios::math::mat4f& parentWorldTransform) noexcept {
521 using namespace helios::math;
522
524 return parentWorldTransform * localTransform_.transform();
525 }
526
527 auto id = parentWorldTransform.decompose(inheritance_);
528
529 return id * localTransform_.transform();
530 }
531
532 /**
533 * @brief Sets which transform components this node inherits from its parent.
534 *
535 * By default, nodes inherit all transform components (`TransformType::All`).
536 * Use this method to selectively inherit only Translation, Rotation, or Scale,
537 * enabling behaviors like cameras that follow an object's position but maintain
538 * independent orientation.
539 *
540 * @param inherit The inheritance flags to apply.
541 *
542 * @see helios::math::TransformType
543 */
544 void setInheritance(const helios::math::TransformType inherit) noexcept {
545 inheritance_ = inherit;
546 needsUpdate_ = true;
547 }
548
549 /**
550 * @brief Returns the current transform inheritance flags for this node.
551 *
552 * @return The active inheritance mask.
553 *
554 * @see setInheritance()
555 */
556 [[nodiscard]] helios::math::TransformType inheritance() const noexcept {
557 return inheritance_;
558 }
559
560 /**
561 * @brief Returns whether this SceneNode and its child nodes should be considered for rendering.
562 *
563 * @return true if the SceneNode should be considered for rendering, otherwise false.
564 */
565 [[nodiscard]] bool isActive() const noexcept {
566 return isActive_;
567 }
568
569 /**
570 * @brief Sets the active state of this SceneNode.
571 *
572 * The active state determines whether the SceneNode should be considered for rendering.
573 *
574 * @param active true to consider this SceneNodefor rendering, otherwise false.
575 */
576 void setActive(const bool active) noexcept {
577 isActive_ = active;
578 }
579 };
580
581} // namespace helios::scene
582

Generated via doxygen2docusaurus 2.0.0 by Doxygen 1.15.0.