TurtleBrains  0.3.5
High quality, portable, C++ framework for rapid 2D game development.
tb_node.hpp
1 
9 #ifndef TurtleBrains_Node_hpp
10 #define TurtleBrains_Node_hpp
11 
12 #include <turtle_brains/core/tb_configuration.hpp>
13 #include <turtle_brains/core/tb_noncopyable.hpp>
14 #include <turtle_brains/core/tb_uuid.hpp>
15 #include <turtle_brains/core/tb_typed_integer.hpp>
16 #include <turtle_brains/core/tb_typed_range.hpp>
17 #include <turtle_brains/core/tb_component.hpp>
18 
19 // 2023-10-15: Core technically shouldn't use anything except Core kit; but the Node is a pretty Core concept and Math
20 // is probably fine for Core to be using in this case. Either Node should move if this become a problem, or Core
21 // requires Math.
22 #include <turtle_brains/math/tb_vector.hpp>
23 #include <turtle_brains/math/tb_matrix.hpp>
24 
25 #include <vector>
26 #include <functional>
27 
28 namespace TurtleBrains::Core
29 {
30  //TurtleBrains doesn't (yet) have a concept of Paused, but Godot had an interesting ActivePolicy:
31  //enum class ActiveMode { Inherit, Always, Never, Pausable, WhenPaused };
32 
33  enum class Space { Local, World };
34  enum class Recursive { No, Yes };
35 
36  enum class NodeKeyType { };
37  using NodeKey = TypedUUID<NodeKeyType>;
38 
39  class Node;
40  using NodePointer = std::unique_ptr<Node>;
41 
42  namespace NodeImplementation
43  {
44  template<typename NodeType> struct NodeHierarchyAccessor
45  {
46  using ContainerType = std::vector<std::reference_wrapper<NodeType>>;
47 
48  ContainerType mRecursedNodes;
49 
50  NodeHierarchyAccessor(NodeType& node, const Recursive recursive = Recursive::Yes) : //, const CallTiming callTiming = CallTiming::Before) :
51  mRecursedNodes()
52  {
53  RecurseTree(mRecursedNodes, node, recursive);
54  }
55 
56  static void RecurseTree(ContainerType& recursedNodes, NodeType& node, const Recursive recursive = Recursive::Yes);
57 
58  typename ContainerType::const_iterator begin(void) const { return mRecursedNodes.begin(); }
59  typename ContainerType::const_iterator end(void) const { return mRecursedNodes.end(); }
60  typename ContainerType::iterator begin(void) { return mRecursedNodes.begin(); }
61  typename ContainerType::iterator end(void) { return mRecursedNodes.end(); }
62  };
63  };
64 
65  const NodePointer& GetRootNode(void);
66  NodePointer& GetMutableRootNode(void);
67  void SetRootNode(NodePointer&& rootNode);
68 
69  class Node : Noncopyable
70  {
71  public:
72  enum class ChildIndexType : uint16 { };
73 
75  static constexpr ChildIndex InvalidChild(void) { return ChildIndex::Integer(~0); }
76 
77  explicit Node(const NodeKey& nodeKey = NodeKey::Invalid());
78  virtual ~Node(void);
79 
83 
84  inline bool HasParent(void) const { return nullptr != mParent; }
85  inline Node& GetParent(void) const { return *mParent; }
86 
87  void ClearChildren(void);
88 
89  ChildIndex AddChild(const NodeKey& nodeKey = NodeKey::Invalid(), const String& name = "");
90  ChildIndex AddChild(std::unique_ptr<Node>&& childNode);
91 
93  // but the callee is responsible for keeping the added object alive until after it has been removed.
94  ChildIndex AddChild(Node& childNode);
95 
96  inline const ChildIndex GetNumberOfChildren(void) const { return tbCore::RangedCast<ChildIndex::Integer>(mChildren.size()); }
97  inline const Node& GetChild(const ChildIndex childIndex) const { return *mChildren[childIndex]; }
98  inline Node& GetChild(const ChildIndex childIndex) { return *mChildren[childIndex]; }
99 
100  using ConstChildContainerAccessor = OtherTypedRange<ChildIndex, const Node&>;
101  using ChildContainerAccessor = OtherTypedRange<ChildIndex, Node&>;
102  inline ConstChildContainerAccessor AllChildren(void) const {
103  return ConstChildContainerAccessor(std::bind(&Node::GetNumberOfChildren, this),
104  std::bind(static_cast<const Node& (Node::*)(const ChildIndex) const>(&Node::GetChild), this, std::placeholders::_1));
105  }
106 
107  inline ChildContainerAccessor AllChildren(void) {
108  return ChildContainerAccessor(std::bind(&Node::GetNumberOfChildren, this),
109  std::bind(static_cast<Node& (Node::*)(const ChildIndex)>(&Node::GetChild), this, std::placeholders::_1));
110  }
111 
112  inline NodeImplementation::NodeHierarchyAccessor<const Node> RecurseTree(const NodeKey& nodeKey, Recursive recursive = Recursive::Yes) const
113  {
114  const Node* startNode = (nodeKey == GetKey()) ? this : FindChildByKey(nodeKey, recursive);
115  tb_error_if(nullptr == startNode, "Expected to find the startNode for RecurseTree().");
116  return NodeImplementation::NodeHierarchyAccessor<const Node>(*startNode, recursive);
117  }
118 
119  inline NodeImplementation::NodeHierarchyAccessor<const Node> RecurseTree(Recursive recursive = Recursive::Yes) const
120  {
121  return NodeImplementation::NodeHierarchyAccessor<const Node>(*this, recursive);
122  }
123 
124  inline NodeImplementation::NodeHierarchyAccessor<Node> RecurseTree(const NodeKey& nodeKey, Recursive recursive = Recursive::Yes)
125  {
126  // 2024-10-12: Hmmm. This is Recursive::Yes instead of using the recursive value because we used it in
127  // the TrackBuilder::PrimaryEditorMode::SyncSelectionDots() which may or may not be recursive; but
128  // does need to find a child on the root recursively, before recursing. Feels a little odd, but was
129  // aiming for the most usable API, and this seems to be the usage of that situation at the cost of a
130  // potential performance trap?
131  Node* startNode = (nodeKey == GetKey()) ? this : FindChildByKey(nodeKey, Recursive::Yes);
132  tb_error_if(nullptr == startNode, "Expected to find the startNode for RecurseTree().");
133  return NodeImplementation::NodeHierarchyAccessor<Node>(*this, recursive);
134  }
135 
136  inline NodeImplementation::NodeHierarchyAccessor<Node> RecurseTree(Recursive recursive = Recursive::Yes)
137  {
138  return NodeImplementation::NodeHierarchyAccessor<Node>(*this, recursive);
139  }
140 
141  //Do want support for "child/path/to/other" type of searching, and stay consistent (or also add support) with
142  // the DynamicStructure GetMember() "child.path.to.other" "garage.racecar.wheel" "garage/racecar/wheel"
143  // we DO want to support that, but we DON'T yet (2024-10-12)
144  const Node* FindChildByName(const String& childName, Recursive recursive = Recursive::No) const;
145  Node* FindChildByName(const String& childName, Recursive recursive = Recursive::No);
146  const Node* FindChildByKey(const NodeKey& childNodeKey, Recursive recursive = Recursive::No) const;
147  Node* FindChildByKey(const NodeKey& childNodeKey, Recursive recursive = Recursive::No);
148  //Node* FindChildByTag(const String& tag, Recursive recursive = Recursive::No);
149 
150  // @note This function is never recursive, and returns the first child with matching name.
151  ChildIndex FindChildIndexByName(const String& childName) const;
152 
153  // @note This function is never recursive, and returns the first child with matching nodeKey.
154  ChildIndex FindChildIndexByKey(const NodeKey& childNodeKey) const;
155 
159 
161  Component* AddComponent(ComponentPointer&& component);
162 
163  template<typename ComponentType> ComponentType* AddComponent(void)
164  {
165  ComponentPointer componentPointer = ComponentCreator::CreateComponent<ComponentType>(*this);
166  if (nullptr == componentPointer)
167  {
168  return nullptr;
169  }
170 
171  return dynamic_cast<ComponentType*>(AddComponent(std::move(componentPointer)));
172  }
173 
174  const Component* GetComponent(const ComponentKey& componentKey) const;
175  Component* GetComponent(const ComponentKey& componentKey);
176  const Component* GetComponentByType(const ComponentTypeKey& componentTypeKey) const;
177  Component* GetComponentByType(const ComponentTypeKey& componentTypeKey);
178 
179  template<typename ComponentType> const ComponentType* GetComponent(void) const
180  {
181  return dynamic_cast<const ComponentType*>(GetComponentByType(ComponentType::ComponentTypeKey()));
182  }
183 
184  template<typename ComponentType> ComponentType* GetComponent(void)
185  {
186  return dynamic_cast<ComponentType*>(GetComponentByType(ComponentType::ComponentTypeKey()));
187  }
188 
189  template<typename ComponentType> const std::vector<const ComponentType*> GetComponents(void) const
190  {
191  std::vector<const ComponentType*> components;
192  components.reserve(mComponents.size());
193 
194  for (const ComponentPointer& component : mComponents)
195  {
196  if (nullptr == components.emplace_back(dynamic_cast<const ComponentType*>(component.get())))
197  {
198  components.pop_back();
199  }
200  }
201 
202  return components;
203  }
204 
205  template<typename ComponentType> std::vector<ComponentType*> GetComponents(void)
206  {
207  std::vector<ComponentType*> components;
208  components.reserve(mComponents.size());
209 
210  for (ComponentPointer& component : mComponents)
211  {
212  if (nullptr == components.emplace_back(dynamic_cast<ComponentType*>(component.get())))
213  {
214  components.pop_back();
215  }
216  }
217 
218  return components;
219  }
220 
221  //std::vector<const Component*>
222 
223  inline size_t GetNumberOfComponents(void) const { return mComponents.size(); }
224  const Component& GetComponent(const size_t componentIndex) const { return *mComponents[componentIndex]; }
225  Component& GetMutableComponent(const size_t componentIndex) { return *mComponents[componentIndex]; }
226 
228  bool IsActive(void) const;
229  inline bool IsActiveSelf(void) const { return mIsActive; }
230 
239  void SetActive(const bool active);
240 
241  inline const String& GetName(void) const { return mName; }
242  inline void SetName(const String& name) { mName = name; }
243 
244  inline const NodeKey& GetKey(void) const { return mNodeKey; }
245 
246  const tbMath::Matrix4& GetObjectToWorld(void) const;
247  void SetObjectToWorld(const tbMath::Matrix4& objectToWorld);
248 
249  const tbMath::Matrix4& GetObjectToParent(void) const;
250  void SetObjectToParent(const tbMath::Matrix4& objectToParent);
251 
252  inline tbMath::Vector3 GetPosition(void) const { return mNodeToWorld.GetPosition(); }
253  inline tbMath::Vector3 GetLocalPosition(void) const { return mNodeToParent.GetPosition(); }
254 
255  inline tbMath::Vector3 GetRight(void) const { return mNodeToWorld.GetBasis(0); }
256  inline tbMath::Vector3 GetUp(void) const { return mNodeToWorld.GetBasis(1); }
257  inline tbMath::Vector3 GetForward(void) const { return -mNodeToWorld.GetBasis(2); }
258  tbMath::Vector3 GetScale(const Space space = Space::Local) const;
259  void SetScale(const tbMath::Vector3& localScale);
260 
261  inline tbMath::Matrix3 GetOrientation(const Space space = Space::Local) const
262  {
263  return (Space::World == space) ? mNodeToWorld.GetOrientation() : mNodeToParent.GetOrientation();
264  }
265 
266  void Rotate(const tbMath::Vector3& eulerAngles, const Space space = Space::Local);
267  void Translate(const tbMath::Vector3& amount, const Space space = Space::Local);
268 
269  void Simulate(void);
270  void Update(const float deltaTime);
271  void Render(void) const;
272 
273  protected:
274  virtual void OnAwake(void);
275  virtual void OnDestroy(void);
276 
277  virtual void OnActivate(void);
278  virtual void OnDeactivate(void);
279 
280  virtual void OnSimulate(void);
281  virtual void OnUpdate(const float deltaTime);
282  virtual void OnRender(void) const;
283 
284  private:
285  void Activate(void);
286  void Deactivate(void);
287 
288  void RecomputeTransforms(void);
289 
290  const NodeKey mNodeKey;
291  String mName;
292  tbMath::Matrix4 mNodeToWorld;
293  tbMath::Matrix4 mNodeToParent;
294 
295  std::vector<Node*> mChildren;
296  std::vector<NodePointer> mManagedChildren;
297  std::vector<ComponentPointer> mComponents; //Always owned/managed.
298  Node* mParent;
299 
300  bool mIsActive;
301  };
302 
303  template<typename NodeType> void NodeImplementation::NodeHierarchyAccessor<NodeType>::RecurseTree(
304  ContainerType& recursedNodes, NodeType& node, const Recursive recursive)
305  {
306  recursedNodes.push_back(node);
307 
308  if (Recursive::Yes == recursive)
309  {
310  for (NodeType& child : node.AllChildren())
311  {
312  RecurseTree(recursedNodes, child, recursive);
313  }
314  }
315  }
316 
317 }; /* namespace TurtleBrains::Core */
318 
319 namespace tbCore = TurtleBrains::Core;
320 
321 #endif /* TurtleBrains_Node_hpp */
Definition: tb_component.hpp:95
Definition: tb_node.hpp:70
ChildIndex AddChild(Node &childNode)
void RemoveAllComponents(void)
void SetActive(const bool active)
bool HasParent(void) const
Definition: tb_node.hpp:84
Definition: tb_noncopyable.hpp:21
Definition: tb_uuid.hpp:33
Definition: tb_matrix.hpp:156
Definition: tb_matrix.hpp:596
TypedVector3< Type > GetBasis(const size_t &basisIndex) const
Definition: tb_matrix.hpp:948
TypedMatrix3< Type > GetOrientation(void) const
Definition: tb_matrix.hpp:1009
TypedVector3< Type > GetPosition(void) const
Definition: tb_matrix.hpp:978
Definition: tb_vector.hpp:472
#define tb_error_if(errorTest, message,...)
Definition: tb_error.hpp:42
Contains core functionality for each component of the API.
Definition: tb_debug_logger.hpp:125
std::uint16_t uint16
Unsigned integer with a size of 16 bits. Supports values from 0 to 65535.
Definition: tb_types.hpp:25
std::string String
Definition: tb_string.hpp:302
Definition: tb_typed_integer.hpp:49