World object and Entities can be serialized and deserialized

master
Joey Pollack 3 years ago
parent 57e610b893
commit 829ca1bbb5

@ -15,7 +15,7 @@ Core:
✔ Wrap NFD in an API in the platform module @low @done(22-05-31 15:44)
Wrapper added to utils - not platform
✔ Add custom (64 bit?) UUID generator (based on Chreno's UUIDs) @done(22-06-27 13:34)
☐ Allow Entities to have children @medium
✔ Allow Entities to have children @medium @done(22-07-06 17:54)
✔ Add Terminal subsystem to allow for printing colored text in a cross-platform way @done(22-05-16 18:05)
✔ Create a LogListener that uses the colored text (replace the current stdout listener) @done(22-05-16 18:23)
✔ Replace XML with JSON (https://github.com/nlohmann/json) @high @done(22-05-19 15:35)

@ -1,5 +1,7 @@
Editor:
☐ Save and unload world data when a new world is selected
✔ Load the selected world when double clicked on in the asset browser @done(22-07-05 14:29)
✔ Asset Location MUST be relative to the project root directory @critical @done(22-05-20 18:35)
✔ Switch to NFD dialogs @done(22-05-20 18:35)
✔ Come up with project directory structure @done (9/17/2021, 6:46:44 PM)
@ -7,9 +9,9 @@ Editor:
✔ Implement Run Mode interface class @high @done (2/8/2022, 4:05:17 PM)
✔ Reference raw asset files in a "content" folder@high @done (3/3/2022, 3:15:32 PM)
✔ Platform independant file browsing @done (2/8/2022, 4:05:29 PM)
Turn World into an editor asset that can be created
☐ Store entities in the World object
☐ Implement Saving/loading the World asset
Turn World into an editor asset that can be created @done(22-07-05 14:04)
✔ Store entities in the World object @done(22-07-06 17:53)
✔ Implement Saving/loading the World asset @done(22-07-06 17:53)
☐ Test the Asset Trashing system @high
☐ Add ability to browse and restore trashed assets
✔ Figure out how to make game asset types integrate with editor asset types @critical @done(22-06-27 13:32)
@ -55,7 +57,7 @@ Editor:
Properties:
☐ Implement showing components on selected Entity
✔ Implement showing components on selected Entity @done(22-07-06 17:53)
Tools:
Tile Map Editor:

@ -17,10 +17,10 @@ namespace lunarium
class JSONSerializable
{
public:
[[nodiscard]] virtual OpRes Serialize(nlohmann::json& node) = 0;
[[nodiscard]] virtual OpRes Deserialize(nlohmann::json& node) = 0;
[[nodiscard]] virtual nlohmann::json AsJSON() = 0;
[[nodiscord]] virtual bool IsValidNode(nlohmann::json& node) = 0;
[[nodiscard]] virtual OpRes Serialize(nlohmann::ordered_json& node) = 0;
[[nodiscard]] virtual OpRes Deserialize(nlohmann::ordered_json& node) = 0;
[[nodiscard]] virtual nlohmann::ordered_json AsJSON() = 0;
[[nodiscord]] virtual bool IsValidNode(nlohmann::ordered_json& node) = 0;
};
}

@ -8,15 +8,18 @@
#include "world.h"
#include <LunariumConfig.h>
#include <editor/editor.h>
#include <world/world.h>
#include <filesystem>
#include <fstream>
namespace lunarium { namespace editor
{
World::World(std::filesystem::path name)
: EditorAsset(AssetType::EATYPE_WORLD), mpWorld(new lunarium::World)
{
mLocation = name;
mLocation = name.string() + ".wld";
}
World::~World()
@ -29,47 +32,91 @@ namespace lunarium { namespace editor
return mpWorld;
}
void World::UnloadWorld()
{
delete mpWorld;
mpWorld = nullptr;
}
OpRes World::LoadWorld()
{
return LoadRawFile();
}
OpRes World::LoadRawFile()
{
return OpRes::OK();
}
OpRes World::Deserialize(nlohmann::json& node)
OpRes World::Deserialize(nlohmann::ordered_json& node)
{
// TODO: Implement World::LoadFromJSON
// Create the lunarium::World Object here
// Replace the one created in the constructor
#if !BUILD_NO_EDITOR // Only does this when this is an editor build
std::filesystem::path file_path = mAssetDir / mLocation;
std::ifstream ifs = std::ifstream(file_path.string().c_str());
if (!ifs.is_open())
{
return OpRes::Fail("Could not open contents file: %s", file_path.string().c_str());
}
nlohmann::ordered_json world_file;
ifs >> world_file;
ifs.close();
if (mpWorld)
{
mpWorld->Deserialize(world_file).LogIfFailed(Editor::LogCat);
}
#endif
return OpRes::OK();
}
OpRes World::Serialize(nlohmann::json& node)
OpRes World::Serialize(nlohmann::ordered_json& node)
{
// TODO: Implement World::SaveToJSON
// Store the entities UUID - the Entity class will serialize itself
#if !BUILD_NO_EDITOR // Only does this when this is an editor build
nlohmann::ordered_json world_file;
if (mpWorld)
{
mpWorld->Serialize(world_file).LogIfFailed(Editor::LogCat);
}
std::filesystem::path file_path = mAssetDir / mLocation;
std::ofstream ofs = std::ofstream(file_path.string().c_str(), std::ios_base::trunc);
if (!ofs.is_open())
{
return OpRes::Fail("Could not save file: %s", file_path.string().c_str());
}
ofs << std::setw(4) << world_file;
ofs.close();
#endif
return OpRes::OK();
}
bool World::IsValidNode(nlohmann::json& node)
bool World::IsValidNode(nlohmann::ordered_json& node)
{
return true;
}
nlohmann::json World::AsJSON()
nlohmann::ordered_json World::AsJSON()
{
#if !BUILD_NO_EDITOR // Only does this when this is an editor build
nlohmann::json node;
nlohmann::ordered_json node;
Serialize(node).LogIfFailed(Editor::LogCat);
return node;
#endif
return nlohmann::json();
return nlohmann::ordered_json();
}
}}

@ -70,7 +70,7 @@ namespace lunarium { namespace editor
return OpRes::Fail("Could not open contents file: %s", mContentFile.string().c_str());
}
nlohmann::json j;
nlohmann::ordered_json j;
ifs >> j;
ifs.close();
@ -175,7 +175,7 @@ namespace lunarium { namespace editor
for (auto iter = mAssets.begin(); iter != mAssets.end(); iter++)
{
EditorAsset* pAsset = iter->second;
nlohmann::json asset;
nlohmann::ordered_json asset;
asset["Type"] = (i32)pAsset->GetType();
asset["ID"] = pAsset->GetID();
@ -184,7 +184,7 @@ namespace lunarium { namespace editor
// TODO: This needs to be a relative path! (Relative to the project root)
asset["Location"] = pAsset->GetFileLocation().string().c_str();
if (Failed(pAsset->Serialize(asset).LogIfFailed(Editor::LogCat).LogIfFailed(Editor::LogCat)))
if (Failed(pAsset->Serialize(asset).LogIfFailed(Editor::LogCat)))
{
return OpRes::Fail("Could not save asset meta data for file: %s", pAsset->GetFileLocation().string().c_str());
}
@ -266,6 +266,7 @@ namespace lunarium { namespace editor
return OpRes::Fail("Could not import asset, ID collision. ID: %llu", mNextID);
}
asset->mAssetDir = mpProject->GetAssetDirectory();
asset->mID = mNextID;
mNextID++;
mAssets[asset->mID] = asset;
@ -344,7 +345,7 @@ namespace lunarium { namespace editor
Save().LogIfFailed(Editor::LogCat, "Asset was moved");
}
bool ContentManager::IsValidAsset(nlohmann::json& node)
bool ContentManager::IsValidAsset(nlohmann::ordered_json& node)
{
if (node.is_null()) { return false; }
if (node["ID"].is_null()) { return false; }

@ -66,7 +66,7 @@ namespace lunarium { namespace editor
ContentManager& operator=(const ContentManager&) = delete;
private: // Helpers
[[nodiscard]] bool IsValidAsset(nlohmann::json& node);
[[nodiscard]] bool IsValidAsset(nlohmann::ordered_json& node);
[[nodiscord]] EditorAsset* CreateAsset(AssetType type);
void FreeAssets();

@ -37,7 +37,7 @@ namespace lunarium { namespace editor
return OpRes::OK();
}
OpRes TileSet::Deserialize(nlohmann::json& node)
OpRes TileSet::Deserialize(nlohmann::ordered_json& node)
{
#if !BUILD_NO_EDITOR // Only does this when this is an editor build
@ -63,7 +63,7 @@ namespace lunarium { namespace editor
return OpRes::OK();
}
OpRes TileSet::Serialize(nlohmann::json& node)
OpRes TileSet::Serialize(nlohmann::ordered_json& node)
{
#if !BUILD_NO_EDITOR // Only does this when this is an editor build
@ -77,9 +77,9 @@ namespace lunarium { namespace editor
return OpRes::OK();
}
nlohmann::json TileSet::AsJSON()
nlohmann::ordered_json TileSet::AsJSON()
{
nlohmann::json node;
nlohmann::ordered_json node;
#if !BUILD_NO_EDITOR // Only does this when this is an editor build
@ -93,7 +93,7 @@ namespace lunarium { namespace editor
return node;
}
bool TileSet::IsValidNode(nlohmann::json& node)
bool TileSet::IsValidNode(nlohmann::ordered_json& node)
{
if (node["TileSetID"].is_null()) { return false; }
if (!node["TileSetID"].is_number()) { return false; }

@ -24,10 +24,10 @@ namespace lunarium { namespace editor
// Load the raw asset file from the internal location
OpRes LoadRawFile();
OpRes Serialize(nlohmann::json& node);
OpRes Deserialize(nlohmann::json& node);
bool IsValidNode(nlohmann::json& node);
nlohmann::json AsJSON();
OpRes Serialize(nlohmann::ordered_json& node);
OpRes Deserialize(nlohmann::ordered_json& node);
bool IsValidNode(nlohmann::ordered_json& node);
nlohmann::ordered_json AsJSON();
void SetTileSetID(int id);
int GetTileSetID() const;

@ -11,6 +11,7 @@
#include "editor_asset.h"
#include <utils/uuid.h>
#include <filesystem>
namespace lunarium
@ -28,12 +29,14 @@ namespace lunarium { namespace editor
~World();
[[nodiscard]] virtual OpRes LoadRawFile();
[[nodiscard]] virtual OpRes Serialize(nlohmann::json& node);
[[nodiscard]] virtual OpRes Deserialize(nlohmann::json& node);
[[nodiscord]] virtual bool IsValidNode(nlohmann::json& node);
[[nodiscard]] virtual nlohmann::json AsJSON();
[[nodiscard]] virtual OpRes Serialize(nlohmann::ordered_json& node);
[[nodiscard]] virtual OpRes Deserialize(nlohmann::ordered_json& node);
[[nodiscord]] virtual bool IsValidNode(nlohmann::ordered_json& node);
[[nodiscard]] virtual nlohmann::ordered_json AsJSON();
lunarium::World* GetWorld();
void UnloadWorld();
OpRes LoadWorld();
private: // DATA
lunarium::World* mpWorld;

@ -438,5 +438,11 @@ namespace editor
//Logger::Info(Editor::LogCat, "ENTITY SELECT: %ll", (long long) pEnt);
((PropertiesView*)mPanelManager.GetPanel(mPanels.PropertiesView))->SetSelection(pEnt);
}
void Editor::ChangeWorld(lunarium::World* pWorld)
{
// TODO: Unload current world, Load new world
((WorldTree*)mPanelManager.GetPanel(mPanels.WorldTree))->SetWorld(pWorld);
}
}
}

@ -214,8 +214,8 @@ namespace editor
if (ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left))
{
// TODO: Open relevant editor
Logger::Info(Editor::LogCat, "Asset double clicked on. Relevant editor should open!");
// Logger::Info(Editor::LogCat, "Asset double clicked on. Relevant editor should open!");
HandleAssetDoubleClick((*iter));
}
if (ImGui::IsItemHovered() && ImGui::IsMouseClicked(ImGuiMouseButton_Right))
@ -333,5 +333,15 @@ namespace editor
}
void AssetBrowser::HandleAssetDoubleClick(EditorAsset* pAsset)
{
if (pAsset->GetType() == AssetType::EATYPE_WORLD)
{
World* pAssetWorld = (World*)pAsset;
mpEditor->ChangeWorld(pAssetWorld->GetWorld());
}
}
}
}

@ -19,6 +19,8 @@ namespace lunarium
{
namespace editor
{
class EditorAsset;
class AssetBrowser : public Panel
{
public:
@ -47,6 +49,7 @@ namespace editor
// General Helpers
void HandleAssetDrop(std::filesystem::path dir);
void HandleAssetDoubleClick(EditorAsset* pAsset);
private: // POPUP ENUMS

@ -25,7 +25,7 @@ namespace lunarium
mpEditor(editor),
// TODO: Temp world created here
mpWorld(nullptr),
mDoNewEntity(false), mpSelectedEntity(nullptr)
mpSelectedEntity(nullptr)
{
AddPopup(PopUp::NEW_ENTITY, "New Entity", [](Panel *p)
{
@ -59,6 +59,7 @@ namespace lunarium
void WorldTree::SetWorld(lunarium::World *pWorld)
{
mpWorld = pWorld;
mpSelectedEntity = nullptr;
}
lunarium::World *WorldTree::GetWorld()

@ -38,9 +38,6 @@ namespace editor
// Context menus
void DoContextMenu();
// Popup events
bool mDoNewEntity;
void HandlePopupEvents();
void PopupNewEntity();

@ -24,9 +24,10 @@ namespace lunarium
{
std::string Info;
TagComponent()
TagComponent(std::string _info = "")
{
Info.reserve(256);
Info.insert(0, _info);
}
};
@ -36,8 +37,8 @@ namespace lunarium
glm::vec3 Rotation = { 0.0f, 0.0f, 0.0f };
glm::vec3 Scale = { 1.0f, 1.0f, 1.0f };
TransformComponent()
: Position(glm::vec3(0.0f, 0.0f, 0.0f)), Rotation(glm::vec3(0.0f, 0.0f, 0.0f)), Scale(glm::vec3(1.0f, 1.0f, 1.0f))
TransformComponent(glm::vec3 p = glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3 r = glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3 s = glm::vec3(1.0f, 1.0f, 1.0f))
: Position(p), Rotation(r), Scale(s)
{
}

@ -8,6 +8,8 @@
#include "entity.h"
#include "world.h"
#include "components.h"
#include <utils/logger.h>
namespace lunarium
{
@ -45,4 +47,96 @@ namespace lunarium
{
mChildren.push_back(pChild);
}
/////////////////////////////////////////////////////////////////////
// SERIALIZING
/////////////////////////////////////////////////////////////////////
OpRes Entity::Serialize(nlohmann::ordered_json& node)
{
#if !BUILD_NO_EDITOR // Only does this when this is an editor build
node["UUID"] = mUUID;
auto& components = node["components"];
if (HasComponent<TagComponent>())
{
nlohmann::ordered_json tag;
tag["type_name"] = "TagComponent";
tag["info"] = GetComponent<TagComponent>().Info;
components.emplace_back(tag);
}
// TODO: If (HasComponent<TransformComponent>()) ...
// Children
auto& children = node["children"];
for (int i = 0; i < mChildren.size(); i++)
{
nlohmann::ordered_json child;
mChildren[i]->Serialize(child).LogIfFailed(LogCategory::GAME_SYSTEM);
children.emplace_back(child);
}
#endif
return OpRes::OK();
}
OpRes Entity::Deserialize(nlohmann::ordered_json& node)
{
#if !BUILD_NO_EDITOR // Only does this when this is an editor build
mUUID = node["UUID"].get<u64>();
// TODO: Load components
auto& components = node["components"];
for (auto iter = components.begin(); iter != components.end(); iter++)
{
auto& comp = *iter;
std::string comp_type_name = comp["type_name"].get<std::string>();
if ("TagComponent" == comp_type_name)
{
std::string info = comp["info"].get<std::string>();
AddComponent<TagComponent>(info);
}
}
// TODO: Load children
auto& children = node["children"];
for (auto iter = children.begin(); iter != children.end(); iter++)
{
auto& child = *iter;
Entity* ne = new Entity(mWorld);
ne->Deserialize(child).LogIfFailed(LogCategory::GAME_SYSTEM);
mChildren.push_back(ne);
}
#endif
return OpRes::OK();
}
bool Entity::IsValidNode(nlohmann::ordered_json& node)
{
if (node["UUID"].is_null()) { return false; }
if (!node["UUID"].is_number()) { return false; }
return true;
}
nlohmann::ordered_json Entity::AsJSON()
{
#if !BUILD_NO_EDITOR // Only does this when this is an editor build
nlohmann::ordered_json node;
Serialize(node).LogIfFailed(LogCategory::GAME_SYSTEM);
return node;
#endif
return nlohmann::ordered_json();
}
}

@ -18,7 +18,7 @@
namespace lunarium // TODO: : public JSONSerializable
{
class Entity
class Entity : public JSONSerializable
{
public:
Entity(World& w);
@ -69,10 +69,11 @@ namespace lunarium // TODO: : public JSONSerializable
bool HasChildren() const;
void AddChild(Entity* pChild);
// TODO: Move to base class
OpRes SaveToJSON(nlohmann::json& node);
OpRes LoadFromJSON(nlohmann::json& node);
OpRes IsNodeValid(nlohmann::json& node);
// Serializing
[[nodiscard]] virtual OpRes Serialize(nlohmann::ordered_json& node);
[[nodiscard]] virtual OpRes Deserialize(nlohmann::ordered_json& node);
[[nodiscord]] virtual bool IsValidNode(nlohmann::ordered_json& node);
[[nodiscard]] virtual nlohmann::ordered_json AsJSON();
private:
LUUID mUUID;

@ -26,6 +26,7 @@ namespace lunarium
// }
World::World()
: mUUID(UUID::GetNewID())
{
}
@ -101,4 +102,79 @@ namespace lunarium
return iter == mEntities.end();
}
/////////////////////////////////////////////////////////////////////
// SERIALIZING
/////////////////////////////////////////////////////////////////////
OpRes World::Serialize(nlohmann::ordered_json& node)
{
#if !BUILD_NO_EDITOR // Only does this when this is an editor build
node["UUID"] = mUUID;
node["NumberOfEntities"] = mEntities.size();
auto& ents = node["Entities"];
for (int i = 0; i < mEntities.size(); i++)
{
nlohmann::ordered_json e;
mEntities[i]->Serialize(e);
ents.emplace_back(e);
}
#endif
return OpRes::OK();
}
OpRes World::Deserialize(nlohmann::ordered_json& node)
{
#if !BUILD_NO_EDITOR // Only does this when this is an editor build
mUUID = node["UUID"].get<u64>();
int num_entities = node["NumberOfEntities"].get<int>();
auto& ents = node["Entities"];
for (auto it = ents.begin(); it != ents.end(); ++it)
{
auto& ent = (*it);
Entity* new_ent = new Entity(*this);
if (!new_ent->IsValidNode(ent))
{
delete new_ent;
return OpRes::Fail("Invalid entity node");
}
new_ent->Deserialize(ent);
mEntities.push_back(new_ent);
}
#endif
return OpRes::OK();
}
bool World::IsValidNode(nlohmann::ordered_json& node)
{
if (node["UUID"].is_null()) { return false; }
if (!node["UUID"].is_number()) { return false; }
if (node["NumberOfEntities"].is_null()) { return false; }
if (!node["NumberOfEntities"].is_number()) { return false; }
if (node["Entities"].is_null()) { return false; }
return false;
}
nlohmann::ordered_json World::AsJSON()
{
#if !BUILD_NO_EDITOR // Only does this when this is an editor build
nlohmann::ordered_json node;
Serialize(node).LogIfFailed(LogCategory::GAME_SYSTEM);
return node;
#endif
return nlohmann::ordered_json();
}
}

@ -14,6 +14,7 @@
#include <core/common_defs.h>
#include <core/types.h>
#include <assets/serializing/json_serializable.h>
#include <utils/op_res.h>
#include <entt/entt.hpp>
@ -38,7 +39,7 @@ namespace lunarium
int Index;
};
class World
class World : public JSONSerializable
{
public:
struct Region
@ -72,8 +73,16 @@ namespace lunarium
std::vector<Entity*>::iterator EntitiesBegin();
bool EntitiesIsEnd(std::vector<Entity*>::iterator& iter);
// Serializing
[[nodiscard]] virtual OpRes Serialize(nlohmann::ordered_json& node);
[[nodiscard]] virtual OpRes Deserialize(nlohmann::ordered_json& node);
[[nodiscord]] virtual bool IsValidNode(nlohmann::ordered_json& node);
[[nodiscard]] virtual nlohmann::ordered_json AsJSON();
private:
LUUID mUUID;
std::string mWorldName;
entt::registry mECSRegistry;
std::vector<Entity*> mEntities;

Loading…
Cancel
Save