refactor(instance): added functions for accessing root datamodel and renamed to gDataModel

This commit is contained in:
maelstrom 2025-04-07 13:14:10 +02:00
parent a47d2c2b57
commit 215f8ed500
10 changed files with 79 additions and 34 deletions

View file

@ -45,12 +45,12 @@ int main() {
glfwMakeContextCurrent(window); glfwMakeContextCurrent(window);
glewInit(); glewInit();
dataModel->Init(); gDataModel->Init();
simulationInit(); simulationInit();
renderInit(window, 1200, 900); renderInit(window, 1200, 900);
// Baseplate // Baseplate
workspace()->AddChild(Part::New({ gWorkspace()->AddChild(Part::New({
.position = glm::vec3(0, -5, 0), .position = glm::vec3(0, -5, 0),
.rotation = glm::vec3(0), .rotation = glm::vec3(0),
.size = glm::vec3(512, 1.2, 512), .size = glm::vec3(512, 1.2, 512),
@ -58,14 +58,14 @@ int main() {
.anchored = true, .anchored = true,
})); }));
workspace()->AddChild(lastPart = Part::New({ gWorkspace()->AddChild(lastPart = Part::New({
.position = glm::vec3(0), .position = glm::vec3(0),
.rotation = glm::vec3(0), .rotation = glm::vec3(0),
.size = glm::vec3(4, 1.2, 2), .size = glm::vec3(4, 1.2, 2),
.color = glm::vec3(0.639216f, 0.635294f, 0.647059f), .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; if (inst->GetClass()->className != "Part") continue;
std::shared_ptr<Part> part = std::dynamic_pointer_cast<Part>(inst); std::shared_ptr<Part> part = std::dynamic_pointer_cast<Part>(inst);
syncPartPhysics(part); 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) { void keyCallback(GLFWwindow* window, int key, int scancode, int action, int mods) {
if (key == GLFW_KEY_F && action == GLFW_PRESS) { 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), .position = camera.cameraPos + camera.cameraFront * glm::vec3(3),
.rotation = glm::vec3(0), .rotation = glm::vec3(0),
.size = glm::vec3(1, 1, 1), .size = glm::vec3(1, 1, 1),

View file

@ -5,7 +5,7 @@
Camera camera(glm::vec3(0.0, 0.0, 3.0)); Camera camera(glm::vec3(0.0, 0.0, 3.0));
//std::vector<Part> parts; //std::vector<Part> parts;
std::shared_ptr<DataModel> dataModel = DataModel::New(); std::shared_ptr<DataModel> gDataModel = DataModel::New();
std::optional<HierarchyPreUpdateHandler> hierarchyPreUpdateHandler; std::optional<HierarchyPreUpdateHandler> hierarchyPreUpdateHandler;
std::optional<HierarchyPostUpdateHandler> hierarchyPostUpdateHandler; std::optional<HierarchyPostUpdateHandler> hierarchyPostUpdateHandler;
std::shared_ptr<Handles> editorToolHandles = Handles::New(); std::shared_ptr<Handles> editorToolHandles = Handles::New();

View file

@ -15,8 +15,8 @@ typedef std::function<void(std::vector<InstanceRefWeak> oldSelection, std::vecto
// TEMPORARY COMMON DATA FOR VARIOUS INTERNAL COMPONENTS // TEMPORARY COMMON DATA FOR VARIOUS INTERNAL COMPONENTS
extern Camera camera; extern Camera camera;
extern std::shared_ptr<DataModel> dataModel; extern std::shared_ptr<DataModel> gDataModel;
inline std::shared_ptr<Workspace> workspace() { return std::dynamic_pointer_cast<Workspace>(dataModel->services["Workspace"]); } inline std::shared_ptr<Workspace> gWorkspace() { return std::dynamic_pointer_cast<Workspace>(gDataModel->services["Workspace"]); }
extern std::optional<HierarchyPreUpdateHandler> hierarchyPreUpdateHandler; extern std::optional<HierarchyPreUpdateHandler> hierarchyPreUpdateHandler;
extern std::optional<HierarchyPostUpdateHandler> hierarchyPostUpdateHandler; extern std::optional<HierarchyPostUpdateHandler> hierarchyPostUpdateHandler;
extern std::shared_ptr<Handles> editorToolHandles; extern std::shared_ptr<Handles> editorToolHandles;

View file

@ -72,6 +72,36 @@ bool Instance::SetParent(std::optional<std::shared_ptr<Instance>> newParent) {
return true; return true;
} }
std::optional<std::shared_ptr<DataModel>> 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<std::shared_ptr<Instance>> currentParent = GetParent();
while (currentParent) {
if (currentParent.value()->GetClass() == &DataModel::TYPE)
return std::dynamic_pointer_cast<DataModel>(currentParent.value());
currentParent = currentParent.value()->GetParent();
}
return std::nullopt;
}
std::optional<std::shared_ptr<Workspace>> Instance::workspace() {
// See comment in above function
std::optional<std::shared_ptr<Instance>> currentParent = GetParent();
while (currentParent) {
if (currentParent.value()->GetClass() == &DataModel::TYPE)
return std::dynamic_pointer_cast<Workspace>(currentParent.value());
currentParent = currentParent.value()->GetParent();
}
return std::nullopt;
}
std::optional<std::shared_ptr<Instance>> Instance::GetParent() { std::optional<std::shared_ptr<Instance>> Instance::GetParent() {
if (!parent.has_value()) return std::nullopt; if (!parent.has_value()) return std::nullopt;
if (parent.value().expired()) return std::nullopt; if (parent.value().expired()) return std::nullopt;

View file

@ -19,6 +19,9 @@
class Instance; class Instance;
typedef std::shared_ptr<Instance>(*InstanceConstructor)(); typedef std::shared_ptr<Instance>(*InstanceConstructor)();
class DataModel;
class Workspace;
// Struct describing information about an instance // Struct describing information about an instance
struct InstanceType { struct InstanceType {
const InstanceType* super; // May be null const InstanceType* super; // May be null
@ -48,6 +51,13 @@ protected:
virtual void OnParentUpdated(std::optional<std::shared_ptr<Instance>> oldParent, std::optional<std::shared_ptr<Instance>> newParent); virtual void OnParentUpdated(std::optional<std::shared_ptr<Instance>> oldParent, std::optional<std::shared_ptr<Instance>> newParent);
// The root data model this object is a descendant of
std::optional<std::shared_ptr<DataModel>> 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<std::shared_ptr<Workspace>> workspace();
template <typename T> inline std::shared_ptr<T> shared() { return std::dynamic_pointer_cast<T>(this->shared_from_this()); } template <typename T> inline std::shared_ptr<T> shared() { return std::dynamic_pointer_cast<T>(this->shared_from_this()); }
public: public:
const static InstanceType TYPE; const static InstanceType TYPE;

View file

@ -77,7 +77,7 @@ void physicsStep(float deltaTime) {
// Naive implementation. Parts are only considered so if they are just under Workspace // 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 // 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 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); std::shared_ptr<Part> part = std::dynamic_pointer_cast<Part>(obj);
const rp::Transform& transform = part->rigidBody->getTransform(); const rp::Transform& transform = part->rigidBody->getTransform();

View file

@ -125,7 +125,7 @@ void renderParts() {
// Sort by nearest // Sort by nearest
std::map<float, std::shared_ptr<Part>> sorted; std::map<float, std::shared_ptr<Part>> sorted;
for (InstanceRef inst : workspace()->GetChildren()) { for (InstanceRef inst : gWorkspace()->GetChildren()) {
if (inst->GetClass()->className != "Part") continue; if (inst->GetClass()->className != "Part") continue;
std::shared_ptr<Part> part = std::dynamic_pointer_cast<Part>(inst); std::shared_ptr<Part> part = std::dynamic_pointer_cast<Part>(inst);
if (part->transparency > 0.00001) { if (part->transparency > 0.00001) {
@ -288,7 +288,7 @@ void renderAABB() {
ghostShader->set("color", glm::vec3(1.f, 0.f, 0.f)); ghostShader->set("color", glm::vec3(1.f, 0.f, 0.f));
// Sort by nearest // Sort by nearest
for (InstanceRef inst : workspace()->GetChildren()) { for (InstanceRef inst : gWorkspace()->GetChildren()) {
if (inst->GetClass()->className != "Part") continue; if (inst->GetClass()->className != "Part") continue;
std::shared_ptr<Part> part = std::dynamic_pointer_cast<Part>(inst); std::shared_ptr<Part> part = std::dynamic_pointer_cast<Part>(inst);
glm::mat4 model = Data::CFrame::IDENTITY + part->cframe.Position(); glm::mat4 model = Data::CFrame::IDENTITY + part->cframe.Position();

View file

@ -400,7 +400,7 @@ void MainGLWidget::keyPressEvent(QKeyEvent* evt) {
else if (evt->key() == Qt::Key_D) moveX = -1; else if (evt->key() == Qt::Key_D) moveX = -1;
if (evt->key() == Qt::Key_F) { if (evt->key() == Qt::Key_F) {
workspace()->AddChild(lastPart = Part::New({ gWorkspace()->AddChild(lastPart = Part::New({
.position = camera.cameraPos + camera.cameraFront * glm::vec3(3), .position = camera.cameraPos + camera.cameraFront * glm::vec3(3),
.rotation = glm::vec3(0), .rotation = glm::vec3(0),
.size = glm::vec3(1, 1, 1), .size = glm::vec3(1, 1, 1),

View file

@ -49,7 +49,7 @@ MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent) : QMainWindow(parent)
, ui(new Ui::MainWindow) , ui(new Ui::MainWindow)
{ {
dataModel->Init(); gDataModel->Init();
ui->setupUi(this); ui->setupUi(this);
timer.start(33, this); timer.start(33, this);
@ -136,11 +136,11 @@ MainWindow::MainWindow(QWidget *parent)
if (result == QMessageBox::Cancel) return; if (result == QMessageBox::Cancel) return;
if (result == QMessageBox::Save) { if (result == QMessageBox::Save) {
std::optional<std::string> path; std::optional<std::string> path;
if (!dataModel->HasFile()) if (!gDataModel->HasFile())
path = openFileDialog("Openblocks Level (*.obl)", ".obl", QFileDialog::AcceptSave, QString::fromStdString("Save " + dataModel->name)); path = openFileDialog("Openblocks Level (*.obl)", ".obl", QFileDialog::AcceptSave, QString::fromStdString("Save " + gDataModel->name));
if (!path || path == "") return; if (!path || path == "") return;
dataModel->SaveToFile(path); gDataModel->SaveToFile(path);
} }
#endif #endif
@ -150,15 +150,15 @@ MainWindow::MainWindow(QWidget *parent)
// TL;DR: This stinks and I need to fix it.) // TL;DR: This stinks and I need to fix it.)
ui->mainWidget->lastPart = Part::New(); ui->mainWidget->lastPart = Part::New();
dataModel = DataModel::New(); gDataModel = DataModel::New();
dataModel->Init(); gDataModel->Init();
ui->explorerView->updateRoot(dataModel); ui->explorerView->updateRoot(gDataModel);
// TODO: Remove this and use a proper fix. This *WILL* cause a leak and memory issues in the future // TODO: Remove this and use a proper fix. This *WILL* cause a leak and memory issues in the future
simulationInit(); simulationInit();
// Baseplate // Baseplate
workspace()->AddChild(ui->mainWidget->lastPart = Part::New({ gWorkspace()->AddChild(ui->mainWidget->lastPart = Part::New({
.position = glm::vec3(0, -5, 0), .position = glm::vec3(0, -5, 0),
.rotation = glm::vec3(0), .rotation = glm::vec3(0),
.size = glm::vec3(512, 1.2, 512), .size = glm::vec3(512, 1.2, 512),
@ -171,25 +171,30 @@ MainWindow::MainWindow(QWidget *parent)
connect(ui->actionSave, &QAction::triggered, this, [&]() { connect(ui->actionSave, &QAction::triggered, this, [&]() {
std::optional<std::string> path; std::optional<std::string> path;
if (!dataModel->HasFile()) if (!gDataModel->HasFile())
path = openFileDialog("Openblocks Level (*.obl)", ".obl", QFileDialog::AcceptSave, QString::fromStdString("Save " + dataModel->name)); path = openFileDialog("Openblocks Level (*.obl)", ".obl", QFileDialog::AcceptSave, QString::fromStdString("Save " + gDataModel->name));
if (!path || path == "") return; if (!path || path == "") return;
dataModel->SaveToFile(path); gDataModel->SaveToFile(path);
}); });
connect(ui->actionSaveAs, &QAction::triggered, this, [&]() { connect(ui->actionSaveAs, &QAction::triggered, this, [&]() {
std::optional<std::string> path = openFileDialog("Openblocks Level (*.obl)", ".obl", QFileDialog::AcceptSave, QString::fromStdString("Save as " + dataModel->name)); std::optional<std::string> path = openFileDialog("Openblocks Level (*.obl)", ".obl", QFileDialog::AcceptSave, QString::fromStdString("Save as " + gDataModel->name));
if (!path || path == "") return; if (!path || path == "") return;
dataModel->SaveToFile(path); gDataModel->SaveToFile(path);
}); });
connect(ui->actionOpen, &QAction::triggered, this, [&]() { connect(ui->actionOpen, &QAction::triggered, this, [&]() {
std::optional<std::string> path = openFileDialog("Openblocks Level (*.obl)", ".obl", QFileDialog::AcceptOpen); std::optional<std::string> path = openFileDialog("Openblocks Level (*.obl)", ".obl", QFileDialog::AcceptOpen);
if (!path || path == "") return; if (!path || path == "") return;
// // See TODO: Also remove this (the reaso
// ui->mainWidget->lastPart = Part::New();
// simulationInit();
std::shared_ptr<DataModel> newModel = DataModel::LoadFromFile(path.value()); std::shared_ptr<DataModel> newModel = DataModel::LoadFromFile(path.value());
dataModel = newModel; gDataModel = newModel;
ui->explorerView->updateRoot(newModel); ui->explorerView->updateRoot(newModel);
}); });
@ -242,7 +247,7 @@ MainWindow::MainWindow(QWidget *parent)
for (pugi::xml_node instNode : rootDoc.children()) { for (pugi::xml_node instNode : rootDoc.children()) {
InstanceRef inst = Instance::Deserialize(&instNode); InstanceRef inst = Instance::Deserialize(&instNode);
workspace()->AddChild(inst); gWorkspace()->AddChild(inst);
} }
}); });
@ -322,7 +327,7 @@ MainWindow::MainWindow(QWidget *parent)
simulationInit(); simulationInit();
// Baseplate // Baseplate
workspace()->AddChild(ui->mainWidget->lastPart = Part::New({ gWorkspace()->AddChild(ui->mainWidget->lastPart = Part::New({
.position = glm::vec3(0, -5, 0), .position = glm::vec3(0, -5, 0),
.rotation = glm::vec3(0), .rotation = glm::vec3(0),
.size = glm::vec3(512, 1.2, 512), .size = glm::vec3(512, 1.2, 512),
@ -332,7 +337,7 @@ MainWindow::MainWindow(QWidget *parent)
ui->mainWidget->lastPart->name = "Baseplate"; ui->mainWidget->lastPart->name = "Baseplate";
syncPartPhysics(ui->mainWidget->lastPart); syncPartPhysics(ui->mainWidget->lastPart);
workspace()->AddChild(ui->mainWidget->lastPart = Part::New({ gWorkspace()->AddChild(ui->mainWidget->lastPart = Part::New({
.position = glm::vec3(0), .position = glm::vec3(0),
.rotation = glm::vec3(0.5, 2, 1), .rotation = glm::vec3(0.5, 2, 1),
.size = glm::vec3(4, 1.2, 2), .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::Cancel) return evt->ignore();
if (result == QMessageBox::Save) { if (result == QMessageBox::Save) {
std::optional<std::string> path; std::optional<std::string> path;
if (!dataModel->HasFile()) if (!gDataModel->HasFile())
path = openFileDialog("Openblocks Level (*.obl)", ".obl", QFileDialog::AcceptSave, QString::fromStdString("Save " + dataModel->name)); path = openFileDialog("Openblocks Level (*.obl)", ".obl", QFileDialog::AcceptSave, QString::fromStdString("Save " + gDataModel->name));
if (!path || path == "") return evt->ignore(); if (!path || path == "") return evt->ignore();
dataModel->SaveToFile(path); gDataModel->SaveToFile(path);
} }
#endif #endif
} }

View file

@ -13,7 +13,7 @@
ExplorerView::ExplorerView(QWidget* parent): ExplorerView::ExplorerView(QWidget* parent):
QTreeView(parent), QTreeView(parent),
model(ExplorerModel(std::dynamic_pointer_cast<Instance>(dataModel))) { model(ExplorerModel(std::dynamic_pointer_cast<Instance>(gDataModel))) {
this->setModel(&model); this->setModel(&model);
// Disabling the root decoration will cause the expand/collapse chevrons to be hidden too, we don't want that // 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); this->setContextMenuPolicy(Qt::CustomContextMenu);
// Expand workspace // Expand workspace
this->expand(model.ObjectToIndex(workspace())); this->expand(model.ObjectToIndex(gWorkspace()));
connect(this, &QTreeView::customContextMenuRequested, this, [&](const QPoint& point) { connect(this, &QTreeView::customContextMenuRequested, this, [&](const QPoint& point) {
QModelIndex index = this->indexAt(point); QModelIndex index = this->indexAt(point);