diff --git a/client/src/main.cpp b/client/src/main.cpp index 367fb17..f46bedc 100644 --- a/client/src/main.cpp +++ b/client/src/main.cpp @@ -45,12 +45,12 @@ int main() { glfwMakeContextCurrent(window); glewInit(); - dataModel->Init(); + gDataModel->Init(); simulationInit(); renderInit(window, 1200, 900); // Baseplate - workspace()->AddChild(Part::New({ + gWorkspace()->AddChild(Part::New({ .position = glm::vec3(0, -5, 0), .rotation = glm::vec3(0), .size = glm::vec3(512, 1.2, 512), @@ -58,14 +58,14 @@ int main() { .anchored = true, })); - workspace()->AddChild(lastPart = Part::New({ + gWorkspace()->AddChild(lastPart = Part::New({ .position = glm::vec3(0), .rotation = glm::vec3(0), .size = glm::vec3(4, 1.2, 2), .color = glm::vec3(0.639216f, 0.635294f, 0.647059f), })); - for (InstanceRef inst : workspace()->GetChildren()) { + for (InstanceRef inst : gWorkspace()->GetChildren()) { if (inst->GetClass()->className != "Part") continue; std::shared_ptr part = std::dynamic_pointer_cast(inst); syncPartPhysics(part); @@ -158,7 +158,7 @@ void mouseButtonCallback(GLFWwindow* window, int button, int action, int mods) { void keyCallback(GLFWwindow* window, int key, int scancode, int action, int mods) { if (key == GLFW_KEY_F && action == GLFW_PRESS) { - workspace()->AddChild(lastPart = Part::New({ + gWorkspace()->AddChild(lastPart = Part::New({ .position = camera.cameraPos + camera.cameraFront * glm::vec3(3), .rotation = glm::vec3(0), .size = glm::vec3(1, 1, 1), diff --git a/core/src/common.cpp b/core/src/common.cpp index 33068e2..f455225 100644 --- a/core/src/common.cpp +++ b/core/src/common.cpp @@ -5,7 +5,7 @@ Camera camera(glm::vec3(0.0, 0.0, 3.0)); //std::vector parts; -std::shared_ptr dataModel = DataModel::New(); +std::shared_ptr gDataModel = DataModel::New(); std::optional hierarchyPreUpdateHandler; std::optional hierarchyPostUpdateHandler; std::shared_ptr editorToolHandles = Handles::New(); diff --git a/core/src/common.h b/core/src/common.h index 484e96d..4a5f052 100644 --- a/core/src/common.h +++ b/core/src/common.h @@ -15,8 +15,8 @@ typedef std::function oldSelection, std::vecto // TEMPORARY COMMON DATA FOR VARIOUS INTERNAL COMPONENTS extern Camera camera; -extern std::shared_ptr dataModel; -inline std::shared_ptr workspace() { return std::dynamic_pointer_cast(dataModel->services["Workspace"]); } +extern std::shared_ptr gDataModel; +inline std::shared_ptr gWorkspace() { return std::dynamic_pointer_cast(gDataModel->services["Workspace"]); } extern std::optional hierarchyPreUpdateHandler; extern std::optional hierarchyPostUpdateHandler; extern std::shared_ptr editorToolHandles; diff --git a/core/src/objects/base/instance.cpp b/core/src/objects/base/instance.cpp index 271f59c..1c94eb5 100644 --- a/core/src/objects/base/instance.cpp +++ b/core/src/objects/base/instance.cpp @@ -72,6 +72,36 @@ bool Instance::SetParent(std::optional> newParent) { return true; } +std::optional> Instance::dataModel() { + // TODO: This algorithm will defer calculations to every time the root data model + // is accessed from any instance. This is inefficient as this can happen many times + // a tick. A better option is to cache these values and only update them if the ancestry + // changes, as that happens way less often. + + std::optional> currentParent = GetParent(); + while (currentParent) { + if (currentParent.value()->GetClass() == &DataModel::TYPE) + return std::dynamic_pointer_cast(currentParent.value()); + + currentParent = currentParent.value()->GetParent(); + } + + return std::nullopt; +} + +std::optional> Instance::workspace() { + // See comment in above function + std::optional> currentParent = GetParent(); + while (currentParent) { + if (currentParent.value()->GetClass() == &DataModel::TYPE) + return std::dynamic_pointer_cast(currentParent.value()); + + currentParent = currentParent.value()->GetParent(); + } + + return std::nullopt; +} + std::optional> Instance::GetParent() { if (!parent.has_value()) return std::nullopt; if (parent.value().expired()) return std::nullopt; diff --git a/core/src/objects/base/instance.h b/core/src/objects/base/instance.h index dacef44..9ccee98 100644 --- a/core/src/objects/base/instance.h +++ b/core/src/objects/base/instance.h @@ -19,6 +19,9 @@ class Instance; typedef std::shared_ptr(*InstanceConstructor)(); +class DataModel; +class Workspace; + // Struct describing information about an instance struct InstanceType { const InstanceType* super; // May be null @@ -48,6 +51,13 @@ protected: virtual void OnParentUpdated(std::optional> oldParent, std::optional> newParent); + // The root data model this object is a descendant of + std::optional> dataModel(); + // The root workspace this object is a descendant of + // NOTE: This value is not necessarily present if dataModel is present + // Objects under services other than workspace will NOT have this field set + std::optional> workspace(); + template inline std::shared_ptr shared() { return std::dynamic_pointer_cast(this->shared_from_this()); } public: const static InstanceType TYPE; diff --git a/core/src/physics/simulation.cpp b/core/src/physics/simulation.cpp index 6782e95..a1384c8 100644 --- a/core/src/physics/simulation.cpp +++ b/core/src/physics/simulation.cpp @@ -77,7 +77,7 @@ 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 : workspace()->GetChildren()) { + for (InstanceRef obj : gWorkspace()->GetChildren()) { 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 7f96356..ade55ff 100644 --- a/core/src/rendering/renderer.cpp +++ b/core/src/rendering/renderer.cpp @@ -125,7 +125,7 @@ void renderParts() { // Sort by nearest std::map> sorted; - for (InstanceRef inst : workspace()->GetChildren()) { + for (InstanceRef inst : gWorkspace()->GetChildren()) { if (inst->GetClass()->className != "Part") continue; std::shared_ptr part = std::dynamic_pointer_cast(inst); if (part->transparency > 0.00001) { @@ -288,7 +288,7 @@ void renderAABB() { ghostShader->set("color", glm::vec3(1.f, 0.f, 0.f)); // Sort by nearest - for (InstanceRef inst : workspace()->GetChildren()) { + for (InstanceRef inst : gWorkspace()->GetChildren()) { if (inst->GetClass()->className != "Part") continue; std::shared_ptr part = std::dynamic_pointer_cast(inst); glm::mat4 model = Data::CFrame::IDENTITY + part->cframe.Position(); diff --git a/editor/mainglwidget.cpp b/editor/mainglwidget.cpp index bd29ee2..b7b5284 100644 --- a/editor/mainglwidget.cpp +++ b/editor/mainglwidget.cpp @@ -400,7 +400,7 @@ void MainGLWidget::keyPressEvent(QKeyEvent* evt) { else if (evt->key() == Qt::Key_D) moveX = -1; if (evt->key() == Qt::Key_F) { - workspace()->AddChild(lastPart = Part::New({ + gWorkspace()->AddChild(lastPart = Part::New({ .position = camera.cameraPos + camera.cameraFront * glm::vec3(3), .rotation = glm::vec3(0), .size = glm::vec3(1, 1, 1), diff --git a/editor/mainwindow.cpp b/editor/mainwindow.cpp index 917091d..a020f25 100644 --- a/editor/mainwindow.cpp +++ b/editor/mainwindow.cpp @@ -49,7 +49,7 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , ui(new Ui::MainWindow) { - dataModel->Init(); + gDataModel->Init(); ui->setupUi(this); timer.start(33, this); @@ -136,11 +136,11 @@ MainWindow::MainWindow(QWidget *parent) if (result == QMessageBox::Cancel) return; if (result == QMessageBox::Save) { std::optional path; - if (!dataModel->HasFile()) - path = openFileDialog("Openblocks Level (*.obl)", ".obl", QFileDialog::AcceptSave, QString::fromStdString("Save " + dataModel->name)); + if (!gDataModel->HasFile()) + path = openFileDialog("Openblocks Level (*.obl)", ".obl", QFileDialog::AcceptSave, QString::fromStdString("Save " + gDataModel->name)); if (!path || path == "") return; - dataModel->SaveToFile(path); + gDataModel->SaveToFile(path); } #endif @@ -150,15 +150,15 @@ MainWindow::MainWindow(QWidget *parent) // TL;DR: This stinks and I need to fix it.) ui->mainWidget->lastPart = Part::New(); - dataModel = DataModel::New(); - dataModel->Init(); - ui->explorerView->updateRoot(dataModel); + gDataModel = DataModel::New(); + gDataModel->Init(); + ui->explorerView->updateRoot(gDataModel); // TODO: Remove this and use a proper fix. This *WILL* cause a leak and memory issues in the future simulationInit(); // Baseplate - workspace()->AddChild(ui->mainWidget->lastPart = Part::New({ + gWorkspace()->AddChild(ui->mainWidget->lastPart = Part::New({ .position = glm::vec3(0, -5, 0), .rotation = glm::vec3(0), .size = glm::vec3(512, 1.2, 512), @@ -171,25 +171,30 @@ MainWindow::MainWindow(QWidget *parent) connect(ui->actionSave, &QAction::triggered, this, [&]() { std::optional path; - if (!dataModel->HasFile()) - path = openFileDialog("Openblocks Level (*.obl)", ".obl", QFileDialog::AcceptSave, QString::fromStdString("Save " + dataModel->name)); + if (!gDataModel->HasFile()) + path = openFileDialog("Openblocks Level (*.obl)", ".obl", QFileDialog::AcceptSave, QString::fromStdString("Save " + gDataModel->name)); if (!path || path == "") return; - dataModel->SaveToFile(path); + gDataModel->SaveToFile(path); }); connect(ui->actionSaveAs, &QAction::triggered, this, [&]() { - std::optional path = openFileDialog("Openblocks Level (*.obl)", ".obl", QFileDialog::AcceptSave, QString::fromStdString("Save as " + dataModel->name)); + std::optional path = openFileDialog("Openblocks Level (*.obl)", ".obl", QFileDialog::AcceptSave, QString::fromStdString("Save as " + gDataModel->name)); if (!path || path == "") return; - dataModel->SaveToFile(path); + gDataModel->SaveToFile(path); }); connect(ui->actionOpen, &QAction::triggered, this, [&]() { std::optional path = openFileDialog("Openblocks Level (*.obl)", ".obl", QFileDialog::AcceptOpen); if (!path || path == "") return; + + // // See TODO: Also remove this (the reaso + // ui->mainWidget->lastPart = Part::New(); + + // simulationInit(); std::shared_ptr newModel = DataModel::LoadFromFile(path.value()); - dataModel = newModel; + gDataModel = newModel; ui->explorerView->updateRoot(newModel); }); @@ -242,7 +247,7 @@ MainWindow::MainWindow(QWidget *parent) for (pugi::xml_node instNode : rootDoc.children()) { InstanceRef inst = Instance::Deserialize(&instNode); - workspace()->AddChild(inst); + gWorkspace()->AddChild(inst); } }); @@ -322,7 +327,7 @@ MainWindow::MainWindow(QWidget *parent) simulationInit(); // Baseplate - workspace()->AddChild(ui->mainWidget->lastPart = Part::New({ + gWorkspace()->AddChild(ui->mainWidget->lastPart = Part::New({ .position = glm::vec3(0, -5, 0), .rotation = glm::vec3(0), .size = glm::vec3(512, 1.2, 512), @@ -332,7 +337,7 @@ MainWindow::MainWindow(QWidget *parent) ui->mainWidget->lastPart->name = "Baseplate"; syncPartPhysics(ui->mainWidget->lastPart); - workspace()->AddChild(ui->mainWidget->lastPart = Part::New({ + gWorkspace()->AddChild(ui->mainWidget->lastPart = Part::New({ .position = glm::vec3(0), .rotation = glm::vec3(0.5, 2, 1), .size = glm::vec3(4, 1.2, 2), @@ -354,11 +359,11 @@ void MainWindow::closeEvent(QCloseEvent* evt) { if (result == QMessageBox::Cancel) return evt->ignore(); if (result == QMessageBox::Save) { std::optional path; - if (!dataModel->HasFile()) - path = openFileDialog("Openblocks Level (*.obl)", ".obl", QFileDialog::AcceptSave, QString::fromStdString("Save " + dataModel->name)); + if (!gDataModel->HasFile()) + path = openFileDialog("Openblocks Level (*.obl)", ".obl", QFileDialog::AcceptSave, QString::fromStdString("Save " + gDataModel->name)); if (!path || path == "") return evt->ignore(); - dataModel->SaveToFile(path); + gDataModel->SaveToFile(path); } #endif } diff --git a/editor/panes/explorerview.cpp b/editor/panes/explorerview.cpp index 3a63f66..e1c407b 100644 --- a/editor/panes/explorerview.cpp +++ b/editor/panes/explorerview.cpp @@ -13,7 +13,7 @@ ExplorerView::ExplorerView(QWidget* parent): QTreeView(parent), - model(ExplorerModel(std::dynamic_pointer_cast(dataModel))) { + model(ExplorerModel(std::dynamic_pointer_cast(gDataModel))) { this->setModel(&model); // Disabling the root decoration will cause the expand/collapse chevrons to be hidden too, we don't want that @@ -29,7 +29,7 @@ ExplorerView::ExplorerView(QWidget* parent): this->setContextMenuPolicy(Qt::CustomContextMenu); // Expand workspace - this->expand(model.ObjectToIndex(workspace())); + this->expand(model.ObjectToIndex(gWorkspace())); connect(this, &QTreeView::customContextMenuRequested, this, [&](const QPoint& point) { QModelIndex index = this->indexAt(point);