feat: deserialization... among other things
This commit is contained in:
parent
37aafe48f4
commit
a3d2026a35
|
@ -18,6 +18,7 @@
|
||||||
#include "objects/datamodel.h"
|
#include "objects/datamodel.h"
|
||||||
#include "physics/simulation.h"
|
#include "physics/simulation.h"
|
||||||
#include "objects/part.h"
|
#include "objects/part.h"
|
||||||
|
#include "qfiledialog.h"
|
||||||
#include "qitemselectionmodel.h"
|
#include "qitemselectionmodel.h"
|
||||||
#include "qobject.h"
|
#include "qobject.h"
|
||||||
#include "qsysinfo.h"
|
#include "qsysinfo.h"
|
||||||
|
@ -36,14 +37,7 @@ MainWindow::MainWindow(QWidget *parent)
|
||||||
timer.start(33, this);
|
timer.start(33, this);
|
||||||
setMouseTracking(true);
|
setMouseTracking(true);
|
||||||
|
|
||||||
connect(ui->explorerView->selectionModel(), &QItemSelectionModel::selectionChanged, this, [&](const QItemSelection &selected, const QItemSelection &deselected) {
|
ConnectSelectionChangeHandler();
|
||||||
if (selected.count() == 0) return;
|
|
||||||
|
|
||||||
std::optional<InstanceRef> inst = selected.count() == 0 ? std::nullopt
|
|
||||||
: std::make_optional(((Instance*)selected.indexes()[0].internalPointer())->shared_from_this());
|
|
||||||
|
|
||||||
ui->propertiesView->setSelected(inst);
|
|
||||||
});
|
|
||||||
|
|
||||||
connect(ui->actionToolSelect, &QAction::triggered, this, [&]() { selectedTool = SelectedTool::SELECT; updateSelectedTool(); });
|
connect(ui->actionToolSelect, &QAction::triggered, this, [&]() { selectedTool = SelectedTool::SELECT; updateSelectedTool(); });
|
||||||
connect(ui->actionToolMove, &QAction::triggered, this, [&](bool state) { selectedTool = state ? SelectedTool::MOVE : SelectedTool::SELECT; updateSelectedTool(); });
|
connect(ui->actionToolMove, &QAction::triggered, this, [&](bool state) { selectedTool = state ? SelectedTool::MOVE : SelectedTool::SELECT; updateSelectedTool(); });
|
||||||
|
@ -65,11 +59,23 @@ MainWindow::MainWindow(QWidget *parent)
|
||||||
});
|
});
|
||||||
|
|
||||||
connect(ui->actionSave, &QAction::triggered, this, [&]() {
|
connect(ui->actionSave, &QAction::triggered, this, [&]() {
|
||||||
pugi::xml_document doc;
|
std::optional<std::string> path;
|
||||||
pugi::xml_node node = doc.append_child("openblocks");
|
if (!dataModel->HasFile())
|
||||||
workspace()->Serialize(&node);
|
path = QFileDialog::getSaveFileName(this, QString::fromStdString("Save " + dataModel->name), "", "*.obl").toStdString();
|
||||||
|
if (path == "") return;
|
||||||
|
|
||||||
doc.print(std::cout);
|
dataModel->SaveToFile(path);
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(ui->actionOpen, &QAction::triggered, this, [&]() {
|
||||||
|
std::string path = QFileDialog::getOpenFileName(this, "Load file", "", "*.obl").toStdString();
|
||||||
|
if (path == "") return;
|
||||||
|
std::shared_ptr<DataModel> newModel = DataModel::LoadFromFile(path);
|
||||||
|
dataModel = newModel;
|
||||||
|
delete ui->explorerView->selectionModel();
|
||||||
|
ui->explorerView->reset();
|
||||||
|
ui->explorerView->setModel(new ExplorerModel(dataModel));
|
||||||
|
ConnectSelectionChangeHandler();
|
||||||
});
|
});
|
||||||
|
|
||||||
// ui->explorerView->Init(ui);
|
// ui->explorerView->Init(ui);
|
||||||
|
@ -104,6 +110,17 @@ MainWindow::MainWindow(QWidget *parent)
|
||||||
syncPartPhysics(ui->mainWidget->lastPart);
|
syncPartPhysics(ui->mainWidget->lastPart);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MainWindow::ConnectSelectionChangeHandler() {
|
||||||
|
connect(ui->explorerView->selectionModel(), &QItemSelectionModel::selectionChanged, this, [&](const QItemSelection &selected, const QItemSelection &deselected) {
|
||||||
|
if (selected.count() == 0) return;
|
||||||
|
|
||||||
|
std::optional<InstanceRef> inst = selected.count() == 0 ? std::nullopt
|
||||||
|
: std::make_optional(((Instance*)selected.indexes()[0].internalPointer())->shared_from_this());
|
||||||
|
|
||||||
|
ui->propertiesView->setSelected(inst);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
static std::chrono::time_point lastTime = std::chrono::steady_clock::now();
|
static std::chrono::time_point lastTime = std::chrono::steady_clock::now();
|
||||||
void MainWindow::timerEvent(QTimerEvent* evt) {
|
void MainWindow::timerEvent(QTimerEvent* evt) {
|
||||||
if (evt->timerId() != timer.timerId()) {
|
if (evt->timerId() != timer.timerId()) {
|
||||||
|
|
|
@ -28,5 +28,6 @@ private:
|
||||||
|
|
||||||
void updateSelectedTool();
|
void updateSelectedTool();
|
||||||
void timerEvent(QTimerEvent*) override;
|
void timerEvent(QTimerEvent*) override;
|
||||||
|
void ConnectSelectionChangeHandler();
|
||||||
};
|
};
|
||||||
#endif // MAINWINDOW_H
|
#endif // MAINWINDOW_H
|
||||||
|
|
|
@ -15,7 +15,7 @@ typedef std::function<void(std::vector<InstanceRefWeak> oldSelection, std::vecto
|
||||||
|
|
||||||
extern Camera camera;
|
extern Camera camera;
|
||||||
extern std::shared_ptr<DataModel> dataModel;
|
extern std::shared_ptr<DataModel> dataModel;
|
||||||
inline std::shared_ptr<Workspace> workspace() { return dataModel->workspace; }
|
inline std::shared_ptr<Workspace> workspace() { return std::dynamic_pointer_cast<Workspace>(dataModel->services["Workspace"]); }
|
||||||
extern std::optional<HierarchyPreUpdateHandler> hierarchyPreUpdateHandler;
|
extern std::optional<HierarchyPreUpdateHandler> hierarchyPreUpdateHandler;
|
||||||
extern std::optional<HierarchyPostUpdateHandler> hierarchyPostUpdateHandler;
|
extern std::optional<HierarchyPostUpdateHandler> hierarchyPostUpdateHandler;
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
#include "base.h"
|
#include "base.h"
|
||||||
|
#include "meta.h"
|
||||||
|
|
||||||
#define IMPL_WRAPPER_CLASS(CLASS_NAME, WRAPPED_TYPE, TYPE_NAME) Data::CLASS_NAME::CLASS_NAME(WRAPPED_TYPE in) : value(in) {} \
|
#define IMPL_WRAPPER_CLASS(CLASS_NAME, WRAPPED_TYPE, TYPE_NAME) Data::CLASS_NAME::CLASS_NAME(WRAPPED_TYPE in) : value(in) {} \
|
||||||
Data::CLASS_NAME::~CLASS_NAME() = default; \
|
Data::CLASS_NAME::~CLASS_NAME() = default; \
|
||||||
Data::CLASS_NAME::operator const WRAPPED_TYPE() const { return value; } \
|
Data::CLASS_NAME::operator const WRAPPED_TYPE() const { return value; } \
|
||||||
const Data::TypeInfo Data::CLASS_NAME::TYPE = { .name = TYPE_NAME, }; \
|
const Data::TypeInfo Data::CLASS_NAME::TYPE = { .name = TYPE_NAME, .deserializer = &Data::CLASS_NAME::Deserialize }; \
|
||||||
const Data::TypeInfo& Data::CLASS_NAME::GetType() const { return Data::CLASS_NAME::TYPE; }; \
|
const Data::TypeInfo& Data::CLASS_NAME::GetType() const { return Data::CLASS_NAME::TYPE; }; \
|
||||||
void Data::CLASS_NAME::Serialize(pugi::xml_node* node) const { node->text().set(std::string(this->ToString())); }
|
void Data::CLASS_NAME::Serialize(pugi::xml_node* node) const { node->text().set(std::string(this->ToString())); }
|
||||||
|
|
||||||
|
@ -13,14 +14,10 @@ Data::Null::Null() {};
|
||||||
Data::Null::~Null() = default;
|
Data::Null::~Null() = default;
|
||||||
const Data::TypeInfo Data::Null::TYPE = {
|
const Data::TypeInfo Data::Null::TYPE = {
|
||||||
.name = "null",
|
.name = "null",
|
||||||
|
.deserializer = &Data::Null::Deserialize,
|
||||||
};
|
};
|
||||||
const Data::TypeInfo& Data::Null::GetType() const { return Data::Null::TYPE; };
|
const Data::TypeInfo& Data::Null::GetType() const { return Data::Null::TYPE; };
|
||||||
|
|
||||||
IMPL_WRAPPER_CLASS(Bool, bool, "bool")
|
|
||||||
IMPL_WRAPPER_CLASS(Int, int, "int")
|
|
||||||
IMPL_WRAPPER_CLASS(Float, float, "float")
|
|
||||||
IMPL_WRAPPER_CLASS(String, std::string, "string")
|
|
||||||
|
|
||||||
const Data::String Data::Null::ToString() const {
|
const Data::String Data::Null::ToString() const {
|
||||||
return Data::String("null");
|
return Data::String("null");
|
||||||
}
|
}
|
||||||
|
@ -29,18 +26,45 @@ void Data::Null::Serialize(pugi::xml_node* node) const {
|
||||||
node->text().set("null");
|
node->text().set("null");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Data::Variant Data::Null::Deserialize(pugi::xml_node* node) {
|
||||||
|
return Data::Null();
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
|
||||||
|
IMPL_WRAPPER_CLASS(Bool, bool, "bool")
|
||||||
|
IMPL_WRAPPER_CLASS(Int, int, "int")
|
||||||
|
IMPL_WRAPPER_CLASS(Float, float, "float")
|
||||||
|
IMPL_WRAPPER_CLASS(String, std::string, "string")
|
||||||
|
|
||||||
const Data::String Data::Bool::ToString() const {
|
const Data::String Data::Bool::ToString() const {
|
||||||
return Data::String(value ? "true" : "false");
|
return Data::String(value ? "true" : "false");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Data::Variant Data::Bool::Deserialize(pugi::xml_node* node) {
|
||||||
|
return Data::Bool(node->text().as_bool());
|
||||||
|
}
|
||||||
|
|
||||||
const Data::String Data::Int::ToString() const {
|
const Data::String Data::Int::ToString() const {
|
||||||
return Data::String(std::to_string(value));
|
return Data::String(std::to_string(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Data::Variant Data::Int::Deserialize(pugi::xml_node* node) {
|
||||||
|
return Data::Int(node->text().as_int());
|
||||||
|
}
|
||||||
|
|
||||||
const Data::String Data::Float::ToString() const {
|
const Data::String Data::Float::ToString() const {
|
||||||
return Data::String(std::to_string(value));
|
return Data::String(std::to_string(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Data::Variant Data::Float::Deserialize(pugi::xml_node* node) {
|
||||||
|
return Data::Float(node->text().as_float());
|
||||||
|
}
|
||||||
|
|
||||||
const Data::String Data::String::ToString() const {
|
const Data::String Data::String::ToString() const {
|
||||||
return *this;
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Data::Variant Data::String::Deserialize(pugi::xml_node* node) {
|
||||||
|
return Data::String(node->text().as_string());
|
||||||
}
|
}
|
|
@ -1,8 +1,8 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <pugixml.hpp>
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <functional>
|
||||||
|
#include <pugixml.hpp>
|
||||||
|
|
||||||
#define DEF_WRAPPER_CLASS(CLASS_NAME, WRAPPED_TYPE) class CLASS_NAME : public Data::Base { \
|
#define DEF_WRAPPER_CLASS(CLASS_NAME, WRAPPED_TYPE) class CLASS_NAME : public Data::Base { \
|
||||||
const WRAPPED_TYPE value; \
|
const WRAPPED_TYPE value; \
|
||||||
|
@ -15,11 +15,16 @@ public: \
|
||||||
\
|
\
|
||||||
virtual const Data::String ToString() const override; \
|
virtual const Data::String ToString() const override; \
|
||||||
virtual void Serialize(pugi::xml_node* node) const override; \
|
virtual void Serialize(pugi::xml_node* node) const override; \
|
||||||
|
static Data::Variant Deserialize(pugi::xml_node* node); \
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace Data {
|
namespace Data {
|
||||||
|
class Variant;
|
||||||
|
typedef std::function<Data::Variant(pugi::xml_node*)> Deserializer;
|
||||||
|
|
||||||
struct TypeInfo {
|
struct TypeInfo {
|
||||||
std::string name;
|
std::string name;
|
||||||
|
Deserializer deserializer;
|
||||||
TypeInfo(const TypeInfo&) = delete;
|
TypeInfo(const TypeInfo&) = delete;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -41,6 +46,7 @@ namespace Data {
|
||||||
|
|
||||||
virtual const Data::String ToString() const override;
|
virtual const Data::String ToString() const override;
|
||||||
virtual void Serialize(pugi::xml_node* node) const override;
|
virtual void Serialize(pugi::xml_node* node) const override;
|
||||||
|
static Data::Variant Deserialize(pugi::xml_node* node);
|
||||||
};
|
};
|
||||||
|
|
||||||
DEF_WRAPPER_CLASS(Bool, bool)
|
DEF_WRAPPER_CLASS(Bool, bool)
|
||||||
|
|
|
@ -5,6 +5,19 @@
|
||||||
#include <reactphysics3d/mathematics/Transform.h>
|
#include <reactphysics3d/mathematics/Transform.h>
|
||||||
#define GLM_ENABLE_EXPERIMENTAL
|
#define GLM_ENABLE_EXPERIMENTAL
|
||||||
#include <glm/gtx/euler_angles.hpp>
|
#include <glm/gtx/euler_angles.hpp>
|
||||||
|
// #include "meta.h" // IWYU pragma: keep
|
||||||
|
|
||||||
|
Data::CFrame::CFrame(float x, float y, float z, float R00, float R01, float R02, float R10, float R11, float R12, float R20, float R21, float R22)
|
||||||
|
: translation(x, y, z)
|
||||||
|
, rotation({
|
||||||
|
// { R00, R01, R02 },
|
||||||
|
// { R10, R11, R12 },
|
||||||
|
// { R20, R21, R22 },
|
||||||
|
{ R00, R10, R20 },
|
||||||
|
{ R01, R11, R21 },
|
||||||
|
{ R02, R12, R22 },
|
||||||
|
}) {
|
||||||
|
}
|
||||||
|
|
||||||
Data::CFrame::CFrame(glm::vec3 translation, glm::mat3 rotation)
|
Data::CFrame::CFrame(glm::vec3 translation, glm::mat3 rotation)
|
||||||
: translation(translation)
|
: translation(translation)
|
||||||
|
@ -41,6 +54,7 @@ Data::CFrame::CFrame(Data::Vector3 position, Data::Vector3 lookAt, Data::Vector3
|
||||||
Data::CFrame::~CFrame() = default;
|
Data::CFrame::~CFrame() = default;
|
||||||
const Data::TypeInfo Data::CFrame::TYPE = {
|
const Data::TypeInfo Data::CFrame::TYPE = {
|
||||||
.name = "CoordinateFrame",
|
.name = "CoordinateFrame",
|
||||||
|
.deserializer = &Data::CFrame::Deserialize,
|
||||||
};
|
};
|
||||||
|
|
||||||
const Data::TypeInfo& Data::CFrame::GetType() const { return Data::Vector3::TYPE; };
|
const Data::TypeInfo& Data::CFrame::GetType() const { return Data::Vector3::TYPE; };
|
||||||
|
@ -97,3 +111,21 @@ void Data::CFrame::Serialize(pugi::xml_node* node) const {
|
||||||
node->append_child("R21").text().set(std::to_string(this->rotation[1][2]));
|
node->append_child("R21").text().set(std::to_string(this->rotation[1][2]));
|
||||||
node->append_child("R22").text().set(std::to_string(this->rotation[2][2]));
|
node->append_child("R22").text().set(std::to_string(this->rotation[2][2]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Data::Variant Data::CFrame::Deserialize(pugi::xml_node* node) {
|
||||||
|
return Data::CFrame(
|
||||||
|
node->child("X").text().as_float(),
|
||||||
|
node->child("Y").text().as_float(),
|
||||||
|
node->child("Z").text().as_float(),
|
||||||
|
node->child("R00").text().as_float(),
|
||||||
|
node->child("R01").text().as_float(),
|
||||||
|
node->child("R02").text().as_float(),
|
||||||
|
node->child("R10").text().as_float(),
|
||||||
|
node->child("R11").text().as_float(),
|
||||||
|
node->child("R12").text().as_float(),
|
||||||
|
node->child("R20").text().as_float(),
|
||||||
|
node->child("R21").text().as_float(),
|
||||||
|
node->child("R22").text().as_float()
|
||||||
|
);
|
||||||
|
}
|
|
@ -22,6 +22,7 @@ namespace Data {
|
||||||
// CFrame(float x, float y, float z);
|
// CFrame(float x, float y, float z);
|
||||||
// CFrame(const glm::vec3&);
|
// CFrame(const glm::vec3&);
|
||||||
// CFrame(const rp::Vector3&);
|
// CFrame(const rp::Vector3&);
|
||||||
|
CFrame(float x, float y, float z, float R00, float R01, float R02, float R10, float R11, float R12, float R20, float R21, float R22);
|
||||||
CFrame(const rp::Transform&);
|
CFrame(const rp::Transform&);
|
||||||
CFrame(Data::Vector3 position, glm::quat quat);
|
CFrame(Data::Vector3 position, glm::quat quat);
|
||||||
CFrame(Data::Vector3 position, Data::Vector3 lookAt, Data::Vector3 up = Data::Vector3(0, 1, 0));
|
CFrame(Data::Vector3 position, Data::Vector3 lookAt, Data::Vector3 up = Data::Vector3(0, 1, 0));
|
||||||
|
@ -32,6 +33,7 @@ namespace Data {
|
||||||
|
|
||||||
virtual const Data::String ToString() const override;
|
virtual const Data::String ToString() const override;
|
||||||
virtual void Serialize(pugi::xml_node* parent) const override;
|
virtual void Serialize(pugi::xml_node* parent) const override;
|
||||||
|
static Data::Variant Deserialize(pugi::xml_node* node);
|
||||||
|
|
||||||
operator glm::mat4() const;
|
operator glm::mat4() const;
|
||||||
operator rp::Transform() const;
|
operator rp::Transform() const;
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
#include "meta.h"
|
#include "meta.h"
|
||||||
|
#include "datatypes/base.h"
|
||||||
|
#include "datatypes/cframe.h"
|
||||||
#include <variant>
|
#include <variant>
|
||||||
|
|
||||||
Data::String Data::Variant::ToString() const {
|
Data::String Data::Variant::ToString() const {
|
||||||
|
@ -11,4 +13,24 @@ void Data::Variant::Serialize(pugi::xml_node* node) const {
|
||||||
std::visit([&](auto&& it) {
|
std::visit([&](auto&& it) {
|
||||||
it.Serialize(node);
|
it.Serialize(node);
|
||||||
}, this->wrapped);
|
}, this->wrapped);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Data::Variant Data::Variant::Deserialize(pugi::xml_node* node) {
|
||||||
|
if (Data::TYPE_MAP.count(node->name()) == 0) {
|
||||||
|
fprintf(stderr, "Unknown type for instance: '%s'\n", node->name());
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
const Data::TypeInfo* type = Data::TYPE_MAP[node->name()];
|
||||||
|
return type->deserializer(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::map<std::string, const Data::TypeInfo*> Data::TYPE_MAP = {
|
||||||
|
{ "null", &Data::Null::TYPE },
|
||||||
|
{ "bool", &Data::Bool::TYPE },
|
||||||
|
{ "int", &Data::Int::TYPE },
|
||||||
|
{ "float", &Data::Float::TYPE },
|
||||||
|
{ "string", &Data::String::TYPE },
|
||||||
|
{ "Vector3", &Data::Vector3::TYPE },
|
||||||
|
{ "CoordinateFrame", &Data::CFrame::TYPE },
|
||||||
|
};
|
|
@ -1,6 +1,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <variant>
|
#include <variant>
|
||||||
|
#include <map>
|
||||||
#include "base.h"
|
#include "base.h"
|
||||||
#include "vector.h"
|
#include "vector.h"
|
||||||
#include "cframe.h"
|
#include "cframe.h"
|
||||||
|
@ -30,6 +31,11 @@ namespace Data {
|
||||||
template <typename T> Variant(T obj) : wrapped(obj) {}
|
template <typename T> Variant(T obj) : wrapped(obj) {}
|
||||||
template <typename T> T get() { return std::get<T>(wrapped); }
|
template <typename T> T get() { return std::get<T>(wrapped); }
|
||||||
Data::String ToString() const;
|
Data::String ToString() const;
|
||||||
|
|
||||||
void Serialize(pugi::xml_node* node) const;
|
void Serialize(pugi::xml_node* node) const;
|
||||||
|
static Data::Variant Deserialize(pugi::xml_node* node);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Map of all data types to their type names
|
||||||
|
extern std::map<std::string, const TypeInfo*> TYPE_MAP;
|
||||||
}
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
#include "vector.h"
|
#include "vector.h"
|
||||||
#include <glm/ext/quaternion_geometric.hpp>
|
#include <glm/ext/quaternion_geometric.hpp>
|
||||||
|
#include "meta.h" // IWYU pragma: keep
|
||||||
|
|
||||||
Data::Vector3::Vector3(const glm::vec3& src) : vector(src) {};
|
Data::Vector3::Vector3(const glm::vec3& src) : vector(src) {};
|
||||||
Data::Vector3::Vector3(const rp::Vector3& src) : vector(glm::vec3(src.x, src.y, src.z)) {};
|
Data::Vector3::Vector3(const rp::Vector3& src) : vector(glm::vec3(src.x, src.y, src.z)) {};
|
||||||
|
@ -8,6 +9,7 @@ Data::Vector3::Vector3(float x, const float y, float z) : vector(glm::vec3(x, y,
|
||||||
Data::Vector3::~Vector3() = default;
|
Data::Vector3::~Vector3() = default;
|
||||||
const Data::TypeInfo Data::Vector3::TYPE = {
|
const Data::TypeInfo Data::Vector3::TYPE = {
|
||||||
.name = "Vector3",
|
.name = "Vector3",
|
||||||
|
.deserializer = &Data::Vector3::Deserialize,
|
||||||
};
|
};
|
||||||
|
|
||||||
const Data::TypeInfo& Data::Vector3::GetType() const { return Data::Vector3::TYPE; };
|
const Data::TypeInfo& Data::Vector3::GetType() const { return Data::Vector3::TYPE; };
|
||||||
|
@ -50,3 +52,7 @@ void Data::Vector3::Serialize(pugi::xml_node* node) const {
|
||||||
node->append_child("Y").text().set(std::to_string(this->Y()));
|
node->append_child("Y").text().set(std::to_string(this->Y()));
|
||||||
node->append_child("Z").text().set(std::to_string(this->Z()));
|
node->append_child("Z").text().set(std::to_string(this->Z()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Data::Variant Data::Vector3::Deserialize(pugi::xml_node* node) {
|
||||||
|
return Data::Vector3(node->child("X").text().as_float(), node->child("Y").text().as_float(), node->child("Z").text().as_float());
|
||||||
|
}
|
||||||
|
|
|
@ -25,6 +25,7 @@ namespace Data {
|
||||||
|
|
||||||
virtual const Data::String ToString() const override;
|
virtual const Data::String ToString() const override;
|
||||||
virtual void Serialize(pugi::xml_node* node) const override;
|
virtual void Serialize(pugi::xml_node* node) const override;
|
||||||
|
static Data::Variant Deserialize(pugi::xml_node* node);
|
||||||
|
|
||||||
operator glm::vec3() const;
|
operator glm::vec3() const;
|
||||||
operator rp::Vector3() const;
|
operator rp::Vector3() const;
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
#include "../../datatypes/meta.h"
|
#include "../../datatypes/meta.h"
|
||||||
#include "datatypes/base.h"
|
#include "datatypes/base.h"
|
||||||
#include "objects/base/member.h"
|
#include "objects/base/member.h"
|
||||||
|
#include "objects/meta.h"
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
|
@ -163,4 +164,39 @@ void Instance::Serialize(pugi::xml_node* parent) {
|
||||||
for (InstanceRef child : this->children) {
|
for (InstanceRef child : this->children) {
|
||||||
child->Serialize(&node);
|
child->Serialize(&node);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
InstanceRef Instance::Deserialize(pugi::xml_node* node) {
|
||||||
|
std::string className = node->attribute("class").value();
|
||||||
|
if (INSTANCE_MAP.count(className) == 0) {
|
||||||
|
fprintf(stderr, "Unknown type for instance: '%s'\n", className.c_str());
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
// This will error if an abstract instance is used in the file. Oh well, not my prob rn.
|
||||||
|
// printf("What are you? A %s sandwich\n", className.c_str());
|
||||||
|
InstanceRef object = INSTANCE_MAP[className]->constructor();
|
||||||
|
object->GetChildren();
|
||||||
|
|
||||||
|
// const InstanceType* type = INSTANCE_MAP.at(className);
|
||||||
|
|
||||||
|
// Read properties
|
||||||
|
pugi::xml_node propertiesNode = node->child("Properties");
|
||||||
|
for (pugi::xml_node propertyNode : propertiesNode) {
|
||||||
|
std::string propertyName = propertyNode.attribute("name").value();
|
||||||
|
auto meta_ = object->GetPropertyMeta(propertyName);
|
||||||
|
if (!meta_.has_value()) {
|
||||||
|
fprintf(stderr, "Attempt to set unknown property '%s' of %s\n", propertyName.c_str(), object->GetClass()->className.c_str());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Data::Variant value = Data::Variant::Deserialize(&propertyNode);
|
||||||
|
object->SetPropertyValue(propertyName, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read children
|
||||||
|
for (pugi::xml_node childNode : node->children("Item")) {
|
||||||
|
InstanceRef child = Instance::Deserialize(&childNode);
|
||||||
|
object->AddChild(child);
|
||||||
|
}
|
||||||
|
|
||||||
|
return object;
|
||||||
}
|
}
|
|
@ -72,6 +72,7 @@ public:
|
||||||
|
|
||||||
// Serialization
|
// Serialization
|
||||||
void Serialize(pugi::xml_node* parent);
|
void Serialize(pugi::xml_node* parent);
|
||||||
|
static std::shared_ptr<Instance> Deserialize(pugi::xml_node* node);
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef std::shared_ptr<Instance> InstanceRef;
|
typedef std::shared_ptr<Instance> InstanceRef;
|
||||||
|
|
|
@ -1,6 +1,18 @@
|
||||||
#include "datamodel.h"
|
#include "datamodel.h"
|
||||||
|
#include "base/service.h"
|
||||||
|
#include "objects/base/instance.h"
|
||||||
|
#include "objects/base/service.h"
|
||||||
#include "workspace.h"
|
#include "workspace.h"
|
||||||
|
#include <cstdio>
|
||||||
|
#include <fstream>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
// TODO: Move this to a different file
|
||||||
|
// ONLY add services here, all types are expected to inherit from Service, or errors WILL occur
|
||||||
|
std::map<std::string, std::function<std::shared_ptr<Service>(std::weak_ptr<DataModel>)>> SERVICE_CONSTRUCTORS = {
|
||||||
|
{ "Workspace", &Workspace::Create },
|
||||||
|
};
|
||||||
|
|
||||||
const InstanceType DataModel::TYPE = {
|
const InstanceType DataModel::TYPE = {
|
||||||
.super = &Instance::TYPE,
|
.super = &Instance::TYPE,
|
||||||
|
@ -14,9 +26,96 @@ const InstanceType* DataModel::GetClass() {
|
||||||
|
|
||||||
DataModel::DataModel()
|
DataModel::DataModel()
|
||||||
: Instance(&TYPE) {
|
: Instance(&TYPE) {
|
||||||
|
this->name = "Place";
|
||||||
}
|
}
|
||||||
|
|
||||||
void DataModel::Init() {
|
void DataModel::Init() {
|
||||||
this->workspace = std::make_shared<Workspace>(shared<DataModel>());
|
// Create the workspace if it doesn't exist
|
||||||
this->workspace->InitService();
|
if (this->services.count("Workspace") == 0)
|
||||||
|
this->services["Workspace"] = std::make_shared<Workspace>(shared<DataModel>());
|
||||||
|
|
||||||
|
// Init all services
|
||||||
|
for (auto [_, service] : this->services) {
|
||||||
|
service->InitService();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DataModel::SaveToFile(std::optional<std::string> path) {
|
||||||
|
if (!path.has_value() && !this->currentFile.has_value()) {
|
||||||
|
fprintf(stderr, "Cannot save DataModel because no path was provided.\n");
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string target = path.has_value() ? path.value() : this->currentFile.value();
|
||||||
|
|
||||||
|
std::ofstream outStream(target);
|
||||||
|
|
||||||
|
pugi::xml_document doc;
|
||||||
|
pugi::xml_node root = doc.append_child("openblocks");
|
||||||
|
|
||||||
|
for (InstanceRef child : this->GetChildren()) {
|
||||||
|
child->Serialize(&root);
|
||||||
|
}
|
||||||
|
|
||||||
|
doc.save(outStream);
|
||||||
|
currentFile = target;
|
||||||
|
name = target;
|
||||||
|
printf("Place saved succesfully\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void DataModel::DeserializeService(pugi::xml_node* node) {
|
||||||
|
std::string className = node->attribute("class").value();
|
||||||
|
if (SERVICE_CONSTRUCTORS.count(className) == 0) {
|
||||||
|
fprintf(stderr, "Unknown service: '%s'\n", className.c_str());
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("Adding a workspace. %d:%d:%d\n", services.count(className), services.size(), GetChildren().size());
|
||||||
|
if (services.count(className) != 0) {
|
||||||
|
fprintf(stderr, "Service %s defined multiple times in file\n", className.c_str());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This will error if an abstract instance is used in the file. Oh well, not my prob rn.
|
||||||
|
InstanceRef object = SERVICE_CONSTRUCTORS[className](shared<DataModel>());
|
||||||
|
|
||||||
|
// Read properties
|
||||||
|
pugi::xml_node propertiesNode = node->child("Properties");
|
||||||
|
for (pugi::xml_node propertyNode : propertiesNode) {
|
||||||
|
std::string propertyName = propertyNode.attribute("name").value();
|
||||||
|
auto meta_ = object->GetPropertyMeta(propertyName);
|
||||||
|
if (!meta_.has_value()) {
|
||||||
|
fprintf(stderr, "Attempt to set unknown property '%s' of %s\n", propertyName.c_str(), object->GetClass()->className.c_str());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Data::Variant value = Data::Variant::Deserialize(&propertyNode);
|
||||||
|
object->SetPropertyValue(propertyName, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add children
|
||||||
|
for (pugi::xml_node childNode : node->children("Item")) {
|
||||||
|
InstanceRef child = Instance::Deserialize(&childNode);
|
||||||
|
object->AddChild(child);
|
||||||
|
}
|
||||||
|
|
||||||
|
// We add the service to the list
|
||||||
|
// All services get init'd at once in InitServices
|
||||||
|
this->services[className] = std::dynamic_pointer_cast<Service>(object);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<DataModel> DataModel::LoadFromFile(std::string path) {
|
||||||
|
std::ifstream inStream(path);
|
||||||
|
pugi::xml_document doc;
|
||||||
|
doc.load(inStream);
|
||||||
|
|
||||||
|
pugi::xml_node rootNode = doc.child("openblocks");
|
||||||
|
std::shared_ptr<DataModel> newModel = std::make_shared<DataModel>();
|
||||||
|
|
||||||
|
for (pugi::xml_node childNode : rootNode.children("Item")) {
|
||||||
|
newModel->DeserializeService(&childNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
newModel->Init();
|
||||||
|
|
||||||
|
return newModel;
|
||||||
}
|
}
|
|
@ -5,17 +5,44 @@
|
||||||
|
|
||||||
class Workspace;
|
class Workspace;
|
||||||
|
|
||||||
|
class DataModel;
|
||||||
|
class Service;
|
||||||
|
extern std::map<std::string, std::function<std::shared_ptr<Service>(std::weak_ptr<DataModel>)>> SERVICE_CONSTRUCTORS;
|
||||||
|
|
||||||
// The root instance to all objects in the hierarchy
|
// The root instance to all objects in the hierarchy
|
||||||
class DataModel : public Instance {
|
class DataModel : public Instance {
|
||||||
//private:
|
private:
|
||||||
|
void DeserializeService(pugi::xml_node* node);
|
||||||
public:
|
public:
|
||||||
const static InstanceType TYPE;
|
const static InstanceType TYPE;
|
||||||
|
|
||||||
std::shared_ptr<Workspace> workspace;
|
std::map<std::string, std::shared_ptr<Service>> services;
|
||||||
|
|
||||||
|
std::optional<std::string> currentFile;
|
||||||
|
|
||||||
DataModel();
|
DataModel();
|
||||||
void Init();
|
void Init();
|
||||||
|
|
||||||
static inline std::shared_ptr<DataModel> New() { return std::make_shared<DataModel>(); };
|
static inline std::shared_ptr<DataModel> New() { return std::make_shared<DataModel>(); };
|
||||||
virtual const InstanceType* GetClass() override;
|
virtual const InstanceType* GetClass() override;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
std::shared_ptr<T> GetService(std::string name) {
|
||||||
|
if (services.count(name) != 0)
|
||||||
|
return services[name];
|
||||||
|
// TODO: Validate name
|
||||||
|
services[name] = SERVICE_CONSTRUCTORS[name](shared<DataModel>());
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
std::optional<std::shared_ptr<T>> FindService() {
|
||||||
|
if (services.count(name) != 0)
|
||||||
|
return services[name];
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Saving/loading
|
||||||
|
inline bool HasFile() { return this->currentFile.has_value(); }
|
||||||
|
void SaveToFile(std::optional<std::string> path = std::nullopt);
|
||||||
|
static std::shared_ptr<DataModel> LoadFromFile(std::string path);
|
||||||
};
|
};
|
10
src/objects/meta.cpp
Normal file
10
src/objects/meta.cpp
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
#include "meta.h"
|
||||||
|
#include "objects/part.h"
|
||||||
|
#include "objects/workspace.h"
|
||||||
|
|
||||||
|
std::map<std::string, const InstanceType*> INSTANCE_MAP = {
|
||||||
|
{ "Instance", &Instance::TYPE },
|
||||||
|
{ "Part", &Part::TYPE },
|
||||||
|
{ "Workspace", &Workspace::TYPE },
|
||||||
|
{ "DataModel", &DataModel::TYPE },
|
||||||
|
};
|
8
src/objects/meta.h
Normal file
8
src/objects/meta.h
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <map>
|
||||||
|
#include "objects/base/instance.h"
|
||||||
|
|
||||||
|
// Map of all instance types to their class names
|
||||||
|
extern std::map<std::string, const InstanceType*> INSTANCE_MAP;
|
|
@ -85,6 +85,11 @@ Part::Part(PartConstructParams params): Instance(&TYPE), cframe(Data::CFrame(par
|
||||||
.type = &Data::CFrame::TYPE,
|
.type = &Data::CFrame::TYPE,
|
||||||
.codec = fieldCodecOf<Data::CFrame>(),
|
.codec = fieldCodecOf<Data::CFrame>(),
|
||||||
.updateCallback = memberFunctionOf(&Part::onUpdated, this),
|
.updateCallback = memberFunctionOf(&Part::onUpdated, this),
|
||||||
|
} }, { "scale", {
|
||||||
|
.backingField = &scale,
|
||||||
|
.type = &Data::Vector3::TYPE,
|
||||||
|
.codec = fieldCodecOf<Data::Vector3, glm::vec3>(),
|
||||||
|
.updateCallback = memberFunctionOf(&Part::onUpdated, this)
|
||||||
} }
|
} }
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -31,7 +31,11 @@ public:
|
||||||
// TODO: Switch these over to our dedicated datatypes
|
// TODO: Switch these over to our dedicated datatypes
|
||||||
Data::CFrame cframe;
|
Data::CFrame cframe;
|
||||||
glm::vec3 scale;
|
glm::vec3 scale;
|
||||||
Material material;
|
Material material {
|
||||||
|
.diffuse = glm::vec3(0.639216f, 0.635294f, 0.647059f),
|
||||||
|
.specular = glm::vec3(0.5f, 0.5f, 0.5f),
|
||||||
|
.shininess = 32.0f,
|
||||||
|
};
|
||||||
bool selected = false;
|
bool selected = false;
|
||||||
|
|
||||||
bool anchored = false;
|
bool anchored = false;
|
||||||
|
|
|
@ -12,6 +12,6 @@ public:
|
||||||
Workspace(std::weak_ptr<DataModel> dataModel);
|
Workspace(std::weak_ptr<DataModel> dataModel);
|
||||||
|
|
||||||
// static inline std::shared_ptr<Workspace> New() { return std::make_shared<Workspace>(); };
|
// static inline std::shared_ptr<Workspace> New() { return std::make_shared<Workspace>(); };
|
||||||
// static inline InstanceRef Create() { return std::make_shared<Workspace>(); };
|
static inline std::shared_ptr<Service> Create(std::weak_ptr<DataModel> parent) { return std::make_shared<Workspace>(parent); };
|
||||||
virtual const InstanceType* GetClass() override;
|
virtual const InstanceType* GetClass() override;
|
||||||
};
|
};
|
|
@ -110,6 +110,21 @@ void renderParts() {
|
||||||
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 (inst->name == "Target") printf("(%f,%f,%f):(%f,%f,%f;%f,%f,%f;%f,%f,%f)\n",
|
||||||
|
// part->cframe.X(),
|
||||||
|
// part->cframe.Y(),
|
||||||
|
// part->cframe.Z(),
|
||||||
|
// part->cframe.RightVector().X(),
|
||||||
|
// part->cframe.UpVector().X(),
|
||||||
|
// part->cframe.LookVector().X(),
|
||||||
|
// part->cframe.RightVector().Y(),
|
||||||
|
// part->cframe.UpVector().Y(),
|
||||||
|
// part->cframe.LookVector().Y(),
|
||||||
|
// part->cframe.RightVector().Z(),
|
||||||
|
// part->cframe.UpVector().Z(),
|
||||||
|
// part->cframe.LookVector().Z()
|
||||||
|
// );
|
||||||
|
|
||||||
glm::mat4 model = part->cframe;
|
glm::mat4 model = part->cframe;
|
||||||
model = glm::scale(model, part->scale);
|
model = glm::scale(model, part->scale);
|
||||||
shader->set("model", model);
|
shader->set("model", model);
|
||||||
|
|
Loading…
Reference in a new issue