refactor(instance): implemented GetDescendant and replaced GetChildren in renderer and simulation with it

This commit is contained in:
maelstrom 2025-04-07 14:25:50 +02:00
parent 215f8ed500
commit 4de2b97c2d
4 changed files with 89 additions and 4 deletions

View file

@ -108,6 +108,15 @@ std::optional<std::shared_ptr<Instance>> Instance::GetParent() {
return parent.value().lock();
}
static std::shared_ptr<Instance> 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<Instance> 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;
}

View file

@ -1,5 +1,6 @@
#pragma once
#include <iterator>
#include <vector>
#include <memory>
#include <optional>
@ -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<std::shared_ptr<Instance>> GetParent();
bool IsParentLocked();
inline const std::vector<std::shared_ptr<Instance>> GetChildren() { return children; }
DescendantsIterator GetDescendantsStart();
DescendantsIterator GetDescendantsEnd();
// Utility functions
inline void AddChild(std::shared_ptr<Instance> object) { object->SetParent(this->shared_from_this()); }
@ -87,4 +92,28 @@ public:
};
typedef std::shared_ptr<Instance> InstanceRef;
typedef std::weak_ptr<Instance> InstanceRefWeak;
typedef std::weak_ptr<Instance> InstanceRefWeak;
// https://gist.github.com/jeetsukumaran/307264
class DescendantsIterator {
public:
typedef DescendantsIterator self_type;
typedef std::shared_ptr<Instance> value_type;
typedef std::shared_ptr<Instance>& reference;
typedef std::shared_ptr<Instance> pointer;
typedef std::forward_iterator_tag iterator_category;
typedef int difference_type;
DescendantsIterator(std::shared_ptr<Instance> current);
inline self_type operator++() { self_type i = *this; ++*this; return i; }
inline std::shared_ptr<Instance> operator*() { return current; }
inline std::shared_ptr<Instance> 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<std::shared_ptr<Instance>> root;
std::shared_ptr<Instance> current;
std::vector<int> siblingIndex;
};

View file

@ -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> part = std::dynamic_pointer_cast<Part>(obj);
const rp::Transform& transform = part->rigidBody->getTransform();

View file

@ -125,7 +125,8 @@ void renderParts() {
// Sort by nearest
std::map<float, std::shared_ptr<Part>> 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> part = std::dynamic_pointer_cast<Part>(inst);
if (part->transparency > 0.00001) {