diff --git a/core/src/objects/base/instance.cpp b/core/src/objects/base/instance.cpp index 1c94eb5..655d540 100644 --- a/core/src/objects/base/instance.cpp +++ b/core/src/objects/base/instance.cpp @@ -108,6 +108,15 @@ std::optional> Instance::GetParent() { return parent.value().lock(); } +static std::shared_ptr DUMMY_INSTANCE; +DescendantsIterator Instance::GetDescendantsStart() { + return DescendantsIterator(GetChildren().size() > 0 ? GetChildren()[0] : DUMMY_INSTANCE); +} + +DescendantsIterator Instance::GetDescendantsEnd() { + return DescendantsIterator(DUMMY_INSTANCE); +} + bool Instance::IsParentLocked() { return this->parentLocked; } @@ -231,4 +240,49 @@ InstanceRef Instance::Deserialize(pugi::xml_node* node) { } return object; +} + +// DescendantsIterator + +DescendantsIterator::DescendantsIterator(std::shared_ptr current) : current(current), root(current == DUMMY_INSTANCE ? DUMMY_INSTANCE : current->GetParent()), siblingIndex { 0 } { } + +DescendantsIterator::self_type DescendantsIterator::operator++(int _) { + // If the current item is dummy, an error has occurred, this is not supposed to happen. + if (current == DUMMY_INSTANCE) { + Logger::fatalError("Attempt to increment a descendant iterator past its end\n"); + panic(); + } + + // If the current item has children, enter it + if (current->GetChildren().size() > 0) { + siblingIndex.push_back(0); + current = current->GetChildren()[0]; + return *this; + } + + // Otherwise, we move to the next sibling, if applicable. + + // But not if one up is null or the root element + if (!current->GetParent() || current == root) { + current = DUMMY_INSTANCE; + return *this; + } + + // If we've hit the end of this item's children, move one up + while (current->GetParent() && current->GetParent().value()->GetChildren().size() <= (siblingIndex.back() + 1)) { + siblingIndex.pop_back(); + current = current->GetParent().value(); + + // But not if one up is null or the root element + if (!current->GetParent() || current == root) { + current = DUMMY_INSTANCE; + return *this; + } + } + + // Now move to the next sibling + siblingIndex.back()++; + current = current->GetParent().value()->GetChildren()[siblingIndex.back()]; + + return *this; } \ No newline at end of file diff --git a/core/src/objects/base/instance.h b/core/src/objects/base/instance.h index 9ccee98..9194baa 100644 --- a/core/src/objects/base/instance.h +++ b/core/src/objects/base/instance.h @@ -1,5 +1,6 @@ #pragma once +#include #include #include #include @@ -30,6 +31,8 @@ struct InstanceType { std::string explorerIcon = ""; }; +class DescendantsIterator; + // Base class for all instances in the data model // Note: enable_shared_from_this HAS to be public or else its field will not be populated // Maybe this could be replaced with a friendship? But that seems unnecessary. @@ -69,7 +72,9 @@ public: std::optional> GetParent(); bool IsParentLocked(); inline const std::vector> GetChildren() { return children; } - + + DescendantsIterator GetDescendantsStart(); + DescendantsIterator GetDescendantsEnd(); // Utility functions inline void AddChild(std::shared_ptr object) { object->SetParent(this->shared_from_this()); } @@ -87,4 +92,28 @@ public: }; typedef std::shared_ptr InstanceRef; -typedef std::weak_ptr InstanceRefWeak; \ No newline at end of file +typedef std::weak_ptr InstanceRefWeak; + +// https://gist.github.com/jeetsukumaran/307264 +class DescendantsIterator { +public: + typedef DescendantsIterator self_type; + typedef std::shared_ptr value_type; + typedef std::shared_ptr& reference; + typedef std::shared_ptr pointer; + typedef std::forward_iterator_tag iterator_category; + typedef int difference_type; + + DescendantsIterator(std::shared_ptr current); + inline self_type operator++() { self_type i = *this; ++*this; return i; } + inline std::shared_ptr operator*() { return current; } + inline std::shared_ptr operator->() { return current; } + inline bool operator==(const self_type& rhs) { return current == rhs.current; } + inline bool operator!=(const self_type& rhs) { return current != rhs.current; } + + self_type operator++(int _); +private: + std::optional> root; + std::shared_ptr current; + std::vector siblingIndex; +}; \ No newline at end of file diff --git a/core/src/physics/simulation.cpp b/core/src/physics/simulation.cpp index a1384c8..b055538 100644 --- a/core/src/physics/simulation.cpp +++ b/core/src/physics/simulation.cpp @@ -77,7 +77,8 @@ void physicsStep(float deltaTime) { // Naive implementation. Parts are only considered so if they are just under Workspace // TODO: Add list of tracked parts in workspace based on their ancestry using inWorkspace property of Instance - for (InstanceRef obj : gWorkspace()->GetChildren()) { + for (auto it = gWorkspace()->GetDescendantsStart(); it != gWorkspace()->GetDescendantsEnd(); it++) { + InstanceRef obj = *it; if (obj->GetClass()->className != "Part") continue; // TODO: Replace this with a .IsA call instead of comparing the class name directly std::shared_ptr part = std::dynamic_pointer_cast(obj); const rp::Transform& transform = part->rigidBody->getTransform(); diff --git a/core/src/rendering/renderer.cpp b/core/src/rendering/renderer.cpp index ade55ff..f878abd 100644 --- a/core/src/rendering/renderer.cpp +++ b/core/src/rendering/renderer.cpp @@ -125,7 +125,8 @@ void renderParts() { // Sort by nearest std::map> sorted; - for (InstanceRef inst : gWorkspace()->GetChildren()) { + for (auto it = gWorkspace()->GetDescendantsStart(); it != gWorkspace()->GetDescendantsEnd(); it++) { + InstanceRef inst = *it; if (inst->GetClass()->className != "Part") continue; std::shared_ptr part = std::dynamic_pointer_cast(inst); if (part->transparency > 0.00001) {