From 7f871b04470766f0f5266cf949b65a54b7a6f79e Mon Sep 17 00:00:00 2001 From: rtk0c Date: Mon, 18 Apr 2022 20:59:31 -0700 Subject: Changeset: 10 Add Uid for IresObject --- source/CMakeLists.txt | 1 + source/EditorCore.cpp | 2 +- source/Ires.cpp | 131 ++++++++++++++++++++++++++++++++------------------ source/Ires.hpp | 23 +++++---- source/Sprite.cpp | 6 +++ source/Uid.cpp | 47 ++++++++++++++++++ source/Uid.hpp | 32 ++++++++++++ source/Utils.hpp | 6 +++ 8 files changed, 189 insertions(+), 59 deletions(-) create mode 100644 source/Uid.cpp create mode 100644 source/Uid.hpp (limited to 'source') diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt index ea0c4c4..f37d4a9 100644 --- a/source/CMakeLists.txt +++ b/source/CMakeLists.txt @@ -21,6 +21,7 @@ PRIVATE SmallVector.cpp Sprite.cpp Texture.cpp + Uid.cpp World.cpp ) diff --git a/source/EditorCore.cpp b/source/EditorCore.cpp index ec500e0..11eea8a 100644 --- a/source/EditorCore.cpp +++ b/source/EditorCore.cpp @@ -248,7 +248,7 @@ void EditorContentBrowser::Show(bool* open) { ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, { 0, 0 }); ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 0); ImGui::SetKeyboardFocusHere(); - if (ImGui::InputText("##Rename", &mInspector->renamingScratchBuffer, ImGuiInputTextFlags_EnterReturnsTrue)) { + if (ImGui::InputText("##Rename", &mInspector->renamingScratchBuffer, ImGuiInputTextFlags_AutoSelectAll | ImGuiInputTextFlags_EnterReturnsTrue)) { // Confirm ires->SetName(std::move(mInspector->renamingScratchBuffer)); mInspector->renaming = false; diff --git a/source/Ires.cpp b/source/Ires.cpp index 255c221..dde4ace 100644 --- a/source/Ires.cpp +++ b/source/Ires.cpp @@ -63,11 +63,19 @@ void IresObject::SetName(std::string name) { } void IresObject::ShowEditor(EditorInstance& editor) { + ImGui::Text("%s", ToString(mKind).data()); + bool isAnnoymous = mName.empty(); if (isAnnoymous) { - ImGui::Text("", (void*)this); + ImGui::Text("Name: ", (void*)this); + } else { + ImGui::Text("Name: %s", mName.c_str()); + } + + if (mUid.IsNull()) { + ImGui::TextUnformatted("Uid: "); } else { - ImGui::Text("%s", mName.c_str()); + ImGui::Text("Uid: %lx-%lx", mUid.upper, mUid.lower); } } @@ -76,6 +84,7 @@ void IresObject::WriteFull(IresObject* ires, rapidjson::Value& value, rapidjson: ires->Write(rvIres, root); value.AddMember("Type", rapidjson::StringRef(ToString(ires->GetKind())), root.GetAllocator()); + value.AddMember("Uid", ires->mUid.Write(root), root.GetAllocator()); value.AddMember("Value", rvIres, root.GetAllocator()); } @@ -86,6 +95,10 @@ std::unique_ptr IresObject::ReadFull(const rapidjson::Value& value) auto ires = Create(kind); if (!ires) return nullptr; + auto rvUid = rapidjson::GetProperty(value, rapidjson::kArrayType, "Uid"sv); + if (!rvUid) return nullptr; + ires->mUid.Read(*rvUid); + if (!ReadPartial(ires.get(), value)) { return nullptr; } @@ -100,6 +113,12 @@ bool IresObject::ReadPartial(IresObject* ires, const rapidjson::Value& value) { return true; } +void IresObject::Write(rapidjson::Value& value, rapidjson::Document& root) const { +} + +void IresObject::Read(const rapidjson::Value& value) { +} + void IresManager::DiscoverFilesDesignatedLocation() { auto path = AppConfig::assetDirPath / "Ires"; DiscoverFiles(path); @@ -116,39 +135,7 @@ void IresManager::DiscoverFiles(const fs::path& dir) { continue; } - auto file = Utils::OpenCstdioFile(item.path(), Utils::Read); - if (!file) continue; - DEFER { fclose(file); }; - - char readerBuffer[65536]; - rapidjson::FileReadStream stream(file, readerBuffer, sizeof(readerBuffer)); - - rapidjson::Document root; - root.ParseStream(stream); - - auto ires = IresObject::ReadFull(root); - if (!ires) { - continue; - } - - auto iden = fs::path(item.path()).replace_extension().lexically_relative(dir).string(); - std::replace(iden.begin(), iden.end(), '\\', '/'); - -#if 0 - std::string_view idenView(iden); - - // Trim heading slashes - while (idenView.front() == '/') { - idenView = std::string_view(idenView.data() + 1, idenView.size()); - } - // Trim trailing slashes - while (idenView.back() == '/') { - idenView = std::string_view(idenView.data(), idenView.size() - 1); - } -#endif - ires->mName = std::move(iden); - std::string_view key(ires->mName); - mObjects.try_emplace(key, ires.release()); + Load(item.path()); } } @@ -162,21 +149,73 @@ std::pair IresManager::Add(IresObject* ires) { snprintf(name.data(), size, "Unnamed %s #%d", IresObject::ToString(ires->GetKind()).data(), n); } - auto [iter, inserted] = mObjects.try_emplace(name, ires); + auto& uid = ires->mUid; + if (uid.IsNull()) { + uid = Uid::Create(); + } + + auto [iter, inserted] = mObjByUid.try_emplace(uid, ires); if (inserted) { ires->mMan = this; + // TODO handle full path return { ires, true }; } else { return { iter->second.Get(), false }; } } +IresObject* IresManager::Load(const fs::path& filePath) { + auto file = Utils::OpenCstdioFile(filePath, Utils::Read); + if (!file) return nullptr; + DEFER { fclose(file); }; + + char readerBuffer[65536]; + rapidjson::FileReadStream stream(file, readerBuffer, sizeof(readerBuffer)); + + rapidjson::Document root; + root.ParseStream(stream); + + auto ires = IresObject::ReadFull(root); + if (!ires) { + return nullptr; + } + + // Load uid should be handled by IresObject::ReadFull + assert(!ires->mUid.IsNull()); + // Load name from filename + ires->mName = filePath.filename().replace_extension().string(); + Add(ires.get()); + + // TODO subdirectory support +#if 0 + auto iden = fs::path(filePath).replace_extension().lexically_relative(dir).string(); + std::replace(iden.begin(), iden.end(), '\\', '/'); + + std::string_view idenView(iden); + + // Trim heading slashes + while (idenView.front() == '/') { + idenView = std::string_view(idenView.data() + 1, idenView.size()); + } + // Trim trailing slashes + while (idenView.back() == '/') { + idenView = std::string_view(idenView.data(), idenView.size() - 1); + } +#endif + + return ires.release(); +} + void IresManager::Delete(IresObject* ires) { // TODO } bool IresManager::Rename(IresObject* ires, std::string newName) { - if (mObjects.contains(newName)) { + ires->mName = std::move(newName); + + // TODO validate no name duplication +#if 0 + if (mObjByPath.contains(newName)) { return false; } @@ -184,20 +223,16 @@ bool IresManager::Rename(IresObject* ires, std::string newName) { RcPtr rc(ires); // Remove old entry (must do before replacing Material::mName, because the std::string_view in the map is a reference to it) - mObjects.erase(ires->GetName()); + mObjByPath.erase(ires->GetName()); // Add new entry ires->mName = std::move(newName); - mObjects.try_emplace(ires->GetName(), ires); - + // TODO handle full path + mObjByPath.try_emplace(ires->GetName(), ires); +#endif return true; } -IresObject* IresManager::Load(const fs::path& path) { - // TODO - return nullptr; -} - static fs::path GetDesignatedPath(IresObject* ires) { return AppConfig::assetDirPath / "Ires" / fs::path(ires->GetName()).replace_extension(".json"); } @@ -235,9 +270,9 @@ void IresManager::Save(IresObject* ires, const fs::path& filePath) { root.Accept(writer); } -IresObject* IresManager::FindIres(std::string_view path) { - auto iter = mObjects.find(path); - if (iter != mObjects.end()) { +IresObject* IresManager::FindIres(const Uid& uid) { + auto iter = mObjByUid.find(uid); + if (iter != mObjByUid.end()) { return iter->second.Get(); } else { return nullptr; diff --git a/source/Ires.hpp b/source/Ires.hpp index 9b055af..f821d87 100644 --- a/source/Ires.hpp +++ b/source/Ires.hpp @@ -2,6 +2,7 @@ #include "EditorAttachment.hpp" #include "RcPtr.hpp" +#include "Uid.hpp" #include "Utils.hpp" #include @@ -26,10 +27,11 @@ public: }; private: - std::string mName; - std::unique_ptr mEditorAttachment; - IresManager* mMan = nullptr; - Kind mKind; + std::string mName; // Serialized as filename + Uid mUid; // Serialized in full mode + std::unique_ptr mEditorAttachment; // Transient + IresManager* mMan = nullptr; // Transient + Kind mKind; // Serialized in full mode public: IresObject(Kind kind); @@ -44,6 +46,7 @@ public: bool IsAnnoymous() const; const std::string& GetName() const { return mName; } void SetName(std::string name); + const Uid& GetUid() const { return mUid; } virtual void ShowEditor(EditorInstance& editor); @@ -53,8 +56,8 @@ public: static void WriteFull(IresObject* ires, rapidjson::Value& value, rapidjson::Document& root); static std::unique_ptr ReadFull(const rapidjson::Value& value); static bool ReadPartial(IresObject* ires, const rapidjson::Value& value); - virtual void Write(rapidjson::Value& value, rapidjson::Document& root) const = 0; - virtual void Read(const rapidjson::Value& value) = 0; + virtual void Write(rapidjson::Value& value, rapidjson::Document& root) const; + virtual void Read(const rapidjson::Value& value); }; class IresManager { @@ -62,21 +65,21 @@ public: static inline IresManager* instance = nullptr; private: - robin_hood::unordered_map> mObjects; + robin_hood::unordered_map> mObjByUid; public: void DiscoverFilesDesignatedLocation(); void DiscoverFiles(const std::filesystem::path& dir); std::pair Add(IresObject* mat); + IresObject* Load(const std::filesystem::path& filePath); void Delete(IresObject* ires); bool Rename(IresObject* ires, std::string newName); - IresObject* Load(const std::filesystem::path& path); void Reload(IresObject* ires); void Save(IresObject* ires); void Save(IresObject* ires, const std::filesystem::path& filePath); - const auto& GetObjects() const { return mObjects; } - IresObject* FindIres(std::string_view path); + const auto& GetObjects() const { return mObjByUid; } + IresObject* FindIres(const Uid& uid); }; diff --git a/source/Sprite.cpp b/source/Sprite.cpp index 6cd575e..d539452 100644 --- a/source/Sprite.cpp +++ b/source/Sprite.cpp @@ -70,12 +70,15 @@ void IresSpriteFiles::InvalidateInstance() { } void IresSpriteFiles::Write(rapidjson::Value& value, rapidjson::Document& root) const { + IresObject::Write(value, root); value.AddMember("Sprites", rapidjson::WriteVectorPrimitives(root, spriteFiles.begin(), spriteFiles.end()), root.GetAllocator()); } void IresSpriteFiles::Read(const rapidjson::Value& value) { InvalidateInstance(); + IresObject::Read(value); + auto rvFileList = rapidjson::GetProperty(value, rapidjson::kArrayType, "Sprites"sv); if (!rvFileList) return; spriteFiles.clear(); @@ -259,6 +262,7 @@ void IresSpritesheet::ShowEditor(EditorInstance& editor) { } void IresSpritesheet::Write(rapidjson::Value& value, rapidjson::Document& root) const { + IresObject::Write(value, root); value.AddMember("SpriteSheet", spritesheetFile, root.GetAllocator()); value.AddMember("WSplit", sheetWSplit, root.GetAllocator()); value.AddMember("HSplit", sheetHSplit, root.GetAllocator()); @@ -269,6 +273,8 @@ void IresSpritesheet::Write(rapidjson::Value& value, rapidjson::Document& root) void IresSpritesheet::Read(const rapidjson::Value& value) { InvalidateInstance(); + + IresObject::Read(value); BRUSSEL_JSON_GET(value, "SpriteSheet", std::string, spritesheetFile, return ); BRUSSEL_JSON_GET(value, "WSplit", int, sheetWSplit, return ); BRUSSEL_JSON_GET(value, "HSplit", int, sheetHSplit, return ); diff --git a/source/Uid.cpp b/source/Uid.cpp new file mode 100644 index 0000000..2520d1e --- /dev/null +++ b/source/Uid.cpp @@ -0,0 +1,47 @@ +#include "Uid.hpp" + +#include "RapidJsonHelper.hpp" + +#include +#include + +Uid Uid::Create() { + std::random_device rd; + std::mt19937_64 gen(rd()); + std::uniform_int_distribution dist( + std::numeric_limits::min(), + std::numeric_limits::max()); + + Uid uid; + uid.upper = dist(gen); + uid.lower = dist(gen); + return uid; +} + +bool Uid::IsNull() const { + return upper == 0 && lower == 0; +} + +void Uid::Read(const rapidjson::Value& value) { + assert(value.IsArray()); + assert(value.Size() == 2); + auto& upper = value[0]; + assert(upper.IsUint64()); + auto& lower = value[1]; + assert(lower.IsUint64()); + + this->upper = upper.GetUint64(); + this->lower = lower.GetUint64(); +} + +void Uid::WriteInto(rapidjson::Value& value, rapidjson::Document& root) { + value.Reserve(2, root.GetAllocator()); + value.PushBack((uint64_t)upper, root.GetAllocator()); + value.PushBack((uint64_t)lower, root.GetAllocator()); +} + +rapidjson::Value Uid::Write(rapidjson::Document& root) { + rapidjson::Value result(rapidjson::kArrayType); + WriteInto(result, root); + return result; +} diff --git a/source/Uid.hpp b/source/Uid.hpp new file mode 100644 index 0000000..a076533 --- /dev/null +++ b/source/Uid.hpp @@ -0,0 +1,32 @@ +#pragma once + +#include "Utils.hpp" + +#include +#include +#include + +struct Uid { + uint64_t upper = 0; + uint64_t lower = 0; + + static Uid Create(); + + bool IsNull() const; + + void Read(const rapidjson::Value& value); + void WriteInto(rapidjson::Value& value, rapidjson::Document& root); + rapidjson::Value Write(rapidjson::Document& root); + + auto operator<=>(const Uid&) const = default; +}; + +template <> +struct std::hash { + size_t operator()(const Uid& uid) const { + size_t hash = 0; + Utils::HashCombine(hash, uid.upper); + Utils::HashCombine(hash, uid.lower); + return hash; + } +}; diff --git a/source/Utils.hpp b/source/Utils.hpp index 6239667..63e610f 100644 --- a/source/Utils.hpp +++ b/source/Utils.hpp @@ -23,8 +23,14 @@ constexpr float Abs(float v) noexcept { bool InRangeInclusive(int n, int lower, int upper); bool LineContains(glm::ivec2 p1, glm::ivec2 p2, glm::ivec2 candidate); + bool IsColinear(glm::ivec2 p1, glm::ivec2 p2); +template +void HashCombine(std::size_t& seed, const T& v) { + seed ^= std::hash{}(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2); +} + } // namespace Utils struct StringHash { -- cgit v1.2.3-70-g09d2