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{};
+ }
+ };
+}