From 8510a85f79f706b93982b4e398b187b5f77081dd Mon Sep 17 00:00:00 2001 From: rtk0c Date: Fri, 3 Jun 2022 22:58:28 -0700 Subject: Changeset: 61 [BUGGED] Move object kind enums to use generated ToString and FromStrong The old mechanism rely on a specific prefix to Ires and GameObject string representations, but the generator currently leaves the enum value. Therefore all editor (e.g. drag & drop) and IO (e.g. ires loading) mechanisms are broken. --- source/10-common/LookupTable.hpp | 78 +++++++++++----------- source/20-codegen-compiler/CodegenUtils.cpp | 4 +- source/20-codegen-compiler/main.cpp | 34 +++++++++- .../test/examples/TestEnum.hpp.txt | 5 +- source/30-game/EditorAttachmentImpl.cpp | 4 +- source/30-game/EditorCorePrivate.cpp | 27 +++++--- source/30-game/GameObject.cpp | 44 +++++------- source/30-game/GameObject.hpp | 30 ++++++--- source/30-game/Ires.cpp | 48 +++++-------- source/30-game/Ires.hpp | 34 ++++++---- source/30-game/Level.cpp | 65 ++++++++++++++++-- source/30-game/Level.hpp | 27 +++++++- source/30-game/Material.cpp | 10 +-- 13 files changed, 256 insertions(+), 154 deletions(-) diff --git a/source/10-common/LookupTable.hpp b/source/10-common/LookupTable.hpp index 360cc2b..54548f2 100644 --- a/source/10-common/LookupTable.hpp +++ b/source/10-common/LookupTable.hpp @@ -5,60 +5,60 @@ // BIDI stands for bi-directional #define BIDI_LUT_DECL(name, aType, aCount, bType, bCount) \ - int gLut_##name##_A2B[aCount]; \ - int gLut_##name##_B2A[bCount]; \ + int gLutBidi_##name##_A2B[aCount]; \ + int gLutBidi_##name##_B2A[bCount]; \ using name##AType = aType; \ using name##BType = bType; \ - void InitializeLut##name() -#define BIDI_LUT_MAP_FOR(name) \ - int* lutMappingA2B = gLut_##name##_A2B; \ - int* lutMappingB2A = gLut_##name##_B2A + void InitializeLutBidi##name() +#define BIDI_LUT_MAP_FOR(name) \ + int* lutMappingA2B = gLutBidi_##name##_A2B; \ + int* lutMappingB2A = gLutBidi_##name##_B2A #define BIDI_LUT_MAP(from, to) \ lutMappingA2B[from] = to; \ lutMappingB2A[to] = from -#define BIDI_LUT_INIT(name) InitializeLut##name() -#define BIDI_LUT_A2B_LOOKUP(name, from) (name##BType)(gLut_##name##_A2B[from]) -#define BIDI_LUT_B2A_LOOKUP(name, to) (name##AType)(gLut_##name##_B2A[to]) +#define BIDI_LUT_INIT(name) InitializeLutBidi##name() +#define BIDI_LUT_A2B_LOOKUP(name, from) (name##BType)(gLutBidi_##name##_A2B[from]) +#define BIDI_LUT_B2A_LOOKUP(name, to) (name##AType)(gLutBidi_##name##_B2A[to]) // Forward string lookup -#define FSTR_LUT_DECL(name, enumMinValue, enumMaxValue) \ - constexpr int kLutMinVal_##name = enumMinValue; \ - const char* gLut_##name[(int)enumMaxValue - (int)enumMinValue]; \ - void InitializeLut##name() -#define FSTR_LUT_MAP_FOR(name) \ - const char** lutMapping = gLut_##name; \ - int lutMappingMinValue = kLutMinVal_##name +#define FSTR_LUT_DECL(name, enumMinValue, enumMaxValue) \ + constexpr int kLutFwMinVal_##name = enumMinValue; \ + const char* gLutFw_##name[(int)enumMaxValue - (int)enumMinValue]; \ + void InitializeLutFw##name() +#define FSTR_LUT_MAP_FOR(name) \ + const char** lutMapping = gLutFw_##name; \ + int lutMappingMinValue = kLutFwMinVal_##name #define FSTR_LUT_MAP(value, text) lutMapping[value - lutMappingMinValue] = text #define FSTR_LUT_MAP_ENUM(enumValue) FSTR_LUT_MAP(enumValue, #enumValue) -#define FSTR_LUT_LOOKUP(name, enumValue) gLut_##name[enumValue - kLutMinVal_##name] -#define FSTR_LUT_INIT(name) InitializeLut##name() +#define FSTR_LUT_LOOKUP(name, enumValue) gLutFw_##name[enumValue - kLutFwMinVal_##name] +#define FSTR_LUT_INIT(name) InitializeLutFw##name() // RSTR stands for reverse-string lookup -#define RSTR_LUT_DECL(name, enumMinValue, enumMaxValue) \ - robin_hood::unordered_flat_map gLut_##name; \ - void InitializeLut##name() -#define RSTR_LUT_MAP_FOR(name) auto& lutMapping = gLut_##name; +#define RSTR_LUT_DECL(name, enumMinValue, enumMaxValue) \ + robin_hood::unordered_flat_map gLutRv_##name; \ + void InitializeLutRv##name() +#define RSTR_LUT_MAP_FOR(name) auto& lutMapping = gLutRv_##name; #define RSTR_LUT_MAP(value, text) lutMapping.insert_or_assign(std::string_view(text), value); -#define RSTR_LUT(name) gLut_##name -#define BSTR_LUT_LOOKUP(name, string) gLut_##name.find(std::string_view(text))->second -#define RSTR_LUT_INIT(name) InitializeLut##name() +#define RSTR_LUT(name) gLutRv_##name +#define BSTR_LUT_LOOKUP(name, string) gLutRv_##name.find(std::string_view(text))->second +#define RSTR_LUT_INIT(name) InitializeLutRv##name() // BSTR stands for bi-directional string lookup -#define BSTR_LUT_DECL(name, enumMinValue, enumMaxValue) \ - constexpr int kLutMinVal_##name = enumMinValue; \ - const char* gLut_##name##_V2S[(int)enumMaxValue - (int)enumMinValue]; \ - robin_hood::unordered_flat_map gLut_##name##_S2V; \ - void InitializeLut##name() -#define BSTR_LUT_MAP_FOR(name) \ - const char** lutMappingV2S = gLut_##name##_V2S; \ - auto& lutMappingS2V = gLut_##name##_S2V; \ - int lutMappingMinValue = kLutMinVal_##name +#define BSTR_LUT_DECL(name, enumMinValue, enumMaxValue) \ + constexpr int kLutBstrMinVal_##name = enumMinValue; \ + const char* gLutBstr_##name##_V2S[(int)enumMaxValue - (int)enumMinValue]; \ + robin_hood::unordered_flat_map gLutBstr_##name##_S2V; \ + void InitializeLutBstr##name() +#define BSTR_LUT_MAP_FOR(name) \ + const char** lutMappingV2S = gLutBstr_##name##_V2S; \ + auto& lutMappingS2V = gLutBstr_##name##_S2V; \ + int lutMappingMinValue = kLutBstrMinVal_##name #define BSTR_LUT_MAP(value, text) \ lutMappingV2S[value - lutMappingMinValue] = text; \ lutMappingS2V.insert_or_assign(std::string_view(text), value); #define BSTR_LUT_MAP_ENUM(enumValue) BSTR_LUT_MAP(enumValue, #enumValue) -#define BSTR_LUT_V2S(name) gLut_##name##_V2S -#define BSTR_LUT_S2V(name) gLut_##name##_S2V -#define BSTR_LUT_V2S_LOOKUP(name, enumValue) gLut_##name##_V2S[enumValue - kLutMinVal_##name] -#define BSTR_LUT_S2V_LOOKUP(name, string) gLut_##name##_S2V.find(std::string_view(text))->second -#define BSTR_LUT_INIT(name) InitializeLut##name() +#define BSTR_LUT_V2S(name) gLutBstr_##name##_V2S +#define BSTR_LUT_S2V(name) gLutBstr_##name##_S2V +#define BSTR_LUT_V2S_LOOKUP(name, enumValue) gLutBstr_##name##_V2S[enumValue - kLutBstrMinVal_##name] +#define BSTR_LUT_S2V_LOOKUP(name, string) gLutBstr_##name##_S2V.find(std::string_view(text))->second +#define BSTR_LUT_INIT(name) InitializeLutBstr##name() diff --git a/source/20-codegen-compiler/CodegenUtils.cpp b/source/20-codegen-compiler/CodegenUtils.cpp index a43b72c..0c70cb6 100644 --- a/source/20-codegen-compiler/CodegenUtils.cpp +++ b/source/20-codegen-compiler/CodegenUtils.cpp @@ -120,10 +120,7 @@ void Utils::ProduceGeneratedHeader(const char* headerFilename, CodegenOutput& he headerOut.text += &R"""( // This file is generated. Any changes will be overidden when building. #pragma once - #include -#include - #include #include )"""[1]; @@ -132,6 +129,7 @@ void Utils::ProduceGeneratedHeader(const char* headerFilename, CodegenOutput& he APPEND_LIT_LN(sourceOut.text, "// This file is generated. Any changes will be overidden when building."); APPEND_FMT_LN(sourceOut.text, "#include \"%s\"", headerFilename); sourceOut.text += &R"""( +#include #include #include using namespace std::literals; diff --git a/source/20-codegen-compiler/main.cpp b/source/20-codegen-compiler/main.cpp index bb7c996..5e052a3 100644 --- a/source/20-codegen-compiler/main.cpp +++ b/source/20-codegen-compiler/main.cpp @@ -66,6 +66,18 @@ FSTR_LUT_DECL(ClexNames, CLEX_eof, CLEX_ext_COUNT) { FSTR_LUT_MAP_ENUM(CLEX_ext_dot_dot_dot); } +FSTR_LUT_DECL(EnumUnderlyingType, 0, EUT_COUNT) { + FSTR_LUT_MAP_FOR(EnumUnderlyingType); + FSTR_LUT_MAP(EUT_Int8, "int8_t"); + FSTR_LUT_MAP(EUT_Int16, "int16_t"); + FSTR_LUT_MAP(EUT_Int32, "int32_t"); + FSTR_LUT_MAP(EUT_Int64, "int64_t"); + FSTR_LUT_MAP(EUT_Uint8, "uint8_t"); + FSTR_LUT_MAP(EUT_Uint16, "uint16_t"); + FSTR_LUT_MAP(EUT_Uint32, "uint32_t"); + FSTR_LUT_MAP(EUT_Uint64, "uint64_t"); +} + RSTR_LUT_DECL(EnumUnderlyingType, 0, EUT_COUNT) { RSTR_LUT_MAP_FOR(EnumUnderlyingType); @@ -352,16 +364,25 @@ void GenerateForEnum(CodegenOutput& headerOut, CodegenOutput& sourceOut, const D int minVal = filteredElements.empty() ? 0 : filteredElements.front().value; int maxVal = filteredElements.empty() ? 0 : filteredElements.back().value; + CodegenOutputThing lookupFunctionDecl; + { + auto& o = lookupFunctionDecl.text; + APPEND_LIT_LN(o, "template <>"); + APPEND_FMT_LN(o, "std::string_view Metadata::EnumToString<%s>(%s value);", enumName, enumName); + } + CodegenOutputThing lookupFunctionDef; { auto& o = lookupFunctionDef.text; APPEND_LIT_LN(o, "template <>"); APPEND_FMT_LN(o, "std::string_view Metadata::EnumToString<%s>(%s value) {", enumName, enumName); - APPEND_FMT_LN(o, " if (value < %d || value > %d) return {};", minVal, maxVal); - APPEND_FMT_LN(o, " return %s[value - %d];", val2StrName, minVal); + APPEND_FMT_LN(o, " auto intVal = (%s)value;", FSTR_LUT_LOOKUP(EnumUnderlyingType, decl.underlyingType)); + APPEND_FMT_LN(o, " if (intVal < %d || intVal > %d) return {};", minVal, maxVal); + APPEND_FMT_LN(o, " return %s[intVal - %d];", val2StrName, minVal); APPEND_LIT_LN(o, "}"); } + headerOut.AddOutputThing(std::move(lookupFunctionDecl)); sourceOut.AddOutputThing(std::move(lookupFunctionDef)); } break; @@ -395,6 +416,13 @@ void GenerateForEnum(CodegenOutput& headerOut, CodegenOutput& sourceOut, const D } // Generate lookup function + CodegenOutputThing lookupFunctionDecl; + { + auto& o = lookupFunctionDecl.text; + APPEND_LIT_LN(o, "template <>"); + APPEND_FMT_LN(o, "std::optional<%s> Metadata::EnumFromString<%s>(std::string_view value);", enumName, enumName); + } + CodegenOutputThing lookupFunctionDef; { auto& o = lookupFunctionDef.text; @@ -410,6 +438,7 @@ void GenerateForEnum(CodegenOutput& headerOut, CodegenOutput& sourceOut, const D } sourceOut.AddOutputThing(std::move(lookupTable)); + headerOut.AddOutputThing(std::move(lookupFunctionDecl)); sourceOut.AddOutputThing(std::move(lookupFunctionDef)); } } @@ -1032,6 +1061,7 @@ InputOpcode ParseInputOpcode(std::string_view text) { int main(int argc, char* argv[]) { FSTR_LUT_INIT(ClexNames); + FSTR_LUT_INIT(EnumUnderlyingType); RSTR_LUT_INIT(EnumUnderlyingType); FSTR_LUT_INIT(EnumValuePattern); RSTR_LUT_INIT(CppKeyword); diff --git a/source/20-codegen-compiler/test/examples/TestEnum.hpp.txt b/source/20-codegen-compiler/test/examples/TestEnum.hpp.txt index 441d97c..30c36c0 100644 --- a/source/20-codegen-compiler/test/examples/TestEnum.hpp.txt +++ b/source/20-codegen-compiler/test/examples/TestEnum.hpp.txt @@ -5,7 +5,8 @@ enum MyEnum { }; BRUSSEL_ENUM(MyEnum, ToString FromString); -enum CountedEnumAll { +// Let's also test enum class +enum class CountedEnumAll { CEA_Foo, CEA_Bar, CEA_COUNT, @@ -21,7 +22,7 @@ enum CountedEnum { BRUSSEL_ENUM(CountedEnum, ToString FromString ExcludeHeuristics); namespace MyNamespace { -enum MyNamespacedEnum { +enum class MyNamespacedEnum { MNE_Foo, MNE_Bar, }; diff --git a/source/30-game/EditorAttachmentImpl.cpp b/source/30-game/EditorAttachmentImpl.cpp index 62d15eb..b09c133 100644 --- a/source/30-game/EditorAttachmentImpl.cpp +++ b/source/30-game/EditorAttachmentImpl.cpp @@ -1,6 +1,8 @@ #include "EditorAttachmentImpl.hpp" #include "EditorAttachment.hpp" +#include + EditorAttachment::EditorAttachment() { } @@ -15,7 +17,7 @@ std::unique_ptr EaGameObject::Create(GameObject* object) { default: result = new EaGameObject(); break; } - result->name = GameObject::ToString(kind); + result->name = Metadata::EnumToString(kind); result->eulerAnglesRotation = glm::eulerAngles(object->GetRotation()); return std::unique_ptr(result); } diff --git a/source/30-game/EditorCorePrivate.cpp b/source/30-game/EditorCorePrivate.cpp index 9fd6087..43857a8 100644 --- a/source/30-game/EditorCorePrivate.cpp +++ b/source/30-game/EditorCorePrivate.cpp @@ -8,13 +8,15 @@ #include "EditorNotification.hpp" #include "EditorUtils.hpp" #include "GameObject.hpp" -#include "Macros.hpp" #include "Mesh.hpp" #include "Player.hpp" #include "SceneThings.hpp" -#include "ScopeGuard.hpp" #include "VertexIndex.hpp" -#include "YCombinator.hpp" + +#include +#include +#include +#include #define GLFW_INCLUDE_NONE #include @@ -146,9 +148,9 @@ void EditorContentBrowser::Show(bool* open) { ImGui::OpenPopup("New Ires"); } if (ImGui::BeginPopup("New Ires")) { - for (int i = 0; i < IresObject::KD_COUNT; ++i) { + for (int i = 0; i < (int)IresObject::KD_COUNT; ++i) { auto kind = static_cast(i); - if (ImGui::MenuItem(IresObject::ToString(kind).data())) { + if (ImGui::MenuItem(Metadata::EnumToString(kind).data())) { auto ires = IresObject::Create(kind); auto [DISCARD, success] = IresManager::instance->Add(ires.get()); if (success) { @@ -228,7 +230,7 @@ void EditorContentBrowser::Show(bool* open) { } if (!mInspector->renaming) { if (ImGui::BeginDragDropSource()) { - auto kindName = IresObject::ToString(ires->GetKind()); + auto kindName = Metadata::EnumToString(ires->GetKind()); // Reason: intentionally using pointer as payload ImGui::SetDragDropPayload(kindName.data(), &ires, sizeof(ires)); // NOLINT(bugprone-sizeof-expression) ImGui::Text("%s '%s'", kindName.data(), name.c_str()); @@ -249,6 +251,11 @@ void EditorContentBrowser::Show(bool* open) { mInspector->renamingScratchBuffer = ldObj.name; } + if (ImGui::Button("Save", !isIttLevel)) { + auto ldObj = static_cast(origItPtr); + LevelManager::instance->SaveLevel(ldObj->level->GetUid()); + } + auto& objects = LevelManager::instance->mObjByUid; for (auto it = objects.begin(); it != objects.end(); ++it) { auto& uid = it->first; @@ -779,7 +786,9 @@ void EditorInstance::ShowInspector(LevelManager::LoadableObject* ldObj) { ImGui::InputText("Name", &ldObj->name); ImGui::InputTextMultiline("Desciption", &ldObj->description); - // TODO level object explorer + if (ImGui::CollapsingHeader("Instanciation Entries")) { + ldObj->level->ShowInstanciationEntries(*this); + } } void EditorInstance::ShowInspector(GameObject* object) { @@ -915,7 +924,7 @@ void EditorInstance::ShowInspector(GameObject* object) { ImGui::SameLine(); IresObject::ShowReferenceSafe(*this, ea->confSprite.Get()); if (ImGui::BeginDragDropTarget()) { - if (auto payload = ImGui::AcceptDragDropPayload(IresObject::ToString(IresObject::KD_Spritesheet).data())) { + if (auto payload = ImGui::AcceptDragDropPayload(Metadata::EnumToString(IresObject::KD_Spritesheet).data())) { auto spritesheet = *static_cast(payload->Data); ea->confSprite.Attach(spritesheet); auto def = spritesheet->GetInstance(); @@ -929,7 +938,7 @@ void EditorInstance::ShowInspector(GameObject* object) { ImGui::SameLine(); IresObject::ShowReferenceSafe(*this, ea->confMaterial.Get()); if (ImGui::BeginDragDropTarget()) { - if (auto payload = ImGui::AcceptDragDropPayload(IresObject::ToString(IresObject::KD_Material).data())) { + if (auto payload = ImGui::AcceptDragDropPayload(Metadata::EnumToString(IresObject::KD_Material).data())) { auto material = *static_cast(payload->Data); ea->confMaterial.Attach(material); player->SetMaterial(material->GetInstance()); diff --git a/source/30-game/GameObject.cpp b/source/30-game/GameObject.cpp index 8bb3ec7..3b15111 100644 --- a/source/30-game/GameObject.cpp +++ b/source/30-game/GameObject.cpp @@ -2,11 +2,14 @@ #include "Level.hpp" #include "Player.hpp" -#include "RapidJsonHelper.hpp" #include "SceneThings.hpp" #include "World.hpp" +#include +#include + #include +#include #include #include @@ -14,11 +17,12 @@ using namespace std::literals; namespace ProjectBrussel_UNITY_ID { GameObject* CreateGameObject(GameObject::Kind kind, GameWorld* world) { + using enum Tags::GameObjectKind; switch (kind) { - case GameObject::KD_Generic: return new GameObject(world); - case GameObject::KD_SimpleGeometry: return new SimpleGeometryObject(world); - case GameObject::KD_Building: return new BuildingObject(world); - case GameObject::KD_LevelWrapper: return new LevelWrapperObject(world); + case KD_Generic: return new GameObject(world); + case KD_SimpleGeometry: return new SimpleGeometryObject(world); + case KD_Building: return new BuildingObject(world); + case KD_LevelWrapper: return new LevelWrapperObject(world); default: break; } return nullptr; @@ -60,27 +64,6 @@ GameObject::~GameObject() { } } -std::string_view GameObject::ToString(Kind kind) { - switch (kind) { - case KD_Generic: return "GameObject"sv; - case KD_Player: return "Player"sv; - case KD_SimpleGeometry: return "SimpleGeometry"sv; - case KD_Building: return "Building"sv; - case KD_LevelWrapper: return "LevelWrapper"sv; - case KD_COUNT: break; - } - return std::string_view(); -} - -GameObject::Kind GameObject::FromString(std::string_view name) { - if (name == "GameObject"sv) return KD_Generic; - if (name == "Player"sv) return KD_Player; - if (name == "SimpleGeometry"sv) return KD_SimpleGeometry; - if (name == "Building"sv) return KD_Building; - if (name == "LevelWrapper"sv) return KD_LevelWrapper; - return KD_COUNT; -} - GameObject::Kind GameObject::GetKind() const { return mKind; } @@ -204,7 +187,7 @@ void GameObject::Update() { rapidjson::Value GameObject::Serialize(GameObject* obj, rapidjson::Document& root) { rapidjson::Value result(rapidjson::kObjectType); - result.AddMember("Type", rapidjson::StringRef(ToString(obj->GetKind())), root.GetAllocator()); + result.AddMember("Type", rapidjson::StringRef(Metadata::EnumToString(obj->GetKind())), root.GetAllocator()); rapidjson::Value rvValue(rapidjson::kObjectType); obj->WriteSaveFormat(rvValue, root); @@ -222,8 +205,9 @@ std::unique_ptr GameObject::Deserialize(const rapidjson::Value& valu auto rvValue = rapidjson::GetProperty(value, rapidjson::kObjectType, "Value"sv); if (!rvValue) return nullptr; - auto kind = FromString(rapidjson::AsStringView(*rvType)); - auto obj = std::unique_ptr(CreateGameObject(kind, world)); + auto kind = Metadata::EnumFromString(rapidjson::AsStringView(*rvType)); + assert(kind.has_value()); + auto obj = std::unique_ptr(CreateGameObject(kind.value(), world)); if (!obj) return nullptr; obj->ReadSaveFormat(*rvValue); @@ -242,3 +226,5 @@ void GameObject::SetParent(GameObject* parent) { // needUpdate(); } } + +#include diff --git a/source/30-game/GameObject.hpp b/source/30-game/GameObject.hpp index 77488b9..f975803 100644 --- a/source/30-game/GameObject.hpp +++ b/source/30-game/GameObject.hpp @@ -2,27 +2,35 @@ #include "EditorAttachment.hpp" #include "Material.hpp" -#include "PodVector.hpp" #include "Renderer.hpp" #include "VertexIndex.hpp" +#include +#include + #include #include #include #include #include +namespace Tags { +enum class GameObjectKind { + KD_Generic, + KD_Player, + KD_SimpleGeometry, + KD_Building, + KD_LevelWrapper, + KD_COUNT, +}; +BRUSSEL_ENUM(GameObjectKind, ToString FromString ExcludeHeuristics); +} // namespace Tags + class GameWorld; class GameObject { public: - enum Kind { - KD_Generic, - KD_Player, - KD_SimpleGeometry, - KD_Building, - KD_LevelWrapper, - KD_COUNT, - }; + using Kind = Tags::GameObjectKind; + using enum Tags::GameObjectKind; private: std::unique_ptr mEditorAttachment; @@ -50,8 +58,6 @@ public: GameObject(GameObject&&) = default; GameObject& operator=(GameObject&&) = default; - static std::string_view ToString(Kind kind); - static Kind FromString(std::string_view name); Kind GetKind() const; GameWorld* GetWorld() const; @@ -92,3 +98,5 @@ public: protected: void SetParent(GameObject* parent); }; + +#include diff --git a/source/30-game/Ires.cpp b/source/30-game/Ires.cpp index 10a6867..bfa4cdf 100644 --- a/source/30-game/Ires.cpp +++ b/source/30-game/Ires.cpp @@ -3,14 +3,16 @@ #include "AppConfig.hpp" #include "EditorCore.hpp" #include "EditorUtils.hpp" -#include "Macros.hpp" #include "Material.hpp" -#include "RapidJsonHelper.hpp" -#include "ScopeGuard.hpp" #include "Shader.hpp" #include "Sprite.hpp" #include "Texture.hpp" -#include "Utils.hpp" + +#include +#include +#include +#include +#include #include #include @@ -29,27 +31,6 @@ IresObject::IresObject(Kind kind) : mKind{ kind } { } -std::string_view IresObject::ToString(Kind kind) { - switch (kind) { - case KD_Texture: return BRUSSEL_TAG_PREFIX_Ires "Texture"sv; - case KD_Shader: return BRUSSEL_TAG_PREFIX_Ires "Shader"sv; - case KD_Material: return BRUSSEL_TAG_PREFIX_Ires "Material"sv; - case KD_SpriteFiles: return BRUSSEL_TAG_PREFIX_Ires "SpriteFiles"sv; - case KD_Spritesheet: return BRUSSEL_TAG_PREFIX_Ires "Spritesheet"sv; - case KD_COUNT: break; - } - return std::string_view(); -} - -IresObject::Kind IresObject::FromString(std::string_view name) { - if (name == BRUSSEL_TAG_PREFIX_Ires "Texture"sv) return KD_Texture; - if (name == BRUSSEL_TAG_PREFIX_Ires "Shader"sv) return KD_Shader; - if (name == BRUSSEL_TAG_PREFIX_Ires "Material"sv) return KD_Material; - if (name == BRUSSEL_TAG_PREFIX_Ires "SpriteFiles"sv) return KD_SpriteFiles; - if (name == BRUSSEL_TAG_PREFIX_Ires "Spritesheet"sv) return KD_Spritesheet; - return KD_COUNT; -} - std::unique_ptr IresObject::Create(Kind kind) { switch (kind) { case KD_Texture: return std::make_unique(); @@ -128,7 +109,7 @@ void IresObject::ShowReference(IEditor& editor) { } void IresObject::ShowEditor(IEditor& editor) { - ImGui::Text("%s", ToString(mKind).data()); + ImGui::Text("%.*s", PRINTF_STRING_VIEW(Metadata::EnumToString(mKind))); bool isAnnoymous = mName.empty(); if (isAnnoymous) { @@ -148,7 +129,7 @@ void IresObject::WriteFull(IresWritingContext& ctx, IresObject* ires, rapidjson: rapidjson::Value rvIres(rapidjson::kObjectType); ires->Write(ctx, rvIres, root); - value.AddMember("Type", rapidjson::StringRef(ToString(ires->GetKind())), root.GetAllocator()); + value.AddMember("Type", rapidjson::StringRef(Metadata::EnumToString(ires->GetKind())), root.GetAllocator()); value.AddMember("Uid", ires->mUid.Write(root), root.GetAllocator()); value.AddMember("Value", rvIres, root.GetAllocator()); } @@ -169,8 +150,9 @@ std::unique_ptr IresObject::ReadFull(IresLoadingContext& ctx, const std::unique_ptr IresObject::ReadBasic(const rapidjson::Value& value) { auto rvType = rapidjson::GetProperty(value, rapidjson::kStringType, "Type"sv); if (!rvType) return nullptr; - auto kind = FromString(rapidjson::AsStringView(*rvType)); - auto ires = Create(kind); + auto kind = Metadata::EnumFromString(rapidjson::AsStringView(*rvType)); + assert(kind.has_value()); + auto ires = Create(kind.value()); if (!ires) return nullptr; auto rvUid = rapidjson::GetProperty(value, rapidjson::kArrayType, "Uid"sv); @@ -212,7 +194,7 @@ void IresManager::DiscoverFiles(const fs::path& dir) { std::vector> candidatesByKind; IresLoadTimeContext() { - candidatesByKind.resize(IresObject::KD_COUNT); + candidatesByKind.resize((int)IresObject::KD_COUNT); } std::vector& GetByKind(IresObject::Kind kind) { @@ -282,7 +264,7 @@ void IresManager::DiscoverFiles(const fs::path& dir) { // Load Ires in order by type, there are dependencies between them // TODO full arbitary dependency between Ires - for (int i = 0; i < IresObject::KD_COUNT; ++i) { + for (int i = 0; i < (int)IresObject::KD_COUNT; ++i) { auto kind = static_cast(i); auto& list = ctx.GetByKind(kind); for (auto cand : list) { @@ -302,7 +284,7 @@ void IresManager::DiscoverFiles(const fs::path& dir) { std::pair IresManager::Add(IresObject* ires) { auto& name = ires->mName; if (name.empty()) { - name = Utils::MakeRandomNumberedName(IresObject::ToString(ires->GetKind()).data()); + name = Utils::MakeRandomNumberedName(Metadata::EnumToString(ires->GetKind()).data()); } auto& uid = ires->mUid; @@ -423,3 +405,5 @@ IresObject* IresManager::FindIres(const Uid& uid) const { return nullptr; } } + +#include diff --git a/source/30-game/Ires.hpp b/source/30-game/Ires.hpp index 83ca175..b6420f3 100644 --- a/source/30-game/Ires.hpp +++ b/source/30-game/Ires.hpp @@ -2,9 +2,11 @@ #include "EditorAttachment.hpp" #include "EditorCore.hpp" -#include "RcPtr.hpp" -#include "Uid.hpp" -#include "Utils.hpp" + +#include +#include +#include +#include #include #include @@ -17,18 +19,24 @@ class IresManager; class IresWritingContext; class IresLoadingContext; +namespace Tags { +enum class IresObjectKind { + KD_Texture, + KD_Shader, + KD_Material, + KD_SpriteFiles, + KD_Spritesheet, + KD_COUNT, +}; +BRUSSEL_ENUM(IresObjectKind, ToString FromString ExcludeHeuristics); +} // namespace Tags + class IresObject : public RefCounted { friend class IresManager; public: - enum Kind { - KD_Texture, - KD_Shader, - KD_Material, - KD_SpriteFiles, - KD_Spritesheet, - KD_COUNT, - }; + using Kind = Tags::IresObjectKind; + using enum Tags::IresObjectKind; private: std::string mName; // Serialized as filename @@ -41,8 +49,6 @@ public: IresObject(Kind kind); virtual ~IresObject() = default; - static std::string_view ToString(Kind kind); - static Kind FromString(std::string_view name); static std::unique_ptr Create(Kind kind); Kind GetKind() const { return mKind; } @@ -115,3 +121,5 @@ public: const auto& GetObjects() const { return mObjByUid; } virtual IresObject* FindIres(const Uid& uid) const override; }; + +#include diff --git a/source/30-game/Level.cpp b/source/30-game/Level.cpp index 5881084..076e5d5 100644 --- a/source/30-game/Level.cpp +++ b/source/30-game/Level.cpp @@ -1,21 +1,24 @@ #include "Level.hpp" #include "AppConfig.hpp" -#include "PodVector.hpp" -#include "RapidJsonHelper.hpp" -#include "ScopeGuard.hpp" -#include "Utils.hpp" +#include +#include +#include +#include + +#include #include #include #include -#include +#include #include #include using namespace std::literals; namespace fs = std::filesystem; +constexpr auto kParentToRootObject = std::numeric_limits::max(); constexpr auto kInvalidEntryId = std::numeric_limits::max(); struct Level::InstanciationEntry { @@ -29,6 +32,23 @@ Level::Level() = default; Level::~Level() = default; void Level::Instanciate(GameObject* relRoot) const { + auto objectsLut = std::make_unique(mEntries.size()); + for (auto& entry : mEntries) { + GameObject* parent; + if (entry.parentId == kParentToRootObject) { + parent = relRoot; + } else { + parent = objectsLut[entry.parentId]; + } + + // TODO deser object + } +} + +void Level::ShowInstanciationEntries(IEditor& editor) { + for (auto& entry : mEntries) { + // TODO + } } void LevelManager::DiscoverFilesDesignatedLocation() { @@ -92,6 +112,7 @@ Level* LevelManager::LoadLevel(const Uid& uid) { ldObj.level.Attach(level = new Level()); level->mMan = this; + level->mUid = uid; #if defined(BRUSSEL_DEV_ENV) BRUSSEL_JSON_GET_DEFAULT(root, "Name", std::string, ldObj.name, BRUSSEL_DEF_LEVEL_NAME); @@ -123,8 +144,9 @@ void LevelManager::PrepareLevel(const Uid& uid) { } LevelManager::LoadableObject& LevelManager::AddLevel(const Uid& uid) { - auto&& [iter, inserted] = mObjByUid.try_emplace(uid, LoadableObject{}); + auto&& [iter, inserted] = mObjByUid.try_emplace(uid); auto& ldObj = iter->second; + ldObj.level->mUid = uid; #if defined(BRUSSEL_DEV_ENV) ldObj.name = BRUSSEL_DEF_LEVEL_NAME; ldObj.description = BRUSSEL_DEF_LEVEL_DESC; @@ -132,6 +154,37 @@ LevelManager::LoadableObject& LevelManager::AddLevel(const Uid& uid) { return ldObj; } +void LevelManager::SaveLevel(const Uid& uid) const { + auto iter = mObjByUid.find(uid); + if (iter == mObjByUid.end()) return; + auto& obj = iter->second; + + SaveLevelImpl(obj, obj.filePath); +} + +void LevelManager::SaveLevel(const Uid& uid, const std::filesystem::path& path) const { + auto iter = mObjByUid.find(uid); + if (iter == mObjByUid.end()) return; + auto& obj = iter->second; + + SaveLevelImpl(obj, path); +} + +void LevelManager::SaveLevelImpl(const LoadableObject& obj, const std::filesystem::path& path) const { + rapidjson::Document root; + + // TODO + + auto file = Utils::OpenCstdioFile(path, Utils::WriteTruncate); + if (!file) return; + DEFER { fclose(file); }; + + char writerBuffer[65536]; + rapidjson::FileWriteStream stream(file, writerBuffer, sizeof(writerBuffer)); + rapidjson::Writer writer(stream); + root.Accept(writer); +} + LevelWrapperObject::LevelWrapperObject(GameWorld* world) : GameObject(KD_LevelWrapper, world) // { diff --git a/source/30-game/Level.hpp b/source/30-game/Level.hpp index c1170a3..9114a64 100644 --- a/source/30-game/Level.hpp +++ b/source/30-game/Level.hpp @@ -1,8 +1,10 @@ #pragma once +#include "EditorCore.hpp" #include "GameObject.hpp" -#include "RcPtr.hpp" -#include "Uid.hpp" + +#include +#include #include #include @@ -20,6 +22,7 @@ private: struct InstanciationEntry; LevelManager* mMan; + Uid mUid; std::vector mEntries; public: @@ -27,6 +30,12 @@ public: ~Level(); void Instanciate(GameObject* relRoot) const; + + LevelManager* GetLinkedLevelManager() const { return mMan; } + const Uid& GetUid() const { return mUid; } + + // Editor stuff + void ShowInstanciationEntries(IEditor& editor); }; class LevelManager { @@ -34,12 +43,16 @@ public: static inline LevelManager* instance = nullptr; public: // NOTE: public for the editor; actual game components should not modify the map using this + // TODO maybe cut this struct to only the first RcPtr field in release mode? struct LoadableObject { RcPtr level; // TODO make weak pointer std::filesystem::path filePath; // NOTE: these fields are only loaded in dev mode std::string name; std::string description; + + // Editor book keeping fields + bool edited = false; }; // We want pointer stability here for the editor (inspector object) robin_hood::unordered_node_map mObjByUid; @@ -54,8 +67,16 @@ public: /// Send the given level to be loaded on another thread void PrepareLevel(const Uid& uid); - // These should only be used by the editor + /// Create and add a new level object with the given uid. + /// Should only be used by the editor. LoadableObject& AddLevel(const Uid& uid); + /// Should only be used by the editor. + void SaveLevel(const Uid& uid) const; + /// Should only be used by the editor. + void SaveLevel(const Uid& uid, const std::filesystem::path& path) const; + +private: + void SaveLevelImpl(const LoadableObject& obj, const std::filesystem::path& path) const; }; class LevelWrapperObject : public GameObject { diff --git a/source/30-game/Material.cpp b/source/30-game/Material.cpp index e648970..9b0c42d 100644 --- a/source/30-game/Material.cpp +++ b/source/30-game/Material.cpp @@ -3,9 +3,11 @@ #include "AppConfig.hpp" #include "EditorCore.hpp" #include "EditorUtils.hpp" -#include "RapidJsonHelper.hpp" -#include "ScopeGuard.hpp" -#include "Utils.hpp" + +#include +#include +#include +#include #include #include @@ -349,7 +351,7 @@ void IresMaterial::ShowEditor(IEditor& editor) { IresObject::ShowReferenceNull(editor); } if (ImGui::BeginDragDropTarget()) { - if (auto payload = ImGui::AcceptDragDropPayload(ToString(KD_Shader).data())) { + if (auto payload = ImGui::AcceptDragDropPayload(Metadata::EnumToString(KD_Shader).data())) { auto shader = *static_cast(payload->Data); mInstance->SetShader(shader->GetInstance()); } -- cgit v1.2.3-70-g09d2