feat: instance and property serialization
This commit is contained in:
parent
3c2eae2028
commit
37aafe48f4
19 changed files with 161 additions and 31 deletions
|
@ -23,14 +23,15 @@ find_package(GLUT REQUIRED)
|
|||
include_directories(${GLUT_INCLUDE_DIRS})
|
||||
|
||||
find_package(glfw3 REQUIRED)
|
||||
|
||||
find_package(OpenGL)
|
||||
|
||||
find_package(glm CONFIG REQUIRED)
|
||||
|
||||
find_package(assimp REQUIRED)
|
||||
|
||||
find_package(ReactPhysics3D REQUIRED)
|
||||
find_package(pugixml REQUIRED)
|
||||
|
||||
# PkgConfig packages
|
||||
# find_package(PkgConfig REQUIRED)
|
||||
# pkg_check_modules(PUGIXML REQUIRED pugixml)
|
||||
|
||||
file(MAKE_DIRECTORY bin)
|
||||
|
||||
|
@ -39,7 +40,7 @@ include_directories("include")
|
|||
file(GLOB_RECURSE SOURCES "src/*.cpp" "src/*.h")
|
||||
add_library(openblocks ${SOURCES})
|
||||
set_target_properties(openblocks PROPERTIES OUTPUT_NAME "openblocks")
|
||||
target_link_libraries(openblocks ${SDL2_LIBRARIES} ${GLEW_LIBRARIES} ${GLUT_LIBRARIES} OpenGL::GL OpenGL::GLU glfw glm::glm assimp ReactPhysics3D::ReactPhysics3D)
|
||||
target_link_libraries(openblocks ${SDL2_LIBRARIES} ${GLEW_LIBRARIES} ${GLUT_LIBRARIES} ${PUGIXML_LIBRARIES} OpenGL::GL OpenGL::GLU glfw glm::glm assimp ReactPhysics3D::ReactPhysics3D pugixml::pugixml)
|
||||
|
||||
# add_executable(client "client/src/main.cpp" $<TARGET_OBJECTS:openblocks>)
|
||||
# include_directories("src")
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
|
||||
add_executable(client "src/main.cpp" $<TARGET_OBJECTS:openblocks>)
|
||||
include_directories("../src")
|
||||
target_link_libraries(client ${SDL2_LIBRARIES} ${GLEW_LIBRARIES} ${GLUT_LIBRARIES} OpenGL::GL OpenGL::GLU glfw glm::glm assimp ReactPhysics3D::ReactPhysics3D)
|
||||
target_link_libraries(client ${SDL2_LIBRARIES} ${GLEW_LIBRARIES} ${GLUT_LIBRARIES} OpenGL::GL OpenGL::GLU glfw glm::glm assimp ReactPhysics3D::ReactPhysics3D pugixml::pugixml)
|
10
deps.txt
Normal file
10
deps.txt
Normal file
|
@ -0,0 +1,10 @@
|
|||
glm
|
||||
opengl
|
||||
assimp
|
||||
sdl2
|
||||
glfw
|
||||
glut
|
||||
glew
|
||||
qt6
|
||||
reactphysics3d
|
||||
pugixml
|
|
@ -66,7 +66,7 @@ else()
|
|||
qt5_create_translation(QM_FILES ${CMAKE_SOURCE_DIR} ${TS_FILES})
|
||||
endif()
|
||||
|
||||
target_link_libraries(editor PRIVATE Qt${QT_VERSION_MAJOR}::Widgets ${SDL2_LIBRARIES} ${GLEW_LIBRARIES} ${GLUT_LIBRARIES} OpenGL::GL OpenGL::GLU glfw glm::glm assimp ReactPhysics3D::ReactPhysics3D)
|
||||
target_link_libraries(editor PRIVATE Qt${QT_VERSION_MAJOR}::Widgets ${SDL2_LIBRARIES} ${GLEW_LIBRARIES} ${GLUT_LIBRARIES} OpenGL::GL OpenGL::GLU glfw glm::glm assimp ReactPhysics3D::ReactPhysics3D pugixml::pugixml)
|
||||
|
||||
# Qt for iOS sets MACOSX_BUNDLE_GUI_IDENTIFIER automatically since Qt 6.1.
|
||||
# If you are developing for iOS or macOS you should consider setting an
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
#include <reactphysics3d/collision/RaycastInfo.h>
|
||||
#include <vector>
|
||||
|
||||
#include "GLFW/glfw3.h"
|
||||
#include "physics/util.h"
|
||||
#include "qcursor.h"
|
||||
#include "qevent.h"
|
||||
|
|
|
@ -14,6 +14,8 @@
|
|||
|
||||
#include "common.h"
|
||||
#include "editorcommon.h"
|
||||
#include "objects/base/instance.h"
|
||||
#include "objects/datamodel.h"
|
||||
#include "physics/simulation.h"
|
||||
#include "objects/part.h"
|
||||
#include "qitemselectionmodel.h"
|
||||
|
@ -62,6 +64,14 @@ MainWindow::MainWindow(QWidget *parent)
|
|||
}
|
||||
});
|
||||
|
||||
connect(ui->actionSave, &QAction::triggered, this, [&]() {
|
||||
pugi::xml_document doc;
|
||||
pugi::xml_node node = doc.append_child("openblocks");
|
||||
workspace()->Serialize(&node);
|
||||
|
||||
doc.print(std::cout);
|
||||
});
|
||||
|
||||
// ui->explorerView->Init(ui);
|
||||
|
||||
simulationInit();
|
||||
|
|
|
@ -1,12 +1,20 @@
|
|||
#include "propertiesmodel.h"
|
||||
#include "datatypes/base.h"
|
||||
#include "datatypes/cframe.h"
|
||||
#include "objects/base/member.h"
|
||||
#include "qnamespace.h"
|
||||
|
||||
PropertiesModel::PropertiesModel(InstanceRef selectedItem, QWidget *parent)
|
||||
: QAbstractItemModel(parent)
|
||||
, selectedItem(selectedItem) {
|
||||
this->propertiesList = selectedItem->GetProperties();
|
||||
this->propertiesList.reserve(selectedItem->GetProperties().size());
|
||||
for (std::string name : selectedItem->GetProperties()) {
|
||||
PropertyMeta meta = selectedItem->GetPropertyMeta(name).value();
|
||||
// Don't show CFrames in properties
|
||||
if (meta.type == &Data::CFrame::TYPE) continue;
|
||||
|
||||
this->propertiesList.push_back(name);
|
||||
}
|
||||
}
|
||||
|
||||
PropertiesModel::~PropertiesModel() = default;
|
||||
|
@ -102,7 +110,7 @@ QModelIndex PropertiesModel::parent(const QModelIndex &index) const {
|
|||
}
|
||||
|
||||
int PropertiesModel::rowCount(const QModelIndex &parent) const {
|
||||
return !parent.isValid() ? selectedItem->GetProperties().size() : 0;
|
||||
return !parent.isValid() ? this->propertiesList.size() : 0;
|
||||
}
|
||||
|
||||
int PropertiesModel::columnCount(const QModelIndex &parent) const {
|
||||
|
|
|
@ -2,9 +2,10 @@
|
|||
|
||||
#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::operator WRAPPED_TYPE() { 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::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())); }
|
||||
|
||||
Data::Base::~Base() {};
|
||||
|
||||
|
@ -24,6 +25,10 @@ const Data::String Data::Null::ToString() const {
|
|||
return Data::String("null");
|
||||
}
|
||||
|
||||
void Data::Null::Serialize(pugi::xml_node* node) const {
|
||||
node->text().set("null");
|
||||
}
|
||||
|
||||
const Data::String Data::Bool::ToString() const {
|
||||
return Data::String(value ? "true" : "false");
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#include <pugixml.hpp>
|
||||
#include <string>
|
||||
|
||||
|
||||
|
@ -8,11 +9,12 @@
|
|||
public: \
|
||||
CLASS_NAME(WRAPPED_TYPE); \
|
||||
~CLASS_NAME(); \
|
||||
operator WRAPPED_TYPE(); \
|
||||
operator const WRAPPED_TYPE() const; \
|
||||
virtual const TypeInfo& GetType() const override; \
|
||||
static const TypeInfo TYPE; \
|
||||
\
|
||||
virtual const Data::String ToString() const override; \
|
||||
virtual void Serialize(pugi::xml_node* node) const override; \
|
||||
};
|
||||
|
||||
namespace Data {
|
||||
|
@ -27,6 +29,7 @@ namespace Data {
|
|||
virtual ~Base();
|
||||
virtual const TypeInfo& GetType() const = 0;
|
||||
virtual const Data::String ToString() const = 0;
|
||||
virtual void Serialize(pugi::xml_node* node) const = 0;
|
||||
};
|
||||
|
||||
class Null : Base {
|
||||
|
@ -37,6 +40,7 @@ namespace Data {
|
|||
static const TypeInfo TYPE;
|
||||
|
||||
virtual const Data::String ToString() const override;
|
||||
virtual void Serialize(pugi::xml_node* node) const override;
|
||||
};
|
||||
|
||||
DEF_WRAPPER_CLASS(Bool, bool)
|
||||
|
|
|
@ -40,7 +40,7 @@ Data::CFrame::CFrame(Data::Vector3 position, Data::Vector3 lookAt, Data::Vector3
|
|||
|
||||
Data::CFrame::~CFrame() = default;
|
||||
const Data::TypeInfo Data::CFrame::TYPE = {
|
||||
.name = "CFrame",
|
||||
.name = "CoordinateFrame",
|
||||
};
|
||||
|
||||
const Data::TypeInfo& Data::CFrame::GetType() const { return Data::Vector3::TYPE; };
|
||||
|
@ -80,3 +80,20 @@ Data::CFrame Data::CFrame::operator +(Data::Vector3 vector) const {
|
|||
Data::CFrame Data::CFrame::operator -(Data::Vector3 vector) const {
|
||||
return *this + -vector;
|
||||
}
|
||||
|
||||
// Serialization
|
||||
|
||||
void Data::CFrame::Serialize(pugi::xml_node* node) const {
|
||||
node->append_child("X").text().set(std::to_string(this->X()));
|
||||
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("R00").text().set(std::to_string(this->rotation[0][0]));
|
||||
node->append_child("R01").text().set(std::to_string(this->rotation[1][0]));
|
||||
node->append_child("R02").text().set(std::to_string(this->rotation[2][0]));
|
||||
node->append_child("R10").text().set(std::to_string(this->rotation[0][1]));
|
||||
node->append_child("R11").text().set(std::to_string(this->rotation[1][1]));
|
||||
node->append_child("R12").text().set(std::to_string(this->rotation[2][1]));
|
||||
node->append_child("R20").text().set(std::to_string(this->rotation[0][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]));
|
||||
}
|
||||
|
|
|
@ -31,6 +31,7 @@ namespace Data {
|
|||
static const TypeInfo TYPE;
|
||||
|
||||
virtual const Data::String ToString() const override;
|
||||
virtual void Serialize(pugi::xml_node* parent) const override;
|
||||
|
||||
operator glm::mat4() const;
|
||||
operator rp::Transform() const;
|
||||
|
|
|
@ -6,3 +6,9 @@ Data::String Data::Variant::ToString() const {
|
|||
return it.ToString();
|
||||
}, this->wrapped);
|
||||
}
|
||||
|
||||
void Data::Variant::Serialize(pugi::xml_node* node) const {
|
||||
std::visit([&](auto&& it) {
|
||||
it.Serialize(node);
|
||||
}, this->wrapped);
|
||||
}
|
|
@ -30,5 +30,6 @@ namespace Data {
|
|||
template <typename T> Variant(T obj) : wrapped(obj) {}
|
||||
template <typename T> T get() { return std::get<T>(wrapped); }
|
||||
Data::String ToString() const;
|
||||
void Serialize(pugi::xml_node* node) const;
|
||||
};
|
||||
}
|
|
@ -42,3 +42,11 @@ Data::Vector3 Data::Vector3::Cross(Data::Vector3 other) const {
|
|||
float Data::Vector3::Dot(Data::Vector3 other) const {
|
||||
return glm::dot(this->vector, other.vector);
|
||||
}
|
||||
|
||||
// Serialization
|
||||
|
||||
void Data::Vector3::Serialize(pugi::xml_node* node) const {
|
||||
node->append_child("X").text().set(std::to_string(this->X()));
|
||||
node->append_child("Y").text().set(std::to_string(this->Y()));
|
||||
node->append_child("Z").text().set(std::to_string(this->Z()));
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ namespace Data {
|
|||
static Data::Vector3 ONE;
|
||||
|
||||
virtual const Data::String ToString() const override;
|
||||
virtual void Serialize(pugi::xml_node* node) const override;
|
||||
|
||||
operator glm::vec3() const;
|
||||
operator rp::Vector3() const;
|
||||
|
|
|
@ -141,3 +141,26 @@ std::vector<std::string> Instance::GetProperties() {
|
|||
cachedMemberList = memberList;
|
||||
return memberList;
|
||||
}
|
||||
|
||||
// Serialization
|
||||
|
||||
void Instance::Serialize(pugi::xml_node* parent) {
|
||||
pugi::xml_node node = parent->append_child("Item");
|
||||
node.append_attribute("class").set_value(this->GetClass()->className);
|
||||
|
||||
// Add properties
|
||||
pugi::xml_node propertiesNode = node.append_child("Properties");
|
||||
for (std::string name : GetProperties()) {
|
||||
PropertyMeta meta = GetPropertyMeta(name).value();
|
||||
if (meta.flags & PropertyFlags::PROP_NOSAVE) continue; // This property should not be serialized. Skip...
|
||||
|
||||
pugi::xml_node propertyNode = propertiesNode.append_child(meta.type->name);
|
||||
propertyNode.append_attribute("name").set_value(name);
|
||||
GetPropertyValue(name)->Serialize(&propertyNode);
|
||||
}
|
||||
|
||||
// Add children
|
||||
for (InstanceRef child : this->children) {
|
||||
child->Serialize(&node);
|
||||
}
|
||||
}
|
|
@ -11,6 +11,7 @@
|
|||
#include <map>
|
||||
#include <vector>
|
||||
#include <../include/expected.hpp>
|
||||
#include <pugixml.hpp>
|
||||
|
||||
#include "member.h"
|
||||
|
||||
|
@ -68,6 +69,9 @@ public:
|
|||
tl::expected<PropertyMeta, MemberNotFound> GetPropertyMeta(std::string name);
|
||||
// Returning a list of property names feels kinda janky. Is this really the way to go?
|
||||
std::vector<std::string> GetProperties();
|
||||
|
||||
// Serialization
|
||||
void Serialize(pugi::xml_node* parent);
|
||||
};
|
||||
|
||||
typedef std::shared_ptr<Instance> InstanceRef;
|
||||
|
|
|
@ -15,21 +15,27 @@ struct FieldCodec {
|
|||
Data::Variant (*read)(void* source);
|
||||
};
|
||||
|
||||
template <typename T, typename U>
|
||||
void _writeCodec(Data::Variant source, void* destination) {
|
||||
*(U*)destination = (U)source.get<T>();
|
||||
}
|
||||
|
||||
template <typename T, typename U>
|
||||
Data::Variant _readCodec(void* source) {
|
||||
return T(*(U*)source);
|
||||
}
|
||||
|
||||
template <typename T, typename U>
|
||||
constexpr FieldCodec fieldCodecOf() {
|
||||
return FieldCodec {
|
||||
.write = &_writeCodec<T, U>,
|
||||
.read = &_readCodec<T, U>,
|
||||
.write = [](Data::Variant source, void* destination) {
|
||||
*(U*)destination = (U)source.get<T>();
|
||||
},
|
||||
.read = [](void* source) -> Data::Variant {
|
||||
return T(*(U*)source);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
constexpr FieldCodec fieldCodecOf() {
|
||||
return FieldCodec {
|
||||
.write = [](Data::Variant source, void* destination) {
|
||||
*(T*)destination = source.get<T>();
|
||||
},
|
||||
.read = [](void* source) -> Data::Variant {
|
||||
return *(T*)source;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -38,11 +44,17 @@ std::function<void(std::string name)> memberFunctionOf(void(T::*func)(std::strin
|
|||
return std::bind(func, obj, std::placeholders::_1);
|
||||
}
|
||||
|
||||
enum PropertyFlags {
|
||||
PROP_HIDDEN = 1 << 0, // Hidden from the editor
|
||||
PROP_NOSAVE = 1 << 1, // Do not serialize
|
||||
};
|
||||
|
||||
struct PropertyMeta {
|
||||
void* backingField;
|
||||
const Data::TypeInfo* type;
|
||||
FieldCodec codec;
|
||||
std::optional<std::function<void(std::string name)>> updateCallback;
|
||||
PropertyFlags flags;
|
||||
};
|
||||
|
||||
typedef std::variant<PropertyMeta> MemberMeta;
|
||||
|
|
|
@ -63,9 +63,29 @@ Part::Part(PartConstructParams params): Instance(&TYPE), cframe(Data::CFrame(par
|
|||
this->memberMap = std::make_unique<MemberMap>(MemberMap {
|
||||
.super = std::move(this->memberMap),
|
||||
.members = {
|
||||
{ "Anchored", { .backingField = &anchored, .type = &Data::Bool::TYPE, .codec = fieldCodecOf<Data::Bool, bool>(), .updateCallback = memberFunctionOf(&Part::onUpdated, this) } },
|
||||
{ "Position", { .backingField = &cframe, .type = &Data::Vector3::TYPE, .codec = cframePositionCodec(), .updateCallback = memberFunctionOf(&Part::onUpdated, this) } },
|
||||
{ "Rotation", { .backingField = &cframe, .type = &Data::Vector3::TYPE, .codec = cframeRotationCodec(), .updateCallback = memberFunctionOf(&Part::onUpdated, this) } }
|
||||
{ "Anchored", {
|
||||
.backingField = &anchored,
|
||||
.type = &Data::Bool::TYPE,
|
||||
.codec = fieldCodecOf<Data::Bool, bool>(),
|
||||
.updateCallback = memberFunctionOf(&Part::onUpdated, this)
|
||||
} }, { "Position", {
|
||||
.backingField = &cframe,
|
||||
.type = &Data::Vector3::TYPE,
|
||||
.codec = cframePositionCodec(),
|
||||
.updateCallback = memberFunctionOf(&Part::onUpdated, this),
|
||||
.flags = PropertyFlags::PROP_NOSAVE
|
||||
} }, { "Rotation", {
|
||||
.backingField = &cframe,
|
||||
.type = &Data::Vector3::TYPE,
|
||||
.codec = cframeRotationCodec(),
|
||||
.updateCallback = memberFunctionOf(&Part::onUpdated, this),
|
||||
.flags = PropertyFlags::PROP_NOSAVE
|
||||
} }, { "CFrame", {
|
||||
.backingField = &cframe,
|
||||
.type = &Data::CFrame::TYPE,
|
||||
.codec = fieldCodecOf<Data::CFrame>(),
|
||||
.updateCallback = memberFunctionOf(&Part::onUpdated, this),
|
||||
} }
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue