diff --git a/core/src/error/result.h b/core/src/error/result.h index a97d689..b643123 100644 --- a/core/src/error/result.h +++ b/core/src/error/result.h @@ -42,7 +42,7 @@ public: bool isSuccess() { return std::holds_alternative(value); } bool isError() { return std::holds_alternative(value); } - std::optional success() { return isSuccess() ? std::get(value).success : std::nullopt; } + std::optional success() { return isSuccess() ? std::make_optional(std::get(value).success) : std::nullopt; } std::optional> error() { return isError() ? std::make_optional(std::get(value).error) : std::nullopt; } void logError(Logger::LogLevel logLevel = Logger::LogLevel::ERROR) { diff --git a/core/src/objects/datamodel.h b/core/src/objects/datamodel.h index 5d46fff..c94bdcb 100644 --- a/core/src/objects/datamodel.h +++ b/core/src/objects/datamodel.h @@ -49,7 +49,7 @@ public: template result, NoSuchService> GetService(std::string name) { if (services.count(name) != 0) - return services[name]; + return std::dynamic_pointer_cast(services[name]); // TODO: Replace this with a result return type if (!INSTANCE_MAP[name] || (INSTANCE_MAP[name]->flags ^ (INSTANCE_NOTCREATABLE | INSTANCE_SERVICE)) != 0) { @@ -59,13 +59,13 @@ public: services[name] = std::dynamic_pointer_cast(INSTANCE_MAP[name]->constructor()); AddChild(std::dynamic_pointer_cast(services[name])); - return services[name]; + return std::dynamic_pointer_cast(services[name]); } template std::optional> FindService() { if (services.count(name) != 0) - return services[name]; + return std::dynamic_pointer_cast(services[name]); return std::nullopt; } diff --git a/core/src/objects/jointsservice.cpp b/core/src/objects/jointsservice.cpp new file mode 100644 index 0000000..23c5eb6 --- /dev/null +++ b/core/src/objects/jointsservice.cpp @@ -0,0 +1,23 @@ +#include "jointsservice.h" + +const InstanceType JointsService::TYPE = { + .super = &Instance::TYPE, + .className = "JointsService", + .constructor = &JointsService::Create, + .flags = INSTANCE_NOTCREATABLE | INSTANCE_SERVICE, +}; + +const InstanceType* JointsService::GetClass() { + return &TYPE; +} + + +JointsService::JointsService(): Service(&TYPE) { +} + +JointsService::~JointsService() = default; + +void JointsService::InitService() { + if (initialized) return; + initialized = true; +} diff --git a/core/src/objects/jointsservice.h b/core/src/objects/jointsservice.h new file mode 100644 index 0000000..3ccb3d4 --- /dev/null +++ b/core/src/objects/jointsservice.h @@ -0,0 +1,18 @@ +#pragma once + +#include "objects/base/service.h" + +class JointsService : public Service { +protected: + void InitService() override; + bool initialized = false; + +public: + const static InstanceType TYPE; + + JointsService(); + ~JointsService(); + + static inline std::shared_ptr Create() { return std::make_shared(); }; + virtual const InstanceType* GetClass() override; +}; \ No newline at end of file diff --git a/core/src/objects/meta.cpp b/core/src/objects/meta.cpp index f836ad6..908a78c 100644 --- a/core/src/objects/meta.cpp +++ b/core/src/objects/meta.cpp @@ -1,4 +1,5 @@ #include "meta.h" +#include "objects/jointsservice.h" #include "objects/part.h" #include "objects/snap.h" #include "objects/workspace.h" @@ -9,4 +10,5 @@ std::map INSTANCE_MAP = { { "Workspace", &Workspace::TYPE }, { "DataModel", &DataModel::TYPE }, { "Snap", &Snap::TYPE }, + { "JointsService", &JointsService::TYPE }, }; \ No newline at end of file diff --git a/core/src/objects/snap.cpp b/core/src/objects/snap.cpp index b3253b8..aba0714 100644 --- a/core/src/objects/snap.cpp +++ b/core/src/objects/snap.cpp @@ -3,6 +3,8 @@ #include "datatypes/cframe.h" #include "datatypes/ref.h" #include "datatypes/vector.h" +#include "objects/datamodel.h" +#include "objects/jointsservice.h" #include "workspace.h" #include "part.h" #include @@ -50,43 +52,67 @@ Snap::Snap(): Instance(&TYPE) { Snap::~Snap() { } -void Snap::OnWorkspaceAdded(std::optional> oldWorkspace, std::shared_ptr newWorkspace) { - // Remove the existing joint if it does - if (this->joint && oldWorkspace) { - oldWorkspace.value()->physicsWorld->destroyJoint(this->joint); +void Snap::OnAncestryChanged(std::optional>, std::optional>) { + // If the old workspace existed, and the new one differs, delete the current joint + if (this->joint && !this->oldWorkspace.expired() && (!workspace() || workspace().value() != this->oldWorkspace.lock())) { + // printf("Broke joint - Removed from workspace\n"); + oldJointWorkspace.lock()->physicsWorld->destroyJoint(this->joint); this->joint = nullptr; } - buildJoint(); -} + // If the previous parent was JointsService, and now it isn't, delete the joint + if (this->joint && !oldParent.expired() && oldParent.lock()->GetClass() == &JointsService::TYPE && (!GetParent() || GetParent() != oldParent.lock())) { + // printf("Broke joint - Removed from JointsService\n"); + oldJointWorkspace.lock()->physicsWorld->destroyJoint(this->joint); + this->joint = nullptr; + } -void Snap::OnWorkspaceRemoved(std::shared_ptr oldWorkspace) { - if (!this->joint) return; + // If the new workspace exists, and the old one differs, create the joint + if (!this->joint && workspace() && (oldWorkspace.expired() || oldWorkspace.lock() != workspace().value())) { + // printf("Made joint - Added to workspace\n"); + buildJoint(); + } - oldWorkspace->physicsWorld->destroyJoint(this->joint); - this->joint = nullptr; + // If the new parent is JointsService and the previous wasn't, then create the joint + if (!this->joint && GetParent() && GetParent().value()->GetClass() == &JointsService::TYPE && (oldParent.expired() || GetParent() != oldParent.lock())) { + // printf("Made joint - Added to JointsService\n"); + buildJoint(); + } + + this->oldParent = !GetParent() ? std::weak_ptr() : GetParent().value(); + this->oldWorkspace = !workspace() ? std::weak_ptr() : workspace().value(); + this->oldJointWorkspace = !jointWorkspace() ? std::weak_ptr() : jointWorkspace().value(); } void Snap::onUpdated(std::string property) { // We are not in the workspace, so we don't really care what values are currently set - if (!workspace()) return; + if (!jointWorkspace()) return; // Workspace cannot have changed, so if the joint currently exists, it is in the present one if (this->joint) - workspace().value()->physicsWorld->destroyJoint(this->joint); + jointWorkspace().value()->physicsWorld->destroyJoint(this->joint); buildJoint(); } void Snap::buildJoint() { - if (part0.expired() || part1.expired() || !workspace()) return; - + if (part0.expired() || part1.expired() || !jointWorkspace()) return; + // Update Part1's rotation and cframe prior to creating the joint as reactphysics3d locks rotation based on how it // used to be rather than specifying an anchor rotation, so whatever. Data::CFrame newFrame = part0.lock()->cframe * (c1.Inverse() * c0); part1.lock()->cframe = newFrame; - workspace().value()->SyncPartPhysics(part1.lock()); + jointWorkspace().value()->SyncPartPhysics(part1.lock()); rp::FixedJointInfo jointInfo(part0.lock()->rigidBody, part1.lock()->rigidBody, (c0.Inverse() * c1).Position()); - this->joint = dynamic_cast(workspace().value()->physicsWorld->createJoint(jointInfo)); + this->joint = dynamic_cast(jointWorkspace().value()->physicsWorld->createJoint(jointInfo)); +} + +std::optional> Snap::jointWorkspace() { + if (workspace()) return workspace(); + + if (GetParent() && GetParent().value()->GetClass() == &JointsService::TYPE) + return std::dynamic_pointer_cast(GetParent().value()->GetParent().value())->GetService("Workspace"); + + return {}; } \ No newline at end of file diff --git a/core/src/objects/snap.h b/core/src/objects/snap.h index 1617d8d..c621009 100644 --- a/core/src/objects/snap.h +++ b/core/src/objects/snap.h @@ -5,13 +5,20 @@ #include class Part; +class Workspace; class Snap : public Instance { rp::FixedJoint* joint = nullptr; -protected: - void OnWorkspaceAdded(std::optional> oldWorkspace, std::shared_ptr newWorkspace) override; - void OnWorkspaceRemoved(std::shared_ptr oldWorkspace) override; + std::weak_ptr oldParent; + // The actual workspace the joint is a part of + std::weak_ptr oldWorkspace; + // The pseudo-workspace the joint is a part of (including if parented to JointsService) + std::weak_ptr oldJointWorkspace; +protected: + void OnAncestryChanged(std::optional>, std::optional>) override; + + std::optional> jointWorkspace(); void onUpdated(std::string property); void buildJoint(); void breakJoint(); diff --git a/editor/mainwindow.cpp b/editor/mainwindow.cpp index 49f35b2..850bc32 100644 --- a/editor/mainwindow.cpp +++ b/editor/mainwindow.cpp @@ -2,6 +2,7 @@ #include "./ui_mainwindow.h" #include "common.h" #include "logger.h" +#include "objects/jointsservice.h" #include "objects/snap.h" #include #include @@ -169,7 +170,8 @@ MainWindow::MainWindow(QWidget *parent) snap->c0 = part1->cframe; snap->c1 = part0->cframe; - gWorkspace()->AddChild(snap); + // gWorkspace()->AddChild(snap); + gDataModel->GetService("JointsService").expect()->AddChild(snap); } void MainWindow::closeEvent(QCloseEvent* evt) {