From 3d82b12cbe25289fc53bc6b44f76cefd0af70e78 Mon Sep 17 00:00:00 2001 From: Thraix Date: Tue, 18 Jul 2023 14:03:20 +0200 Subject: [PATCH] Cleanup Scene file - Introduce new class ComponentHandler which handles all the Serializing/Deserializing/Editor Gui of Components - Currently only Deserializing and Editor Gui is implemented for existing components --- CopiumEngine/CopiumEngine.vcxproj | 4 + CopiumEngine/CopiumEngine.vcxproj.filters | 12 + CopiumEngine/res/scenes/scene.meta | 6 +- .../src/copium/buffer/IndexBuffer.cpp | 2 +- CopiumEngine/src/copium/core/Scene.cpp | 385 ++---------------- CopiumEngine/src/copium/core/Scene.h | 46 +-- .../src/copium/example/ComponentHandler.h | 92 +++++ .../copium/example/ComponentHandlerBase.cpp | 50 +++ .../src/copium/example/ComponentHandlerBase.h | 32 ++ .../src/copium/example/ComponentHandlers.h | 358 ++++++++++++++++ 10 files changed, 599 insertions(+), 388 deletions(-) create mode 100644 CopiumEngine/src/copium/example/ComponentHandler.h create mode 100644 CopiumEngine/src/copium/example/ComponentHandlerBase.cpp create mode 100644 CopiumEngine/src/copium/example/ComponentHandlerBase.h create mode 100644 CopiumEngine/src/copium/example/ComponentHandlers.h diff --git a/CopiumEngine/CopiumEngine.vcxproj b/CopiumEngine/CopiumEngine.vcxproj index d13b131..0da3fe1 100644 --- a/CopiumEngine/CopiumEngine.vcxproj +++ b/CopiumEngine/CopiumEngine.vcxproj @@ -201,6 +201,7 @@ + @@ -262,6 +263,9 @@ + + + diff --git a/CopiumEngine/CopiumEngine.vcxproj.filters b/CopiumEngine/CopiumEngine.vcxproj.filters index cf27edc..e63805f 100644 --- a/CopiumEngine/CopiumEngine.vcxproj.filters +++ b/CopiumEngine/CopiumEngine.vcxproj.filters @@ -228,6 +228,9 @@ Source Files + + Source Files + @@ -536,5 +539,14 @@ Header Files + + Header Files + + + Header Files + + + Header Files + \ No newline at end of file diff --git a/CopiumEngine/res/scenes/scene.meta b/CopiumEngine/res/scenes/scene.meta index ee75eab..4e97510 100644 --- a/CopiumEngine/res/scenes/scene.meta +++ b/CopiumEngine/res/scenes/scene.meta @@ -8,7 +8,7 @@ name=Level Generator name=Block [Transform] -position=2 -8.5 +position=0 -8.5 size=1 1 [Texture] @@ -49,7 +49,7 @@ size=0.2 0.2 [Texture] texture-uuid=f49a5284-d666-0982-95ca-cf68cc3d4f45 -[MouseFollow] +[MouseFollower] [Renderable] --- @@ -65,7 +65,7 @@ font=3e9ef1a2-59ba-2e4d-b2cd-f5efaa531af7 text=0 fps font-size=20 -[FrameCount] +[FrameCounter] [UiRenderable] --- diff --git a/CopiumEngine/src/copium/buffer/IndexBuffer.cpp b/CopiumEngine/src/copium/buffer/IndexBuffer.cpp index 8038471..e65214e 100644 --- a/CopiumEngine/src/copium/buffer/IndexBuffer.cpp +++ b/CopiumEngine/src/copium/buffer/IndexBuffer.cpp @@ -21,7 +21,7 @@ namespace Copium void IndexBuffer::Draw(const CommandBuffer& commandBuffer, int indices) { - CP_ASSERT(indices > 0 && indices <= indexCount, "amount of indices is out of range"); + CP_ASSERT(indices >= 0 && indices <= indexCount, "amount of indices is out of range"); vkCmdDrawIndexed(commandBuffer, indices, 1, 0, 0, 0); } } \ No newline at end of file diff --git a/CopiumEngine/src/copium/core/Scene.cpp b/CopiumEngine/src/copium/core/Scene.cpp index 95776b6..f9b25fa 100644 --- a/CopiumEngine/src/copium/core/Scene.cpp +++ b/CopiumEngine/src/copium/core/Scene.cpp @@ -11,6 +11,7 @@ #include "copium/example/CameraFollowPlayerSystem.h" #include "copium/example/CameraUpdateSystem.h" #include "copium/example/ColliderSystem.h" +#include "copium/example/ComponentHandlers.h" #include "copium/example/Components.h" #include "copium/example/DebugSystem.h" #include "copium/example/FrameCountSystem.h" @@ -35,6 +36,7 @@ namespace Copium { Scene::Scene(CommandBuffer& commandBuffer, DescriptorPool& descriptorPool) + : viewport{-1, -1, 1, 1} { fileIcon = AssetManager::LoadAsset("fileicon.meta"); descriptorSetFileIcon = Vulkan::GetImGuiInstance().CreateDescriptorSet(); @@ -62,6 +64,26 @@ namespace Copium ecs->SetComponentListener(); ecs->SetComponentListener(); + RegisterComponentHandler(); + RegisterComponentHandler(); + RegisterComponentHandler(); + RegisterComponentHandler(); + RegisterComponentHandler(); + RegisterComponentHandler(); + RegisterComponentHandler(); + RegisterComponentHandler(&viewport); + RegisterComponentHandler(); + RegisterComponentHandler(); + RegisterComponentHandler(); + RegisterComponentHandler(); + RegisterComponentHandler(); + RegisterFlagComponentHandler("Mouse Follower"); + RegisterFlagComponentHandler("Renderable"); + RegisterFlagComponentHandler("Ui Renderable"); + RegisterFlagComponentHandler("Pickup"); + RegisterFlagComponentHandler("Level Generator"); + RegisterFlagComponentHandler("Frame Counter"); + Deserialize("res/scenes/scene.meta"); } @@ -95,8 +117,6 @@ namespace Copium void Scene::Deserialize(const std::string& file) { - float aspect = Vulkan::GetSwapChain().GetExtent().width / (float)Vulkan::GetSwapChain().GetExtent().height; - std::vector metaFiles = MetaFile::ReadList(file); for (auto& metaFile : metaFiles) @@ -107,115 +127,17 @@ namespace Copium { try { - char* endPtr; - if (name == "Transform") + bool found = false; + for (auto& componentHandler : componentHandlers) { - TransformC transform; - transform.position = ReadVec2Opt(metaClass, "position", glm::vec2{0.0f, 0.0f}); - transform.size = ReadVec2Opt(metaClass, "size", glm::vec2{1.0f, 1.0f}); - entity.AddComponent(transform); + if (name == componentHandler->GetSerializedName()) + { + componentHandler->Deserialize(entity, metaClass); + found = true; + break; + } } - else if (name == "Texture") - { - TextureC texture; - texture.asset = AssetRef{AssetManager::LoadAsset(Uuid{metaClass.GetValue("texture-uuid")})}; - texture.texCoord1 = ReadVec2Opt(metaClass, "tex-coord", glm::vec2{0.0f, 0.0f}); - texture.texCoord2 = texture.texCoord1 + ReadVec2Opt(metaClass, "tex-size", glm::vec2{1.0f, 1.0f} - texture.texCoord1); - entity.AddComponent(texture); - } - else if (name == "StaticCollider") - { - StaticColliderC staticCollider; - staticCollider.resolveCollision = ReadBoolOpt(metaClass, "resolve-collision", true); - entity.AddComponent(staticCollider); - } - else if (name == "DynamicCollider") - { - DynamicColliderC dynamicCollider; - dynamicCollider.resolveCollision = ReadBoolOpt(metaClass, "resolve-collision", true); - dynamicCollider.colliderOffset = ReadVec2Opt(metaClass, "collider-offset", glm::vec2{0.0f, 0.0f}); - dynamicCollider.colliderSize = ReadVec2Opt(metaClass, "collider-size", glm::vec2{1.0f, 1.0f}); - entity.AddComponent(dynamicCollider); - } - else if (name == "Text") - { - TextC text; - text.font = AssetRef{AssetManager::LoadAsset(Uuid{metaClass.GetValue("font")})}; - text.text = metaClass.GetValue("text"); - text.fontSize = std::strtof(metaClass.GetValue("font-size").c_str(), &endPtr); - entity.AddComponent(text); - } - else if (name == "Camera") - { - CameraC camera; - camera.staticBoundingBox = ReadBoolOpt(metaClass, "static-bounding-box", false); - camera.uiCamera = ReadBoolOpt(metaClass, "ui-camera", false); - if (camera.uiCamera) - camera.projection = BoundingBox(0, 0, Vulkan::GetSwapChain().GetExtent().width, Vulkan::GetSwapChain().GetExtent().height); - else - camera.projection = BoundingBox(-aspect, -1.0f, aspect, 1.0f); - entity.AddComponent(camera); - } - else if (name == "Uuid") - { - UuidC uuid; - uuid.uuid = Uuid{metaClass.GetValue("uuid")}; - entity.AddComponent(uuid); - } - else if (name == "Player") - { - PlayerC player; - player.camera = GetEntity(Uuid{metaClass.GetValue("camera-uuid")}); - entity.AddComponent(player); - } - else if (name == "Health") - { - HealthC health; - health.max = std::strtol(metaClass.GetValue("health").c_str(), &endPtr, 10); - health.current = health.max; - entity.AddComponent(health); - } - else if (name == "Physics") - { - PhysicsC physics; - physics.mass = std::strtof(metaClass.GetValue("mass").c_str(), &endPtr); - entity.AddComponent(physics); - } - else if (name == "Animation") - { - AnimationC animation; - animation.sheetCoord = ReadVec2Opt(metaClass, "sheet-coord", glm::ivec2{0, 0}); - animation.sheetSize = ReadVec2Opt(metaClass, "sheet-size", glm::ivec2{1, 1}); - animation.images = std::strtol(metaClass.GetValue("images").c_str(), &endPtr, 10); - animation.horizontal = ReadBoolOpt(metaClass, "horizontal", true); - animation.time = std::strtof(metaClass.GetValue("time").c_str(), &endPtr); - entity.AddComponent(animation); - } - else if (name == "Debug") - { - DebugC debug; - debug.playerEntity = GetEntity(Uuid{metaClass.GetValue("player-uuid")}); - entity.AddComponent(debug); - } - else if (name == "Name") - { - NameC name; - name.name = metaClass.GetValue("name"); - entity.AddComponent(name); - } - else if (name == "Renderable") - entity.AddComponent(); - else if (name == "Pickup") - entity.AddComponent(); - else if (name == "FrameCount") - entity.AddComponent(); - else if (name == "MouseFollow") - entity.AddComponent(); - else if (name == "UiRenderable") - entity.AddComponent(); - else if (name == "LevelGenerator") - entity.AddComponent(); - else + if (!found) CP_WARN("Unknown component: %s", name.c_str()); } catch (RuntimeException& exception) { CP_ERR("Invalid %s component: %s", name.c_str(), exception.GetErrorMessage().c_str()); } @@ -261,25 +183,11 @@ namespace Copium } ImGui::SeparatorText("Components"); - ComponentGui(selectedEntity, "Name", Scene::NameGui, Scene::NameCreate); - ComponentGui(selectedEntity, "Transform", Scene::TransformGui, Scene::TransformCreate); - ComponentGui(selectedEntity, "Texture", Scene::TextureGui, Scene::TextureCreate); - ComponentGui(selectedEntity, "Text", Scene::TextGui, Scene::TextCreate); - ComponentGui(selectedEntity, "Player", Scene::PlayerGui, Scene::PlayerCreate); - ComponentGui(selectedEntity, "Static Collider", Scene::StaticColliderGui, Scene::StaticColliderCreate); - ComponentGui(selectedEntity, "Dynamic Collider", Scene::DynamicColliderGui, Scene::DynamicColliderCreate); - ComponentGui(selectedEntity, "Camera", Scene::CameraGui, Scene::CameraCreate); - ComponentGui(selectedEntity, "Health", Scene::HealthGui, Scene::HealthCreate); - ComponentGui(selectedEntity, "Physics", Scene::PhysicsGui, Scene::PhysicsCreate); - ComponentGui(selectedEntity, "Animation", Scene::AnimationGui, Scene::AnimationCreate); - ComponentGui(selectedEntity, "Debug Information", Scene::DebugGui, Scene::DebugCreate); - ComponentGui(selectedEntity, "Mouse Follow"); - ComponentGui(selectedEntity, "Renderable"); - ComponentGui(selectedEntity, "Ui Renderable"); - ComponentGui(selectedEntity, "Pickup"); - ComponentGui(selectedEntity, "Level Generator"); - ComponentGui(selectedEntity, "Frame Count"); + for (auto& componentHandler : componentHandlers) + { + componentHandler->ComponentGui(selectedEntity); + } } ImGui::End(); } @@ -317,227 +225,4 @@ namespace Copium style.ItemSpacing.x = defaultSpacing; ImGui::End(); } - - Entity Scene::GetEntity(const Uuid& uuid) const - { - Entity entity{ecs.get(),ecs->Find([&](EntityId entity, const UuidC& uuidArg) { return uuid == uuidArg.uuid; })}; - CP_ASSERT(entity, "Failed to find entity with Uuid=%s", uuid.ToString().c_str()); - - return entity; - } - - glm::vec2 Scene::ReadVec2Opt(const MetaFileClass& metaClass, const std::string& key, glm::vec2 vec) - { - if (!metaClass.HasValue(key)) - return vec; - - const std::string& value = metaClass.GetValue(key); - char* endPos; - vec.x = std::strtof(value.c_str(), &endPos); - vec.y = std::strtof(endPos, &endPos); - return vec; - } - - glm::ivec2 Scene::ReadVec2Opt(const MetaFileClass& metaClass, const std::string& key, glm::ivec2 vec) - { - if (!metaClass.HasValue(key)) - return vec; - - const std::string& value = metaClass.GetValue(key); - char* endPos; - vec.x = std::strtof(value.c_str(), &endPos); - vec.y = std::strtof(endPos, &endPos); - return vec; - } - - - bool Scene::ReadBoolOpt(const MetaFileClass& metaClass, const std::string& key, bool vec) - { - if (!metaClass.HasValue(key)) - return vec; - - const std::string& value = metaClass.GetValue(key); - if (value == "true") - return true; - return false; - } - - template - void Scene::ComponentGui(Entity entity, const std::string& componentName, FuncGui funcGui, FuncCreate funcCreate) - { - if (entity.HasComponent()) - { - Component& component = entity.GetComponent(); - if (ImGui::CollapsingHeader(componentName.c_str(), ImGuiTreeNodeFlags_DefaultOpen)) - { - funcGui(component); - if (ImGui::Button(std::string("Delete " + componentName).c_str())) - entity.RemoveComponent(); - } - } - else - { - // TODO: These buttons should probably be in a context menu when you right-click the entity instead - if (ImGui::Button(std::string("Add " + componentName).c_str())) - funcCreate(entity); - } - } - - template - void Scene::ComponentGui(Entity entity, const std::string& componentName) - { - if (entity.HasComponent()) - { - Component& component = entity.GetComponent(); - if (ImGui::Button(std::string("Delete " + componentName).c_str())) - entity.RemoveComponent(); - } - else - { - // TODO: These buttons should probably be in a context menu when you right-click the entity instead - if (ImGui::Button(std::string("Add " + componentName).c_str())) - entity.AddComponent(); - } - } - - void Scene::TransformGui(TransformC& transform) - { - ImGui::DragFloat2("Position", (float*)&transform.position); - ImGui::DragFloat2("Size", (float*)&transform.size); - } - - void Scene::TransformCreate(Entity entity) - { - entity.AddComponent(glm::vec2{0, 0}, glm::vec2{1, 1}); - } - - void Scene::TextureGui(TextureC& texture) - { - Asset& asset = AssetManager::GetAsset(texture.asset); - ImGui::Text("Asset: %s", asset.GetName().c_str()); - ImGui::DragFloat2("Tex Coord 1", (float*)&texture.texCoord1, 0.01, 0.0f, 1.0f); - ImGui::DragFloat2("Tex Coord 2", (float*)&texture.texCoord2, 0.01, 0.0f, 1.0f); - } - - void Scene::TextureCreate(Entity entity) - { - entity.AddComponent(AssetRef{AssetManager::DuplicateAsset(Vulkan::GetEmptyTexture2D())}, glm::vec2{0, 0}, glm::vec2{1, 1}); - } - - void Scene::TextGui(TextC& text) - { - Asset& asset = AssetManager::GetAsset(text.font); - ImGui::Text(asset.GetName().c_str()); - ImGui::InputTextMultiline("Text##Text", &text.text); - ImGui::DragFloat("Font Size", &text.fontSize); - } - - void Scene::TextCreate(Entity entity) - { - entity.AddComponent(AssetRef{AssetManager::LoadAsset("font.meta")}, "", 20.0f); - } - - void Scene::PlayerGui(PlayerC& player) - { - if (player.camera) - ImGui::Text("Camera: %s", player.camera.GetComponent().name.c_str()); - else - ImGui::Text("No camera attached"); - } - - void Scene::PlayerCreate(Entity entity) - { - entity.AddComponent(); - } - - void Scene::StaticColliderGui(StaticColliderC& staticCollider) - { - ImGui::Checkbox("StaticCollider##Resolve Collision", &staticCollider.resolveCollision); - } - - void Scene::StaticColliderCreate(Entity entity) - { - entity.AddComponent(false); - } - - void Scene::DynamicColliderGui(DynamicColliderC& dynamicCollider) - { - ImGui::Checkbox("DynamicCollider##Resolve Collision", &dynamicCollider.resolveCollision); - ImGui::DragFloat2("Collider Offset", (float*)&dynamicCollider.colliderOffset, 0.01); - ImGui::DragFloat2("Collider Size", (float*)&dynamicCollider.colliderSize, 0.01); - } - - void Scene::DynamicColliderCreate(Entity entity) - { - entity.AddComponent(true, glm::vec2{0, 0}, glm::vec2{1, 1}); - } - - void Scene::CameraGui(CameraC& camera) - { - ImGui::Checkbox("Static", &camera.staticBoundingBox); - ImGui::Checkbox("Ui camera", &camera.uiCamera); // TODO: If this changes, the bounding box should be modified if it is not static - } - - void Scene::CameraCreate(Entity entity) - { - // TODO: Make the BoundingBox based on the viewport somehow - entity.AddComponent(BoundingBox{-1.0f, -1.0f, 1.0f, 1.0f}, false, false); - } - - void Scene::HealthGui(HealthC& health) - { - ImGui::DragInt("Max Health", &health.max); - } - - void Scene::HealthCreate(Entity entity) - { - entity.AddComponent(10, 10); - } - - void Scene::PhysicsGui(PhysicsC& physics) - { - ImGui::DragFloat("Mass", &physics.mass); - } - - void Scene::PhysicsCreate(Entity entity) - { - entity.AddComponent(10.0f); - } - - void Scene::AnimationGui(AnimationC& animation) - { - ImGui::DragInt2("Sheet Size", (int*)&animation.sheetSize); - ImGui::DragInt2("Sheet Coord", (int*)&animation.sheetCoord, 1, 0, std::max(animation.sheetSize.x, animation.sheetSize.y)); - ImGui::DragInt("Images", &animation.images); - ImGui::DragFloat("Frame time", &animation.time); - ImGui::Checkbox("Horizontal", &animation.horizontal); - } - - void Scene::AnimationCreate(Entity entity) - { - entity.AddComponent(glm::ivec2{0, 0}, glm::ivec2{1, 1}, 1, true, 1.0f); - } - - void Scene::DebugGui(DebugC& debug) - { - if (debug.playerEntity) - ImGui::Text("Player: %s", debug.playerEntity.GetComponent().name.c_str()); - else - ImGui::Text("No player attached"); - } - - void Scene::DebugCreate(Entity entity) - { - entity.AddComponent(); - } - - void Scene::NameGui(NameC& name) - { - ImGui::InputText("Name##Name", &name.name); - } - - void Scene::NameCreate(Entity entity) - { - entity.AddComponent(String::Format("Entity %d", entity.GetId())); - } } \ No newline at end of file diff --git a/CopiumEngine/src/copium/core/Scene.h b/CopiumEngine/src/copium/core/Scene.h index ede1d00..dfe9d8a 100644 --- a/CopiumEngine/src/copium/core/Scene.h +++ b/CopiumEngine/src/copium/core/Scene.h @@ -5,6 +5,8 @@ #include "copium/event/Event.h" #include "copium/event/EventResult.h" #include "copium/example/Components.h" +#include "copium/example/ComponentHandlerBase.h" +#include "copium/example/ComponentHandler.h" #include "copium/renderer/Renderer.h" #include "copium/util/Uuid.h" @@ -20,6 +22,7 @@ namespace Copium std::unique_ptr ecs; std::unique_ptr descriptorSetRenderer; std::unique_ptr uiDescriptorSetRenderer; + std::vector> componentHandlers; AssetHandle fileIcon; std::unique_ptr descriptorSetFileIcon; glm::mat4 projectionMatrix; @@ -40,41 +43,16 @@ namespace Copium void ComponentViewGui(); void AssetViewGui(); - Entity GetEntity(const Uuid& uuid) const; + template + void RegisterComponentHandler(const Args&... args) + { + componentHandlers.emplace_back(std::make_unique(args...)); + } - glm::vec2 ReadVec2Opt(const MetaFileClass& metaClass, const std::string& key, glm::vec2 vec); - glm::ivec2 ReadVec2Opt(const MetaFileClass& metaClass, const std::string& key, glm::ivec2 vec); - bool ReadBoolOpt(const MetaFileClass& metaClass, const std::string& key, bool vec); - - template - void ComponentGui(Entity entity, const std::string& componentName, FuncGui funcGui, FuncCreate funcCreate); template - static void ComponentGui(Entity entity, const std::string& componentName); - - // TODO: Make these a class instead? - static void TransformGui(TransformC& transform); - static void TransformCreate(Entity entity); - static void TextureGui(TextureC& texture); - static void TextureCreate(Entity entity); - static void TextGui(TextC& text); - static void TextCreate(Entity entity); - static void PlayerGui(PlayerC& player); - static void PlayerCreate(Entity entity); - static void StaticColliderGui(StaticColliderC& staticCollider); - static void StaticColliderCreate(Entity entity); - static void DynamicColliderGui(DynamicColliderC& dynamicCollider); - static void DynamicColliderCreate(Entity entity); - static void CameraGui(CameraC& camera); - static void CameraCreate(Entity entity); - static void PhysicsGui(PhysicsC& physics); - static void PhysicsCreate(Entity entity); - static void HealthGui(HealthC& health); - static void HealthCreate(Entity entity); - static void AnimationGui(AnimationC& animation); - static void AnimationCreate(Entity entity); - static void DebugGui(DebugC& debug); - static void DebugCreate(Entity entity); - static void NameGui(NameC& name); - static void NameCreate(Entity entity); + void RegisterFlagComponentHandler(const std::string& name) + { + componentHandlers.emplace_back(std::make_unique>(name, true)); + } }; } \ No newline at end of file diff --git a/CopiumEngine/src/copium/example/ComponentHandler.h b/CopiumEngine/src/copium/example/ComponentHandler.h new file mode 100644 index 0000000..1825d18 --- /dev/null +++ b/CopiumEngine/src/copium/example/ComponentHandler.h @@ -0,0 +1,92 @@ +#pragma once + +#include "copium/example/ComponentHandlerBase.h" + +#include + +namespace Copium +{ + template + class ComponentHandler : public ComponentHandlerBase + { + protected: + std::string name; + std::string serializedName; + bool flagComponent; + public: + ComponentHandler(const std::string& name, bool flagComponent) + : name{name}, flagComponent{flagComponent} + { + serializedName = name; + serializedName.erase(std::remove(serializedName.begin(), serializedName.end(), ' '), serializedName.end()); + CP_INFO("%s", serializedName.c_str()); + } + + void Serialize(Entity entity, MetaFile& metaFile) override + { + if(entity.HasComponent()) + metaFile.AddMetaClass(serializedName, MetaFileClass{}); + } + + void Deserialize(Entity entity, const MetaFileClass& metaClass) override + { + entity.AddComponent(); + } + + void ComponentGui(Entity entity) override + { + if (flagComponent) + FlagComponentGui(entity); + else + DataComponentGui(entity); + } + + const std::string& GetName() const override + { + return name; + } + + const std::string& GetSerializedName() const override + { + return serializedName; + } + + protected: + virtual void Gui(Component& component) {} + virtual Component Create(Entity entity) { return Component{}; } + + void FlagComponentGui(Entity entity) + { + bool shouldHaveComponent = entity.HasComponent(); + ImGui::Checkbox(name.c_str(), &shouldHaveComponent); + if(shouldHaveComponent == entity.HasComponent()) + return; + + if (shouldHaveComponent) + entity.AddComponent(); + else + entity.RemoveComponent(); + } + + void DataComponentGui(Entity entity) + { + if (entity.HasComponent()) + { + Component& component = entity.GetComponent(); + if (ImGui::CollapsingHeader(name.c_str(), ImGuiTreeNodeFlags_DefaultOpen)) + { + Gui(component); + if (ImGui::Button(std::string("Delete " + name).c_str())) + entity.RemoveComponent(); + } + } + else + { + // TODO: These buttons should probably be in a context menu when you right-click the entity instead + if (ImGui::Button(std::string("Add " + name).c_str())) + entity.AddComponent(Create(entity)); + } + } + + }; +} diff --git a/CopiumEngine/src/copium/example/ComponentHandlerBase.cpp b/CopiumEngine/src/copium/example/ComponentHandlerBase.cpp new file mode 100644 index 0000000..41bf252 --- /dev/null +++ b/CopiumEngine/src/copium/example/ComponentHandlerBase.cpp @@ -0,0 +1,50 @@ +#include "copium/example/ComponentHandlerBase.h" + +#include "copium/example/Components.h" + +namespace Copium +{ + glm::vec2 ComponentHandlerBase::ReadVec2Opt(const MetaFileClass& metaClass, const std::string& key, glm::vec2 vec) + { + if (!metaClass.HasValue(key)) + return vec; + + const std::string& value = metaClass.GetValue(key); + char* endPos; + vec.x = std::strtof(value.c_str(), &endPos); + vec.y = std::strtof(endPos, &endPos); + return vec; + } + + glm::ivec2 ComponentHandlerBase::ReadVec2Opt(const MetaFileClass& metaClass, const std::string& key, glm::ivec2 vec) + { + if (!metaClass.HasValue(key)) + return vec; + + const std::string& value = metaClass.GetValue(key); + char* endPos; + vec.x = std::strtof(value.c_str(), &endPos); + vec.y = std::strtof(endPos, &endPos); + return vec; + } + + + bool ComponentHandlerBase::ReadBoolOpt(const MetaFileClass& metaClass, const std::string& key, bool vec) + { + if (!metaClass.HasValue(key)) + return vec; + + const std::string& value = metaClass.GetValue(key); + if (value == "true") + return true; + return false; + } + + Entity ComponentHandlerBase::GetEntity(ECSManager* ecs, const Uuid& uuid) const + { + Entity entity{ecs, ecs->Find([&](EntityId entity, const UuidC& uuidArg) { return uuid == uuidArg.uuid; })}; + CP_ASSERT(entity, "Failed to find entity with Uuid=%s", uuid.ToString().c_str()); + + return entity; + } +} diff --git a/CopiumEngine/src/copium/example/ComponentHandlerBase.h b/CopiumEngine/src/copium/example/ComponentHandlerBase.h new file mode 100644 index 0000000..8f51777 --- /dev/null +++ b/CopiumEngine/src/copium/example/ComponentHandlerBase.h @@ -0,0 +1,32 @@ +#pragma once + +#include "copium/ecs/Entity.h" +#include "copium/util/MetaFile.h" +#include "copium/util/Uuid.h" + +#include + +#include + +namespace Copium +{ + class ComponentHandlerBase + { + public: + virtual void Serialize(Entity entity, MetaFile& metaFile) = 0; + virtual void Deserialize(Entity entity, const MetaFileClass& metaClass) = 0; + virtual void ComponentGui(Entity entity) = 0; + virtual const std::string& GetName() const = 0; + virtual const std::string& GetSerializedName() const = 0; + + protected: + glm::vec2 ReadVec2Opt(const MetaFileClass& metaClass, const std::string& key, glm::vec2 vec); + glm::ivec2 ReadVec2Opt(const MetaFileClass& metaClass, const std::string& key, glm::ivec2 vec); + bool ReadBoolOpt(const MetaFileClass& metaClass, const std::string& key, bool vec); + + // TODO: I don't like this implementation at all. + // It shouldn't be the ComponentHandlerBases responsibility to know about entities + // But for now I don't know how to solve it in a cleaner way + Entity GetEntity(ECSManager* ecs, const Uuid& uuid) const; + }; +} \ No newline at end of file diff --git a/CopiumEngine/src/copium/example/ComponentHandlers.h b/CopiumEngine/src/copium/example/ComponentHandlers.h new file mode 100644 index 0000000..f7e7f7d --- /dev/null +++ b/CopiumEngine/src/copium/example/ComponentHandlers.h @@ -0,0 +1,358 @@ +#pragma once + +#include "copium/example/ComponentHandler.h" +#include "copium/example/Components.h" + +#include + +namespace Copium +{ + class NameComponentHandler : public ComponentHandler + { + public: + NameComponentHandler() : ComponentHandler{"Name", false} {} + + void Deserialize(Entity entity, const MetaFileClass& metaClass) override + { + NameC name; + name.name = metaClass.GetValue("name"); + entity.AddComponent(name); + } + + protected: + void Gui(NameC& name) override + { + ImGui::InputText("Name##Name", &name.name); + } + + NameC Create(Entity entity) override + { + return NameC{String::Format("Entity %d", entity.GetId())}; + } + }; + + class TransformComponentHandler : public ComponentHandler + { + public: + TransformComponentHandler() : ComponentHandler{"Transform", false} {} + + void Deserialize(Entity entity, const MetaFileClass& metaClass) override + { + TransformC transform; + transform.position = ReadVec2Opt(metaClass, "position", glm::vec2{0.0f, 0.0f}); + transform.size = ReadVec2Opt(metaClass, "size", glm::vec2{1.0f, 1.0f}); + entity.AddComponent(transform); + } + + protected: + void Gui(TransformC& transform) override + { + ImGui::DragFloat2("Position", (float*)&transform.position); + ImGui::DragFloat2("Size", (float*)&transform.size); + } + + TransformC Create(Entity entity) override + { + return TransformC{glm::vec2{0, 0}, glm::vec2{1, 1}}; + } + }; + + class TextureComponentHandler : public ComponentHandler + { + public: + TextureComponentHandler() : ComponentHandler{"Texture", false} {} + + void Deserialize(Entity entity, const MetaFileClass& metaClass) override + { + TextureC texture; + texture.asset = AssetRef{AssetManager::LoadAsset(Uuid{metaClass.GetValue("texture-uuid")})}; + texture.texCoord1 = ReadVec2Opt(metaClass, "tex-coord", glm::vec2{0.0f, 0.0f}); + texture.texCoord2 = texture.texCoord1 + ReadVec2Opt(metaClass, "tex-size", glm::vec2{1.0f, 1.0f} - texture.texCoord1); + entity.AddComponent(texture); + } + + protected: + void Gui(TextureC& texture) override + { + Asset& asset = AssetManager::GetAsset(texture.asset); + ImGui::Text("Asset: %s", asset.GetName().c_str()); + ImGui::DragFloat2("Tex Coord 1", (float*)&texture.texCoord1, 0.01, 0.0f, 1.0f); + ImGui::DragFloat2("Tex Coord 2", (float*)&texture.texCoord2, 0.01, 0.0f, 1.0f); + } + + TextureC Create(Entity entity) override + { + return TextureC{AssetRef{AssetManager::DuplicateAsset(Vulkan::GetEmptyTexture2D())}, glm::vec2{0, 0}, glm::vec2{1, 1}}; + } + }; + + class TextComponentHandler : public ComponentHandler + { + public: + TextComponentHandler() : ComponentHandler{"Text", false} {} + + void Deserialize(Entity entity, const MetaFileClass& metaClass) override + { + char* endPtr; + TextC text; + text.font = AssetRef{AssetManager::LoadAsset(Uuid{metaClass.GetValue("font")})}; + text.text = metaClass.GetValue("text"); + text.fontSize = std::strtof(metaClass.GetValue("font-size").c_str(), &endPtr); + entity.AddComponent(text); + } + + protected: + void Gui(TextC& text) override + { + Asset& asset = AssetManager::GetAsset(text.font); + ImGui::Text(asset.GetName().c_str()); + ImGui::InputTextMultiline("Text##Text", &text.text); + ImGui::DragFloat("Font Size", &text.fontSize); + } + + TextC Create(Entity entity) override + { + return TextC{AssetRef{AssetManager::LoadAsset("font.meta")}, "", 20.0f}; + } + }; + + class StaticColliderComponentHandler : public ComponentHandler + { + public: + StaticColliderComponentHandler() : ComponentHandler{"Static Collider", false} {} + + void Deserialize(Entity entity, const MetaFileClass& metaClass) override + { + StaticColliderC staticCollider; + staticCollider.resolveCollision = ReadBoolOpt(metaClass, "resolve-collision", true); + entity.AddComponent(staticCollider); + } + + protected: + void Gui(StaticColliderC& staticCollider) override + { + ImGui::Checkbox("Resolve Collision##StaticCollider", &staticCollider.resolveCollision); + } + + StaticColliderC Create(Entity entity) override + { + return StaticColliderC{false}; + } + }; + + class DynamicColliderComponentHandler : public ComponentHandler + { + public: + DynamicColliderComponentHandler() : ComponentHandler{"Dynamic Collider", false} {} + + void Deserialize(Entity entity, const MetaFileClass& metaClass) override + { + DynamicColliderC dynamicCollider; + dynamicCollider.resolveCollision = ReadBoolOpt(metaClass, "resolve-collision", true); + dynamicCollider.colliderOffset = ReadVec2Opt(metaClass, "collider-offset", glm::vec2{0.0f, 0.0f}); + dynamicCollider.colliderSize = ReadVec2Opt(metaClass, "collider-size", glm::vec2{1.0f, 1.0f}); + entity.AddComponent(dynamicCollider); + } + + protected: + void Gui(DynamicColliderC& dynamicCollider) override + { + ImGui::Checkbox("Resolve Collision##DynamicCollider", &dynamicCollider.resolveCollision); + ImGui::DragFloat2("Collider Offset", (float*)&dynamicCollider.colliderOffset, 0.01); + ImGui::DragFloat2("Collider Size", (float*)&dynamicCollider.colliderSize, 0.01); + } + + DynamicColliderC Create(Entity entity) override + { + return DynamicColliderC{true, glm::vec2{0, 0}, glm::vec2{1, 1}}; + } + }; + + class PlayerComponentHandler : public ComponentHandler + { + public: + PlayerComponentHandler() : ComponentHandler{"Player", false} {} + + void Deserialize(Entity entity, const MetaFileClass& metaClass) override + { + PlayerC player; + player.camera = GetEntity(entity.GetManager(), Uuid{metaClass.GetValue("camera-uuid")}); + entity.AddComponent(player); + } + + protected: + void Gui(PlayerC& player) override + { + if (player.camera) + ImGui::Text("Camera: %s", player.camera.GetComponent().name.c_str()); + else + ImGui::Text("No camera attached"); + } + + PlayerC Create(Entity entity) override + { + return PlayerC{}; + } + }; + + class CameraComponentHandler : public ComponentHandler + { + private: + BoundingBox* viewport; + public: + CameraComponentHandler(BoundingBox* viewport) : ComponentHandler{"Camera", false}, viewport{viewport} {} + + void Deserialize(Entity entity, const MetaFileClass& metaClass) override + { + float aspect = viewport->GetSize().x / viewport->GetSize().y; + + CameraC camera; + camera.staticBoundingBox = ReadBoolOpt(metaClass, "static-bounding-box", false); + camera.uiCamera = ReadBoolOpt(metaClass, "ui-camera", false); + if (camera.uiCamera) + camera.projection = BoundingBox(0, 0, Vulkan::GetSwapChain().GetExtent().width, Vulkan::GetSwapChain().GetExtent().height); + else + camera.projection = BoundingBox(-aspect, -1.0f, aspect, 1.0f); + entity.AddComponent(camera); + } + + protected: + void Gui(CameraC& camera) override + { + ImGui::Checkbox("Static", &camera.staticBoundingBox); + ImGui::Checkbox("Ui camera", &camera.uiCamera); // TODO: If this changes, the bounding box should be modified if it is not static + } + + CameraC Create(Entity entity) override + { + return CameraC{BoundingBox{-1.0f, -1.0f, 1.0f, 1.0f}, false, false}; + } + }; + + class UuidComponentHandler : public ComponentHandler + { + public: + UuidComponentHandler() : ComponentHandler{"Uuid", false} {} + + void Deserialize(Entity entity, const MetaFileClass& metaClass) override + { + UuidC uuid; + uuid.uuid = Uuid{metaClass.GetValue("uuid")}; + entity.AddComponent(uuid); + } + + protected: + void ComponentGui(Entity entity) override {} + }; + + class HealthComponentHandler : public ComponentHandler + { + public: + HealthComponentHandler() : ComponentHandler{"Health", false} {} + + void Deserialize(Entity entity, const MetaFileClass& metaClass) override + { + char* endPtr; + HealthC health; + health.max = std::strtol(metaClass.GetValue("health").c_str(), &endPtr, 10); + health.current = health.max; + entity.AddComponent(health); + } + + protected: + void Gui(HealthC& health) override + { + ImGui::DragInt("Max Health", &health.max); + } + + HealthC Create(Entity entity) override + { + return HealthC{10, 10}; + } + }; + + class PhysicsComponentHandler : public ComponentHandler + { + public: + PhysicsComponentHandler() : ComponentHandler{"Physics", false} {} + + void Deserialize(Entity entity, const MetaFileClass& metaClass) override + { + char* endPtr; + PhysicsC physics; + physics.mass = std::strtof(metaClass.GetValue("mass").c_str(), &endPtr); + entity.AddComponent(physics); + } + + protected: + void Gui(PhysicsC& physics) override + { + ImGui::DragFloat("Mass", &physics.mass); + } + + PhysicsC Create(Entity entity) override + { + return PhysicsC{10.0f}; + } + }; + + class AnimationComponentHandler : public ComponentHandler + { + public: + AnimationComponentHandler() : ComponentHandler{"Animation", false} {} + + void Deserialize(Entity entity, const MetaFileClass& metaClass) override + { + char* endPtr; + AnimationC animation; + animation.sheetCoord = ReadVec2Opt(metaClass, "sheet-coord", glm::ivec2{0, 0}); + animation.sheetSize = ReadVec2Opt(metaClass, "sheet-size", glm::ivec2{1, 1}); + animation.images = std::strtol(metaClass.GetValue("images").c_str(), &endPtr, 10); + animation.horizontal = ReadBoolOpt(metaClass, "horizontal", true); + animation.time = std::strtof(metaClass.GetValue("time").c_str(), &endPtr); + entity.AddComponent(animation); + } + + protected: + void Gui(AnimationC& animation) override + { + ImGui::DragInt2("Sheet Size", (int*)&animation.sheetSize); + ImGui::DragInt2("Sheet Coord", (int*)&animation.sheetCoord, 1, 0, std::max(animation.sheetSize.x, animation.sheetSize.y)); + ImGui::DragInt("Images", &animation.images); + ImGui::DragFloat("Frame time", &animation.time); + ImGui::Checkbox("Horizontal", &animation.horizontal); + } + + AnimationC Create(Entity entity) override + { + return AnimationC{glm::ivec2{0, 0}, glm::ivec2{1, 1}, 1, true, 1.0f}; + } + }; + + class DebugComponentHandler : public ComponentHandler + { + public: + DebugComponentHandler() : ComponentHandler{"Debug", false} {} + + void Deserialize(Entity entity, const MetaFileClass& metaClass) override + { + DebugC debug; + debug.playerEntity = GetEntity(entity.GetManager(), Uuid{metaClass.GetValue("player-uuid")}); + entity.AddComponent(debug); + } + + protected: + void Gui(DebugC& debug) override + { + if (debug.playerEntity) + ImGui::Text("Player: %s", debug.playerEntity.GetComponent().name.c_str()); + else + ImGui::Text("No player attached"); + } + + DebugC Create(Entity entity) override + { + return DebugC{}; + } + }; +}