XML changed to JSON

gui_api_redesign
Joey Pollack 4 years ago
parent de0bc576ee
commit 6f00e84229

3
.gitmodules vendored

@ -7,9 +7,6 @@
[submodule "external/lua"]
path = external/lua
url = https://github.com/walterschell/Lua.git
[submodule "external/pugixml"]
path = external/pugixml
url = https://github.com/zeux/pugixml.git
[submodule "external/freetype"]
path = external/freetype
url = https://gitlab.freedesktop.org/freetype/freetype.git

@ -122,10 +122,6 @@ if(NOT EXISTS "${PROJECT_SOURCE_DIR}/external/lua/CMakeLists.txt")
message(FATAL_ERROR "LUA submodule was not downloaded! GIT_SUBMODULE was turned off or failed. Please update submodules and try again.")
endif()
if(NOT EXISTS "${PROJECT_SOURCE_DIR}/external/pugixml/CMakeLists.txt")
message(FATAL_ERROR "PUGIXML submodule was not downloaded! GIT_SUBMODULE was turned off or failed. Please update submodules and try again.")
endif()
if(NOT EXISTS "${PROJECT_SOURCE_DIR}/external/freetype/CMakeLists.txt")
message(FATAL_ERROR "FREETYPE submodule was not downloaded! GIT_SUBMODULE was turned off or failed. Please update submodules and try again.")
endif()
@ -149,9 +145,6 @@ add_subdirectory(external/dearimgui)
# add lua -- https://github.com/walterschell/Lua
add_subdirectory(external/lua)
# add pugixml
add_subdirectory(external/pugixml)
# add freetype
add_subdirectory(external/freetype)
@ -192,7 +185,7 @@ target_link_directories(${PROJECT_NAME}
PRIVATE external/box2d/bin
)
target_link_libraries(${PROJECT_NAME} box2d glfw glad glm dearimgui lua_static pugixml freetype testbed)
target_link_libraries(${PROJECT_NAME} box2d glfw glad glm dearimgui lua_static freetype testbed)
if (NOT NO_EDITOR)
target_link_libraries(${PROJECT_NAME} editor)

@ -1,5 +1,6 @@
High Importance:
☐ Editor does not get absolute paths from the file browser - replace with NFD dialogs @critical
✔ The Map Editor does not get the tile maps when a project is opened @high @done (3/3/2022, 2:47:41 PM)
✔ Tile Set IDs (as opposed to the Asset ID) is not saved or loaded yet @high @done (3/11/2022, 2:10:30 PM)
✔ Had to flip the V component of the UVs for the sprite vertices. This fixes the partial image drawing but will need to be accounted for in other places in the editor. @done (3/14/2022, 1:46:48 PM)

@ -7,9 +7,10 @@ Build System:
✔ Modify .sh scripts to recognize the noeditor flag @done (1/25/2022, 3:59:23 PM)
Core:
Add custom (64 bit?) UUID generator (based on Chreno's UUIDs: )
✔ 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
Replace XML with JSON (https://github.com/nlohmann/json) @high @done(22-05-19 15:35)
✔ Move internal libs back into the core and refactor @high @done(22-05-17 14:27)
✔ Utils @done(22-05-13 17:29)
✔ assets @done(22-05-17 14:27)

@ -1,5 +1,7 @@
Editor:
☐ Asset Location MUST be relative to the project root directory @critical
☐ Switch to NFD dialogs
✔ Come up with project directory structure @done (9/17/2021, 6:46:44 PM)
✔ Make the editor a separate module @high @done (11/1/2021, 2:24:35 PM)
✔ Implement Run Mode interface class @high @done (2/8/2022, 4:05:17 PM)

1
external/pugixml vendored

@ -1 +0,0 @@
Subproject commit 9e382f98076e57581fcc61323728443374889646

@ -257,6 +257,9 @@ namespace lunarium
auto& s = j["State"];
s["DataDirectory"] = DataDirectory;
const char* modes[] = { "game", "editor", "test" };
s["Mode"] = modes[Mode];
auto& d = s["Display"];
const char* names[] = { "opengl", "vulkan", "unknown" };
d["FullScreenResolution"]["Width"] = Display.FullScreenResolution.Width;

@ -102,8 +102,8 @@ namespace lunarium
if (ImGui::InputText("selection", mInputBuffer, mBufferSize))
{
// mInputSelection = mCurrentDirectory / mInputBuffer;
mInputSelection = mInputBuffer;
mInputSelection = mCurrentDirectory / mInputBuffer;
//mInputSelection = mInputBuffer;
mpSelectedItem = &mInputSelection;
}

@ -31,6 +31,5 @@ target_include_directories(editor
PUBLIC ../../../external/glad/include
PUBLIC ../../../external/glfw/include
PUBLIC ../../../external/box2d/include
PUBLIC ../../../external/pugixml/src
PUBLIC ../../../external/
)

@ -9,10 +9,11 @@
******************************************************************************/
#include "content_manager.h"
#include <editor/editor.h>
#include "../project.h"
#include <pugixml.hpp>
#include <core/common_defs.h>
#include "editor_asset.h"
#include "../project.h"
#include <editor/editor.h>
#include <fstream>
// Asset types
#include "tile_set.h"
@ -52,7 +53,7 @@ namespace lunarium { namespace editor
mpProject = project;
std::filesystem::path root = mpProject->GetRootDirectory();
mContentFile = root / "contents/content_meta.xml";
mContentFile = root / "contents/content_meta.json";
// If this is a new project being generated just create a base contents file
if (!std::filesystem::exists(mContentFile))
@ -72,72 +73,93 @@ namespace lunarium { namespace editor
return Save();
}
// Load the file
pugi::xml_document doc;
pugi::xml_parse_result result = doc.load_file(mContentFile.string().c_str());
if (result.status != pugi::xml_parse_status::status_ok)
std::ifstream ifs = std::ifstream(mContentFile.string().c_str());
if (!ifs.is_open())
{
return OpRes::Fail("Could not open contents file: %s, %s", mContentFile.string().c_str(), result.description());
return OpRes::Fail("Could not open contents file: %s", mContentFile.string().c_str());
}
pugi::xml_node proj_node = doc.child("Project");
if (!proj_node)
nlohmann::json j;
ifs >> j;
ifs.close();
auto p = j["Project"];
if (p.is_null())
{
return OpRes::Fail("content_meta.xml missing Project root node");
return OpRes::Fail("content_meta.json missing Project element");
}
// Get ID
pugi::xml_node ID = proj_node.child("NextID");
if (!ID)
if (p["NextID"].is_null())
{
return OpRes::Fail("content_meta.xml missing NextID node");
}
mNextID = ID.attribute("ID").as_ullong();
mNextID = p["NextID"].get<u64>();
pugi::xml_node contents = proj_node.child("Contents");
auto c = p["Contents"];
if (!contents)
if (c.is_null())
{
return OpRes::Fail("content_meta.xml missing Contents node");
}
// Iterate through content
for (pugi::xml_node asset = contents.child("Asset"); asset; asset = asset.next_sibling("Asset"))
// ARRAY EXAMPLE
/*
std::string raw = "{\"Contents\" : [ { \"Name\" : \"first\", \"ID\" : 1 }, { \"Name\" : \"second\", \"ID\" : 2 }]}";
auto j = nlohmann::json::parse(raw);
for (auto it = j["Contents"].begin(); it != j["Contents"].end(); ++it)
{
std::cout << (*it)["ID"] << " - " << (*it)["Name"] << "\n";
}
nlohmann::json oj;
oj["Contents"].push_back({ {"Name", "first"}, {"ID", 1} });
oj["Contents"].push_back({ {"Name", "second"}, { "ID", 2 } });
std::cout << "\n" << std::setw(4) << oj;
*/
///////////////////////////////////
for (auto it = c.begin(); it != c.end(); ++it)
{
auto& asset = (*it);
if (!IsValidAsset(asset))
{
return OpRes::Fail("Invalid asset node in content_meta.xml");
return OpRes::Fail("Invalid asset node in content_meta.json");
}
// Load Type
AssetType type = (AssetType)asset.attribute("Type").as_int();
AssetType type = (AssetType)asset["Type"].get<int>();
EditorAsset* pAsset = CreateAsset(type);
pAsset->mAssetDir = mpProject->GetAssetDirectory();
if (!pAsset)
{
return OpRes::Fail("Could not create Editor Asset. Unknown type: %n", (int)type);
}
pAsset->mAssetDir = mpProject->GetAssetDirectory();
// Load ID
pAsset->mID = (uint64_t)asset.attribute("ID").as_ullong();
pAsset->mID = asset["ID"].get<u64>();
// Load Location
pAsset->mLocation = std::filesystem::path(asset.attribute("Location").as_string());
pAsset->mLocation = std::filesystem::path(asset["Location"].get<std::string>());
// Load type specific data
if (Failed(pAsset->LoadFromXML(asset).LogIfFailed(Editor::LogCat)))
if (Failed(pAsset->LoadFromJSON(asset).LogIfFailed(Editor::LogCat).LogIfFailed(Editor::LogCat)))
{
return OpRes::Fail("Could not load asset type specific data for asset with ID: %llu, File: %s",
pAsset->GetID(), pAsset->GetFileLocation().filename().string().c_str());
}
// Store asset
if (mAssets.find(pAsset->mID) != mAssets.end())
auto iter = mAssets.find(pAsset->mID);
if (iter != mAssets.end())
{
return OpRes::Fail("Asset ID collision, ID: %llu, File: %s",
return OpRes::Fail("Asset ID collision, First asset: ID: %llu, File: %s, Second asset: ID: %llu, File: %s",
iter->second->GetID(), iter->second->GetFileLocation().filename().string().c_str(),
pAsset->GetID(), pAsset->GetFileLocation().filename().string().c_str());
}
@ -154,35 +176,43 @@ namespace lunarium { namespace editor
return OpRes::Fail("ConentManager::Save failed: no project set");
}
nlohmann::json oj;
auto& p = oj["Project"];
// Save header info
pugi::xml_document doc;
pugi::xml_node proj_node = doc.append_child("Project");
proj_node.append_attribute("Name").set_value(mpProject->GetName().c_str());
proj_node.append_child("NextID").append_attribute("ID").set_value(mNextID);
// pugi::xml_node proj_node = doc.append_child("Contents");
p["Name"] = mpProject->GetName();
p["NextID"] = mNextID;
// Save all assets
pugi::xml_node contents = proj_node.append_child("Contents");
auto& c = p["Contents"];
for (auto iter = mAssets.begin(); iter != mAssets.end(); iter++)
{
EditorAsset* pAsset = iter->second;
pugi::xml_node asset = contents.append_child("Asset");
nlohmann::json asset;
asset.append_attribute("Type").set_value((int)pAsset->GetType());
asset.append_attribute("ID").set_value(pAsset->GetID());
asset.append_attribute("Location").set_value(pAsset->GetFileLocation().string().c_str());
asset["Type"] = (i32)pAsset->GetType();
asset["ID"] = pAsset->GetID();
if (Failed(pAsset->SaveToXML(asset).LogIfFailed(Editor::LogCat)))
// TODO: This needs to be a relative path! (Relative to the project root)
asset["Location"] = pAsset->GetFileLocation().string().c_str();
if (Failed(pAsset->SaveToJSON(asset).LogIfFailed(Editor::LogCat).LogIfFailed(Editor::LogCat)))
{
return OpRes::Fail("Could not save asset meta data for file: %s", pAsset->GetFileLocation().string().c_str());
}
c.push_back(asset);
}
if (!doc.save_file(mContentFile.string().c_str()))
std::ofstream ofs = std::ofstream(mContentFile.string().c_str());
if (!ofs.is_open())
{
return OpRes::Fail("ContentManager could not save file: %s", mContentFile.string().c_str());
return OpRes::Fail("ContentManager could not save file: %s - could not open file", mContentFile.string().c_str());
}
ofs << std::setw(4) << oj;
ofs.close();
return OpRes::OK();
}
@ -254,11 +284,13 @@ namespace lunarium { namespace editor
return OpRes::Fail("Could not copy asset file from: %s to: %s", file.string().c_str(), to_location.string().c_str());
}
EditorAsset* pAsset = CreateAsset(type);
pAsset->mAssetDir = mpProject->GetAssetDirectory();
pAsset->mID = mNextID;
mNextID++;
pAsset->mLocation = to_location;
pAsset->mLocation = mpProject->MakeRelativeToRoot(to_location);
if (Failed(pAsset->LoadRawFile().LogIfFailed(Editor::LogCat)))
{
delete pAsset;
@ -283,11 +315,14 @@ namespace lunarium { namespace editor
mAssets.erase(iter);
}
bool ContentManager::IsValidAsset(pugi::xml_node& node)
bool ContentManager::IsValidAsset(nlohmann::json& node)
{
if (!node.attribute("ID")) { return false; }
if (!node.attribute("Type")) { return false; }
if (!node.attribute("Location")) { return false; }
if (node.is_null()) { return false; }
if (node["ID"].is_null()) { return false; }
if (!node["ID"].is_number_unsigned()) { return false; }
if (node["Type"].is_null()) { return false; }
if (!node["Type"].is_number()) { return false; }
if (node["Location"].is_null()) { return false; }
return true;
}

@ -17,8 +17,7 @@
#include <utils/op_res.h>
#include <map>
#include <vector>
namespace pugi { class xml_node; }
#include <nlohmann/json.hpp>
namespace lunarium { namespace editor
{
@ -63,7 +62,7 @@ namespace lunarium { namespace editor
ContentManager& operator=(const ContentManager&) = delete;
private: // Helpers
[[nodiscard]] bool IsValidAsset(pugi::xml_node& node);
[[nodiscard]] bool IsValidAsset(nlohmann::json& node);
[[nodiscord]] EditorAsset* CreateAsset(AssetType type);
void FreeAssets();

@ -28,8 +28,8 @@ namespace lunarium { namespace editor
std::filesystem::path GetFileLocation();
[[nodiscard]] virtual OpRes LoadRawFile() = 0;
[[nodiscard]] virtual OpRes LoadFromXML(pugi::xml_node& node) = 0;
[[nodiscard]] virtual OpRes SaveToXML(pugi::xml_node& node) = 0;
[[nodiscard]] virtual OpRes LoadFromJSON(nlohmann::json& node) = 0;
[[nodiscard]] virtual OpRes SaveToJSON(nlohmann::json& node) = 0;
private:
friend class ContentManager;
@ -40,6 +40,8 @@ namespace lunarium { namespace editor
protected:
std::filesystem::path mAssetDir;
[[nodiscord]] virtual bool IsValidNode(nlohmann::json& node) = 0;
};
}}

@ -12,7 +12,7 @@
#include <graphics/graphics.h>
#include <assets/types/image.h>
#include <utils/logger.h>
#include <pugixml.hpp>
#include <nlohmann/json.hpp>
namespace lunarium { namespace editor
{
@ -37,7 +37,7 @@ namespace lunarium { namespace editor
return OpRes::OK();
}
OpRes TileSet::LoadFromXML(pugi::xml_node& node)
OpRes TileSet::LoadFromJSON(nlohmann::json& node)
{
// Load Image
mSetImage = FileLoaders::LoadImage(mAssetDir / GetFileLocation());
@ -46,26 +46,51 @@ namespace lunarium { namespace editor
return OpRes::Fail("Could not load image file: %s", GetFileLocation().string().c_str());
}
mTileSetID = node.attribute("TileSetID").as_int();
mTileSize.Width = node.attribute("TileSizeWidth").as_int();
mTileSize.Height = node.attribute("TileSizeHeight").as_int();
mNumTiles.Width = node.attribute("NumTilesCol").as_int();
mNumTiles.Height = node.attribute("NumTilesRow").as_int();
if (!IsValidNode(node))
{
return OpRes::Fail("Asset node is invalid");
}
mTileSetID = node["TileSetID"].get<int>();
mTileSize.Width = node["TileSizeWidth"].get<int>();
mTileSize.Height = node["TileSizeHeight"].get<int>();
mNumTiles.Width = node["NumTilesCol"].get<int>();
mNumTiles.Height = node["NumTilesRow"].get<int>();
return OpRes::OK();
}
OpRes TileSet::SaveToXML(pugi::xml_node& node)
OpRes TileSet::SaveToJSON(nlohmann::json& node)
{
node.append_attribute("TileSetID").set_value(mTileSetID);
node.append_attribute("TileSizeWidth").set_value(mTileSize.Width);
node.append_attribute("TileSizeHeight").set_value(mTileSize.Height);
node.append_attribute("NumTilesCol").set_value(mNumTiles.Width);
node.append_attribute("NumTilesRow").set_value(mNumTiles.Height);
node["TileSetID"] = mTileSetID;
node["TileSizeWidth"] = mTileSize.Width;
node["TileSizeHeight"] = mTileSize.Height;
node["NumTilesCol"] = mNumTiles.Width;
node["NumTilesRow"] = mNumTiles.Height;
return OpRes::OK();
}
bool TileSet::IsValidNode(nlohmann::json& node)
{
if (node["TileSetID"].is_null()) { return false; }
if (!node["TileSetID"].is_number()) { return false; }
if (node["TileSizeWidth"].is_null()) { return false; }
if (!node["TileSizeWidth"].is_number()) { return false; }
if (node["TileSizeHeight"].is_null()) { return false; }
if (!node["TileSizeHeight"].is_number()) { return false; }
if (node["NumTilesCol"].is_null()) { return false; }
if (!node["NumTilesCol"].is_number()) { return false; }
if (node["NumTilesRow"].is_null()) { return false; }
if (!node["NumTilesRow"].is_number()) { return false; }
return true;
}
void TileSet::SetTileSetID(int id)
{
mTileSetID = id;

@ -24,8 +24,8 @@ namespace lunarium { namespace editor
// Load the raw asset file from the internal location
OpRes LoadRawFile();
OpRes LoadFromXML(pugi::xml_node& node);
OpRes SaveToXML(pugi::xml_node& node);
OpRes LoadFromJSON(nlohmann::json& node);
OpRes SaveToJSON(nlohmann::json& node);
void SetTileSetID(int id);
int GetTileSetID() const;
@ -45,6 +45,10 @@ namespace lunarium { namespace editor
Sizei mNumTiles;
int mTileSetID;
private:
[[nodiscord]] virtual bool IsValidNode(nlohmann::json& node);
};
}}

@ -8,8 +8,10 @@
#include "project.h"
#include <editor/editor.h>
#include <utils/logger.h>
#include "contents/content_manager.h"
#include <pugixml.hpp>
#include <nlohmann/json.hpp>
#include <fstream>
namespace lunarium { namespace editor
{
@ -40,11 +42,17 @@ namespace lunarium { namespace editor
std::filesystem::path proj_doc = mLocation;
proj_doc /= std::filesystem::path(mName + ".lproj");
pugi::xml_document doc;
pugi::xml_node proj_node = doc.append_child("Lunarium_Project");
proj_node.append_attribute("name").set_value(mName.c_str());
//pugi::xml_node name_node = proj_node.append_child("Name");
doc.save_file(proj_doc.string().c_str());
nlohmann::json j;
auto& p = j["Project"];
p["Name"] = mName;
std::ofstream ofs = std::ofstream(proj_doc.c_str());
if (!ofs.is_open())
{
return OpRes::Fail("Failed to create project file");
}
ofs << std::setw(4) << j;
ofs.close();
std::filesystem::create_directory(mLocation / std::filesystem::path("engine"));
std::filesystem::create_directory(mLocation / std::filesystem::path("contents"));
@ -117,4 +125,52 @@ namespace lunarium { namespace editor
return GetContentDirectory() / "assets";
}
std::filesystem::path Project::MakeRelativeToRoot(std::filesystem::path abs_path) const
{
/* // HOW TO GET THE PATH RELATIVE TO THE PROJECT ROOT
std::filesystem::path p = "D:\\Projects\\lunarium\\test2\\contents\\assets\\LinkToThePast1_sized.png";
std::cout << "\nFull Path: " << p;
std::cout << "\nRelative path: " << p.relative_path();
std::filesystem::path proj = "D:\\Projects\\lunarium\\test2";
std::filesystem::path new_path = proj.filename();
for (auto iter = p.begin(); iter != p.end(); iter++)
{
std::cout << "\n" << (*iter);
if (proj.string().find((*iter).string()) == std::string::npos)
{
new_path /= (*iter);
}
}
std::cout << "\nNew Path: " << new_path; // new_path is what should be used as the location of the asset!
*/
// Validate the passed in path
if (!abs_path.is_absolute())
{
if (abs_path.filename() == std::filesystem::path(*mLocation.begin()))
{
// This path is already relative to the project root
return abs_path;
}
Logger::Warn(Editor::LogCat, "Relative path passed into Project::MakeRelativeToRoot: %s", abs_path.string().c_str());
return "";
}
std::filesystem::path new_path = mLocation.filename();
for (auto iter = abs_path.begin(); iter != abs_path.end(); iter++)
{
if (mLocation.string().find((*iter).string()) == std::string::npos)
{
new_path /= (*iter);
}
}
return new_path;
}
}}

@ -39,6 +39,9 @@ namespace lunarium { namespace editor
std::filesystem::path GetContentDirectory() const;
std::filesystem::path GetAssetDirectory() const;
// Convert an absolute path to a path relative to the project root
std::filesystem::path MakeRelativeToRoot(std::filesystem::path) const;
private:
bool mIsLoaded;
std::string mName;

@ -5,3 +5,20 @@
<Asset Type="1" ID="0" Location="D:\Projects\lunarium\build\Debug\test2\contents\assets\LinkToThePast1_sized.png" TileSizeWidth="16" TileSizeHeight="16" NumTilesCol="64" NumTilesRow="64" />
</Contents>
</Project>
{
"Project" : {
"Name" = "test2.lproj",
"NextID" = 1,
"Contents" : [
{
"Type" = 1,
"ID" = 0,
"Location" = "D:\Projects\lunarium\build\Debug\test2\contents\assets\LinkToThePast1_sized.png",
"TileSizeWidth" = 16,
"TileSizeHeight" = 16,
"NumTilesCol" = 64,
"NumTilesRow" = 64
},
]
}
}

@ -1,2 +1,6 @@
<?xml version="1.0"?>
<Lunarium_Project name="test2" />
{
"Project" : {
"Name" : "test2"
}
}

Loading…
Cancel
Save