From d4c420d5b4026e044f666559e8c3feee74f0ef45 Mon Sep 17 00:00:00 2001 From: Erris Date: Tue, 17 Feb 2026 00:50:43 +0100 Subject: [PATCH] ECS with entt --- editor/imgui.ini | 77 ++++ editor/include/editor.hpp | 415 +++++++++++------- editor/include/level_editor.hpp | 13 +- editor/include/panels/scene_hierarchy.hpp | 40 ++ editor/src/editor.cpp | 12 +- editor/src/panels/scene_hierarchy.cpp | 150 +++++++ imgui.ini | 59 +++ licenses/entt.md | 24 + licenses/fmt.md | 20 + open_engine/include/open_engine.hpp | 6 +- .../include/open_engine/application.hpp | 1 + open_engine/include/open_engine/core.hpp | 3 +- .../include/open_engine/imgui/imgui_layer.hpp | 5 + .../open_engine/orthographic_camera.hpp | 6 +- open_engine/include/open_engine/pch.hpp | 1 + .../include/open_engine/renderer/camera.hpp | 26 ++ .../open_engine/renderer/renderer2d.hpp | 8 +- .../include/open_engine/scene/components.hpp | 151 +++++++ .../include/open_engine/scene/entity.hpp | 64 +++ .../scene/native_scriptable_entity.hpp | 27 ++ .../include/open_engine/scene/scene.hpp | 33 ++ .../open_engine/scene/scene_camera.hpp | 74 ++++ .../src/open_engine/imgui/imgui_layer.cpp | 14 +- .../open_engine/opengl/opengl_framebuffer.cpp | 6 + .../src/open_engine/renderer/camera.cpp | 7 + .../src/open_engine/renderer/renderer2d.cpp | 145 ++++-- open_engine/src/open_engine/scene/camera.cpp | 60 +++ open_engine/src/open_engine/scene/entity.cpp | 10 + .../scene/native_scriptable_entity.cpp | 1 + open_engine/src/open_engine/scene/scene.cpp | 78 ++++ 30 files changed, 1314 insertions(+), 222 deletions(-) create mode 100644 editor/imgui.ini create mode 100644 editor/include/panels/scene_hierarchy.hpp create mode 100644 editor/src/panels/scene_hierarchy.cpp create mode 100644 imgui.ini create mode 100644 licenses/entt.md create mode 100644 licenses/fmt.md create mode 100644 open_engine/include/open_engine/renderer/camera.hpp create mode 100644 open_engine/include/open_engine/scene/components.hpp create mode 100644 open_engine/include/open_engine/scene/entity.hpp create mode 100644 open_engine/include/open_engine/scene/native_scriptable_entity.hpp create mode 100644 open_engine/include/open_engine/scene/scene.hpp create mode 100644 open_engine/include/open_engine/scene/scene_camera.hpp create mode 100644 open_engine/src/open_engine/renderer/camera.cpp create mode 100755 open_engine/src/open_engine/scene/camera.cpp create mode 100644 open_engine/src/open_engine/scene/entity.cpp create mode 100644 open_engine/src/open_engine/scene/native_scriptable_entity.cpp create mode 100644 open_engine/src/open_engine/scene/scene.cpp diff --git a/editor/imgui.ini b/editor/imgui.ini new file mode 100644 index 0000000..72570a3 --- /dev/null +++ b/editor/imgui.ini @@ -0,0 +1,77 @@ +[Window][Debug##Default] +Pos=60,60 +Size=166,153 +Collapsed=0 + +[Window][Dear ImGui Demo] +Pos=899,10 +Size=358,368 +Collapsed=0 + +[Window][Dear ImGui Style Editor] +Pos=136,23 +Size=353,682 +Collapsed=0 + +[Window][Example: Log] +Pos=431,613 +Size=409,223 +Collapsed=0 +DockId=0x00000006,0 + +[Window][My First Tool] +Pos=181,56 +Size=388,464 +Collapsed=0 + +[Window][DockSpace Demo] +Size=1259,688 +Collapsed=0 + +[Window][Dear ImGui Debug Log] +Pos=1349,371 +Size=644,567 +Collapsed=0 + +[Window][Example: Console] +Pos=0,19 +Size=519,669 +Collapsed=0 +DockId=0x00000003,0 + +[Window][Example: Assets Browser] +Pos=842,613 +Size=326,223 +Collapsed=0 +DockId=0x00000009,0 + +[Window][Example: Property editor] +Pos=0,19 +Size=429,817 +Collapsed=0 +DockId=0x00000005,0 + +[Window][Example: Property editor/##tree_37EC733C] +IsChild=1 +Size=300,782 + +[Table][0xB6880529,2] +RefScale=13 +Column 0 Sort=0v + +[Table][0x2048C668,2] +RefScale=13 +Column 0 Width=4 +Column 1 Weight=2.0000 + +[Docking][Data] +DockSpace ID=0xC0DFADC4 Pos=0,19 Size=1259,669 Split=X Selected=0x5E5F7166 + DockNode ID=0x00000005 Parent=0xC0DFADC4 SizeRef=429,817 Selected=0x256ED220 + DockNode ID=0x0000000A Parent=0xC0DFADC4 SizeRef=1143,817 Split=X + DockNode ID=0x00000003 Parent=0x0000000A SizeRef=519,669 Selected=0x1BCA3180 + DockNode ID=0x00000004 Parent=0x0000000A SizeRef=751,669 Split=Y + DockNode ID=0x00000007 Parent=0x00000004 SizeRef=860,592 CentralNode=1 + DockNode ID=0x00000008 Parent=0x00000004 SizeRef=860,223 Split=X Selected=0x38CCB771 + DockNode ID=0x00000006 Parent=0x00000008 SizeRef=649,223 Selected=0x38CCB771 + DockNode ID=0x00000009 Parent=0x00000008 SizeRef=517,223 Selected=0xD2C213DD + diff --git a/editor/include/editor.hpp b/editor/include/editor.hpp index 703f575..d90b4cc 100755 --- a/editor/include/editor.hpp +++ b/editor/include/editor.hpp @@ -1,209 +1,292 @@ #ifndef EDITOR_HPP #define EDITOR_HPP -#include "open_engine/input/input_system.hpp" -#include +#include "open_engine/ref_scope.hpp" +#include "panels/scene_hierarchy.hpp" #include #include #include #include -#include -#include +#include #include #include +#include +#include -class EditorLayer : public OpenEngine::Layer -{ - public: - EditorLayer(OpenEngine::Ref& view_layer) - : OpenEngine::Layer("editor_layer"), - view(view_layer) +namespace OpenEngine { + + class CameraController : public NativeScriptableEntity + { + public: + void OnCreate() + { + }; + + void OnUpdate() + { + auto delta = Time::DeltaTime(); + + auto& transform = GetComponent(); + + if (Input::IsKeyPressed(KeyCode::Up)) + transform.transform[3][1] += 5.0 * delta; + if (Input::IsKeyPressed(KeyCode::Down)) + transform.transform[3][1] -= 5.0 * delta; + if (Input::IsKeyPressed(KeyCode::Right)) + transform.transform[3][0] += 5.0 * delta; + if (Input::IsKeyPressed(KeyCode::Left)) + transform.transform[3][0] -= 5.0 * delta; + }; + + void OnDestroy() + { + }; + }; + + class EditorLayer : public Layer + { + public: + EditorLayer() + : Layer("editor_layer")/*, + camera((float)Application::Get().GetWindow().GetWidth() / + Application::Get().GetWindow().GetHeight(), 1.0f) + */ { } - ~EditorLayer() {}; + ~EditorLayer() {}; - void OnAttach() override - { - OE_PROFILE_FUNCTION(); - } + entt::registry registry; - void OnDetach() override - { - } - - void OnUpdate() override - { - - OpenEngine::Renderer2D::ResetStats(); + void OnAttach() override { OE_PROFILE_FUNCTION(); - view->GetCamera().OnUpdate(); + FramebufferSpecification specs; + specs.width = 1280; + specs.height = 720; + + framebuffer = FrameBuffer::Create(specs); + + scene = CreateRef(); + + sq_entity = scene->CreateEntity("square"); + + sq_entity.AddComponents(); + sq_entity.AddComponents(glm::vec4(1.0f, 0.0f, 0.0f, 1.0f)); + + camera_bis = scene->CreateEntity("Main camera"); + camera_bis.AddComponents(); + camera_bis.AddComponents(); + camera_bis.AddComponents().Bind(); + + scene_hierarchy.SetContext(scene); } - OpenEngine::Renderer2D::EndScene(); - } - - void OnEvent(OpenEngine::Event& event) override - { - OE_PROFILE_FUNCTION(); - + void OnDetach() override { - OE_PROFILE_SCOPE("Camera OnEvent"); - view->GetCamera().OnEvent(event); - } - } - - float remap(float value, float minInput, float maxInput, float minOutput, float maxOutput) { - // 1. Normalize the input to a 0.0 - 1.0 range - float t = (value - minInput) / (maxInput - minInput); - - // 2. Use glm::mix to interpolate between the output range - return glm::mix(minOutput, maxOutput, t); - } - - glm::vec3 ScreenToWorld( - float screenX, - float screenY, - float screenZ, // depth value (0.0 = near plane, 1.0 = far plane) - const glm::mat4& view, - const glm::mat4& projection, - int viewportWidth, - int viewportHeight) - { - // 1. Convert screen coordinates to normalized device coordinates (NDC) - // Screen space: origin at top-left, Y points down - // NDC space: origin at center, range [-1, 1] for x and y - - float x = (2.0f * screenX) / viewportWidth - 1.0f; - float y = 1.0f - (2.0f * screenY) / viewportHeight; // Flip Y - float z = 2.0f * screenZ - 1.0f; // [0,1] -> [-1,1] - - glm::vec4 clipCoords(x, y, z, 1.0f); - - // 2. Transform from clip space to view space - glm::mat4 invProjection = glm::inverse(projection); - glm::vec4 viewCoords = invProjection * clipCoords; - - // 3. Transform from view space to world space - glm::mat4 invView = glm::inverse(view); - glm::vec4 worldCoords = invView * viewCoords; - - // 4. Perspective divide - if (worldCoords.w != 0.0f) { - worldCoords /= worldCoords.w; } - return glm::vec3(worldCoords); - } - - void OnImGuiRender() override - { - OE_PROFILE_FUNCTION(); - - OpenEngine::Ref framebuffer = view->GetFramebuffer(); - - ImGui::DockSpaceOverViewport(); - - ImGui::Begin("Square settings"); - - //ImGui::SliderFloat("Angle", &angle, -360, 360); - //ImGui::ColorPicker4("Rectangle color", &rectangle_color[0]); - - for (auto& result : profiling_results) + void OnUpdate() override { - char label[50]; - strcpy(label, "%.3fms "); - strcat(label, result.name); - ImGui::Text(label, result.duration); + FramebufferSpecification spec = framebuffer->GetSpecification(); + if (viewport_size.x > 0.0f && viewport_size.y > 0.0f && + (spec.width != viewport_size.x || spec.height != viewport_size.y)) + { + framebuffer->Resize((uint32_t)viewport_size.x, (uint32_t)viewport_size.y); + //camera.OnResize(viewport_size.x, viewport_size.y); + } + + { + OE_PROFILE_SCOPE("Setting up Rendering"); + //camera.OnUpdate(); + framebuffer->Bind(); + + scene->OnViewportResize((uint32_t)viewport_size.x, (uint32_t)viewport_size.y); + } + + Renderer2D::ResetStats(); + RenderCommand::SetClearColor({0.11f, 0.11f, 0.15f, 1.0f}); + RenderCommand::Clear(); + + //Renderer2D::BeginScene(camera.GetCamera()); + + /* + for (float y = -5.0f; y < 5.0f; y += 0.5f) { + for (float x = -5.0f; x < 5.0f; x += 0.5f) { + + glm::vec4 gradient_color = {(x + 5.0f) / 10.0f, 0.3f, (y + 5.0f) / 10.0f, 1.0f}; + Renderer2D::DrawQuad({{x, y, 0.0f}, glm::vec3(0.45f, 0.45f, 0.0f)}, gradient_color); + } + } + + Transform tr5 = { + glm::vec3(world_pos_cursor.x, world_pos_cursor.y, 0.9f), + glm::vec3(0.1f, 0.1f, 0.0f), 0.0f}; + + Renderer2D::DrawQuad(tr5, {1, 1, 1, 1}); + */ + + scene->OnUpdate(); + + //Renderer2D::EndScene(); + + framebuffer->Unbind(); } - profiling_results.clear(); + void OnEvent(Event& event) override + { + OE_PROFILE_FUNCTION(); + //camera.OnEvent(event); + } + + float remap(float value, float minInput, float maxInput, float minOutput, float maxOutput) { + // 1. Normalize the input to a 0.0 - 1.0 range + float t = (value - minInput) / (maxInput - minInput); + + // 2. Use glm::mix to interpolate between the output range + return glm::mix(minOutput, maxOutput, t); + } + + glm::vec3 ScreenToWorld( + float screenX, + float screenY, + float screenZ, // depth value (0.0 = near plane, 1.0 = far plane) + const glm::mat4& view, + const glm::mat4& projection, + int viewportWidth, + int viewportHeight) + { + // 1. Convert screen coordinates to normalized device coordinates (NDC) + // Screen space: origin at top-left, Y points down + // NDC space: origin at center, range [-1, 1] for x and y + + float x = (2.0f * screenX) / viewportWidth - 1.0f; + float y = 1.0f - (2.0f * screenY) / viewportHeight; // Flip Y + float z = 2.0f * screenZ - 1.0f; // [0,1] -> [-1,1] + + glm::vec4 clipCoords(x, y, z, 1.0f); + + // 2. Transform from clip space to view space + glm::mat4 invProjection = glm::inverse(projection); + glm::vec4 viewCoords = invProjection * clipCoords; + + // 3. Transform from view space to world space + glm::mat4 invView = glm::inverse(view); + glm::vec4 worldCoords = invView * viewCoords; + + // 4. Perspective divide + if (worldCoords.w != 0.0f) { + worldCoords /= worldCoords.w; + } + + return glm::vec3(worldCoords); + }; + + void DrawViewport() + { + ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2{ 0, 0 }); + + ImGui::Begin("Viewport"); + bool focused = ImGui::IsWindowFocused(); + bool hovered = ImGui::IsWindowHovered(); + Application::Get().GetImGuiLayer()->SetBlockEvents(!focused || !hovered); + + ImVec2 viewport_panel_size = ImGui::GetContentRegionAvail(); - ImGui::End(); - ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2{ 0, 0 }); - ImGui::Begin("Viewport"); - ImVec2 viewport_panel_size = ImGui::GetContentRegionAvail(); - if (viewport_size != *((glm::vec2*)&viewport_panel_size)) { - framebuffer->Resize(viewport_panel_size.x, viewport_panel_size.y); viewport_size = { viewport_panel_size.x, viewport_panel_size.y }; - view->GetCamera().OnResize(viewport_panel_size.x, viewport_panel_size.y); - } - uint32_t texture_id = framebuffer->GetColorAttachmentRendererID(); - ImGuiTabBarFlags tab_bar_flags = ImGuiTabBarFlags_None; - if (ImGui::BeginTabBar("MyTabBar", tab_bar_flags)) + ImVec2 imgui_cursor_position = ImGui::GetCursorScreenPos(); + auto pair_position = Input::GetMousePosition(); + glm::vec2 mouse_position = { pair_position.first, pair_position.second }; + + int max_x = viewport_size.x + imgui_cursor_position.x; + int max_y = viewport_size.y + imgui_cursor_position.y; + + if (focused || hovered) { + float x = mouse_position.x - imgui_cursor_position.x; + float y = mouse_position.y - imgui_cursor_position.y; + + /* + auto& cam = camera.GetCamera(); + world_pos_cursor = ScreenToWorld(x, y, 0.0f, + cam.GetViewMatrix(), + cam.GetProjectionMatrix(), + viewport_size.x, + viewport_size.y); + */ + } + + uint32_t texture_id = framebuffer->GetColorAttachmentRendererID(); + ImGui::Image((void*)(uint64_t)texture_id, ImVec2{ viewport_size.x, viewport_size.y }, ImVec2{ 0, 1 }, ImVec2{ 1, 0 }); + + ImGui::End(); + ImGui::PopStyleVar(); + }; + + void DrawStats() { - if (ImGui::BeginTabItem("Avocado")) - { - ImVec2 imgui_cursor_position = ImGui::GetCursorScreenPos(); + auto stats = Renderer2D::GetStats(); + static float time = 0; - auto pair_position = OpenEngine::Input::GetMousePosition(); - glm::vec2 mouse_position = { pair_position.first, pair_position.second }; + time += Time::DeltaTime(); - int max_x = viewport_size.x + imgui_cursor_position.x; - int max_y = viewport_size.y + imgui_cursor_position.y; - - if ((mouse_position.x <= max_x && mouse_position.x >= imgui_cursor_position.x) - && (mouse_position.y <= max_y && mouse_position.y >= imgui_cursor_position.y)) { - - float x = mouse_position.x - imgui_cursor_position.x; - float y = mouse_position.y - imgui_cursor_position.y; - - auto& camera = view->GetCamera().GetCamera(); - auto world_coords = ScreenToWorld(x, y, 0.0f, - camera.GetViewMatrix(), - camera.GetProjectionMatrix(), - viewport_size.x, - viewport_size.y); - view->SetCursorPos({ world_coords.x, world_coords.y }); - } - ImGui::Image((void*)texture_id, ImVec2{ viewport_size.x, viewport_size.y }, ImVec2{ 0, 1 }, ImVec2{ 1, 0 }); - ImGui::EndTabItem(); + if (time >= 1) { + time = 0; } - if (ImGui::BeginTabItem("Broccoli")) - { - ImGui::Text("This is the Broccoli tab!\nblah blah blah blah blah"); - ImGui::EndTabItem(); - } - if (ImGui::BeginTabItem("Cucumber")) - { - ImGui::Text("This is the Cucumber tab!\nblah blah blah blah blah"); - ImGui::EndTabItem(); - } - ImGui::EndTabBar(); - } - ImGui::End(); - ImGui::PopStyleVar(); - auto stats = OpenEngine::Renderer2D::GetStats(); - static float time = 0; + ImGui::Begin("Statistics"); + ImGui::Text("FPS: %0.0f", 1 / Time::DeltaTime()); + ImGui::Text("Renderer2D:"); + ImGui::Text("\t\tDraw calls: %d", stats.draw_calls); + ImGui::Text("\t\tQuad count: %d", stats.quad_count); + ImGui::Text("\t\tVertices count: %d", stats.GetToralVertexCount()); + ImGui::Text("\t\tIndices count: %d", stats.GetToralIndexCount()); - time += OpenEngine::Time::DeltaTime(); + /* + for (auto& result : profiling_results) + { + char label[50]; + strcpy(label, "%.3fms "); + strcat(label, result.name); + ImGui::Text(label, result.duration); + } - if (time >= 1) { - time = 0; - } + profiling_results.clear(); + */ - ImGui::Begin("Statistics"); - ImGui::Text("FPS: %0.0f", 1 / OpenEngine::Time::DeltaTime()); - ImGui::Text("Renderer2D:"); - ImGui::Text("\t\tDraw calls: %d", stats.draw_calls); - ImGui::Text("\t\tQuad count: %d", stats.quad_count); - ImGui::Text("\t\tVertices count: %d", stats.GetToralVertexCount()); - ImGui::Text("\t\tIndices count: %d", stats.GetToralIndexCount()); - ImGui::ShowDemoWindow(); - ImGui::End(); - } + ImGui::End(); + }; - private: - std::vector profiling_results; + void OnImGuiRender() override + { + OE_PROFILE_FUNCTION(); - glm::vec2 viewport_size = { 0.0f, 0.0f }; + ImGui::DockSpaceOverViewport(); - OpenEngine::Ref view; -}; + DrawStats(); + scene_hierarchy.OnImGuiRender(); + DrawViewport(); + + ImGui::ShowDemoWindow(); + }; + + private: + std::vector profiling_results; + + glm::vec2 world_pos_cursor = { 0.0f, 0.0f }; + + glm::vec2 viewport_size = { 0.0f, 0.0f }; + Ref framebuffer; + SceneHierarchy scene_hierarchy; + //OrthographicCameraController camera; + + Entity camera_bis; + + Ref scene; + Entity sq_entity; + }; +} class EditorApp : public OpenEngine::Application { diff --git a/editor/include/level_editor.hpp b/editor/include/level_editor.hpp index 9b6aec6..7e379e3 100644 --- a/editor/include/level_editor.hpp +++ b/editor/include/level_editor.hpp @@ -31,7 +31,7 @@ class LevelEditor : public ViewLayer GetCamera().OnUpdate(); GetFramebuffer()->Bind(); } - + OpenEngine::RenderCommand::SetClearColor({0.11f, 0.11f, 0.15f, 1.0f}); OpenEngine::RenderCommand::Clear(); @@ -47,7 +47,6 @@ class LevelEditor : public ViewLayer } auto& cursor = GetCursorPos(); - OE_TRACE("world coordinates: {} {}", cursor.x, cursor.y); OpenEngine::Transform tr5 = {glm::vec3(cursor.x, cursor.y, 0.9f), glm::vec3(0.1f, 0.1f, 0.0f), 0.0f}; OpenEngine::Renderer2D::DrawQuad(tr5, {1, 1, 1, 1}); @@ -58,14 +57,8 @@ class LevelEditor : public ViewLayer void OnEvent(OpenEngine::Event& event) override { - auto& cursor = GetCursorPos(); - OE_TRACE("world coordinates: {} {}", cursor.x, cursor.y); - OpenEngine::Transform tr5 = {glm::vec3(cursor.x, cursor.y, 0.9f), glm::vec3(1.0f, 1.0f, 0.0f), 0.0f}; - OpenEngine::Renderer2D::DrawQuad(tr5, {1, 1, 1, 1}); - - OpenEngine::Renderer2D::EndScene(); - - GetFramebuffer()->Unbind(); + OE_PROFILE_FUNCTION(); + GetCamera().OnEvent(event); }; void OnDetach() override {}; diff --git a/editor/include/panels/scene_hierarchy.hpp b/editor/include/panels/scene_hierarchy.hpp new file mode 100644 index 0000000..c7ea4b4 --- /dev/null +++ b/editor/include/panels/scene_hierarchy.hpp @@ -0,0 +1,40 @@ +#ifndef SCENE_HIERARCHY_HPP +#define SCENE_HIERARCHY_HPP + +#include "imgui.h" +#include + +namespace OpenEngine { + class SceneHierarchy + { + public: + SceneHierarchy() = default; + SceneHierarchy(const Ref& scene); + + void SetContext(const Ref& scene); + + void OnImGuiRender(); + + private: + void DrawEntityNode(Entity& entity); + void DrawComponents(Entity& entity); + + template + void DrawComponentDrawer(Entity& entity, const char* header) + { + if (entity.HasComponent()) + if (ImGui::TreeNodeEx((void*)typeid(T).hash_code(), ImGuiTreeNodeFlags_DefaultOpen, "%s", header)) { + entity.GetComponents().OnImGuiRender(entity); + ImGui::TreePop(); + ImGui::Separator(); + } + } + + private: + Ref scene; + Entity selected_context, renamed_entity; + double last_selected_time = 10.0; + }; +} + +#endif // SCENE_HIERARCHY_HPP diff --git a/editor/src/editor.cpp b/editor/src/editor.cpp index f363e49..fdf30fc 100644 --- a/editor/src/editor.cpp +++ b/editor/src/editor.cpp @@ -9,18 +9,14 @@ #include #include -#include - EditorApp::EditorApp() : Application("OpenEngine Editor") { - OpenEngine::Ref modding_layer = std::make_shared(); - OpenEngine::Ref view = OpenEngine::CreateRef(); - OpenEngine::Ref initial_layer = std::make_shared(view); - OpenEngine::Ref control_layer = std::make_shared(initial_layer); + OpenEngine::Ref modding_layer = OpenEngine::CreateRef(); + OpenEngine::Ref editor_layer = OpenEngine::CreateRef(); + OpenEngine::Ref control_layer = OpenEngine::CreateRef(editor_layer); - QueueLayerPush(view); - QueueLayerPush(initial_layer); + QueueLayerPush(editor_layer); QueueLayerPush(control_layer); QueueLayerPush(modding_layer); } diff --git a/editor/src/panels/scene_hierarchy.cpp b/editor/src/panels/scene_hierarchy.cpp new file mode 100644 index 0000000..44200cc --- /dev/null +++ b/editor/src/panels/scene_hierarchy.cpp @@ -0,0 +1,150 @@ +#include "open_engine/scene/components.hpp" +#include +#include +#include + +#include + +namespace OpenEngine { + SceneHierarchy::SceneHierarchy(const Ref& scene) + : scene(scene) + { + } + + void SceneHierarchy::SetContext(const Ref& context) + { + scene = context; + } + + void SceneHierarchy::OnImGuiRender() + { + ImGui::Begin("Scene"); + + for (auto entity_entt : scene->GetRegistry().view()) { + Entity entity{ entity_entt, scene.get() }; + + DrawEntityNode(entity); + } + + if ((ImGui::IsMouseDown(0) && ImGui::IsWindowHovered()) + || (ImGui::IsKeyDown(ImGuiKey_Escape) && ImGui::IsWindowHovered())) + { + selected_context = {}; + renamed_entity = {}; + } + + if (ImGui::BeginPopupContextWindow(0, ImGuiPopupFlags_MouseButtonRight | ImGuiPopupFlags_NoOpenOverItems)) + { + if (ImGui::MenuItem("Create Empty Entity")) + { + scene->CreateEntity("Empty entity"); + } + ImGui::EndPopup(); + } + + ImGui::End(); + + ImGui::Begin("Properties"); + + if (selected_context) { + DrawComponents(selected_context); + + if (ImGui::BeginPopupContextWindow(0, ImGuiPopupFlags_MouseButtonRight | ImGuiPopupFlags_NoOpenOverItems)) + { + if (ImGui::MenuItem("Add transform")) + { + selected_context.AddComponents(); + } + if (ImGui::MenuItem("Add Sprite")) + { + selected_context.AddComponents(); + } + if (ImGui::MenuItem("Add camera")) + { + selected_context.AddComponents(); + } + ImGui::EndPopup(); + } + } + + ImGui::End(); + } + + void SceneHierarchy::DrawEntityNode(Entity& entity) + { + auto& tag = entity.GetComponents().tag; + + if (renamed_entity == entity && selected_context == entity) { + char buffer[255]; + std::memset(buffer, 0, sizeof(buffer)); + std::strncpy(buffer, tag.c_str(), sizeof(buffer)); + + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(2, 2)); + ImGui::SetKeyboardFocusHere(); + + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + + if (ImGui::InputText("##tagEditor", buffer, sizeof(buffer), ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_AutoSelectAll) + || (!ImGui::IsItemActive() && ImGui::IsMouseClicked(0))) { + tag = buffer; + renamed_entity = {}; + } + + if (ImGui::IsKeyPressed(ImGuiKey_Escape)) + renamed_entity = {}; + + ImGui::PopStyleVar(); + + } else { + ImGuiTreeNodeFlags flags = ((selected_context == entity) ? ImGuiTreeNodeFlags_Selected : 0) | ImGuiTreeNodeFlags_OpenOnArrow; + flags |= ImGuiTreeNodeFlags_SpanAvailWidth; + + bool opened = ImGui::TreeNodeEx((void*)(uint64_t)(uint32_t)entity, flags, "%s", tag.c_str()); + + if (ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) { + // FocusViewportOnEntity(entity); // future implementation + renamed_entity = {}; + } else if (ImGui::IsItemClicked()) { + + double current_time = ImGui::GetTime(); + double double_click_time = ImGui::GetIO().MouseDoubleClickTime; // Usually ~0.3s + + if (selected_context == entity) { + renamed_entity = entity; + if (current_time - last_selected_time > double_click_time) + { + renamed_entity = entity; + } + } else { + // First time selecting + selected_context = entity; + last_selected_time = current_time; + } + } + + if (opened) + ImGui::TreePop(); + } + } + + void SceneHierarchy::DrawComponents(Entity& entity) + { + if (entity.HasComponent()) + entity.GetComponents().OnImGuiRender(entity); + + DrawComponentDrawer(entity, "Transform"); + DrawComponentDrawer(entity, "Sprite"); + DrawComponentDrawer(entity, "Camera"); + } +} + +/* + if (ImGui::BeginPopupContextWindow(0, ImGuiPopupFlags_MouseButtonRight | ImGuiPopupFlags_NoOpenOverItems)) + { + if (ImGui::MenuItem("Create Empty Entity")) + { + // m_Context->CreateEntity("Empty Entity"); + } + ImGui::EndPopup(); + } + */ diff --git a/imgui.ini b/imgui.ini new file mode 100644 index 0000000..1f3c2d7 --- /dev/null +++ b/imgui.ini @@ -0,0 +1,59 @@ +[Window][WindowOverViewport_11111111] +Pos=0,0 +Size=1272,1386 +Collapsed=0 + +[Window][Statistics] +Pos=0,0 +Size=404,149 +Collapsed=0 +DockId=0x00000001,0 + +[Window][Debug##Default] +Pos=60,60 +Size=400,400 +Collapsed=0 + +[Window][Square settings] +Pos=1037,307 +Size=235,1079 +Collapsed=0 +DockId=0x00000008,0 + +[Window][Viewport] +Pos=406,0 +Size=629,1386 +Collapsed=0 +DockId=0x00000006,0 + +[Window][Dear ImGui Demo] +Pos=1037,0 +Size=235,1386 +Collapsed=0 +DockId=0x00000007,0 + +[Window][Scene] +Pos=0,151 +Size=404,264 +Collapsed=0 +DockId=0x00000009,0 + +[Window][Properties] +Pos=0,417 +Size=404,969 +Collapsed=0 +DockId=0x0000000A,0 + +[Docking][Data] +DockSpace ID=0x08BD597D Window=0x1BBC0F80 Pos=0,0 Size=1272,1386 Split=X + DockNode ID=0x00000003 Parent=0x08BD597D SizeRef=1035,1386 Split=X + DockNode ID=0x00000005 Parent=0x00000003 SizeRef=404,1386 Split=Y Selected=0x553E127E + DockNode ID=0x00000001 Parent=0x00000005 SizeRef=274,149 Selected=0x553E127E + DockNode ID=0x00000002 Parent=0x00000005 SizeRef=274,1235 Split=Y Selected=0xE601B12F + DockNode ID=0x00000009 Parent=0x00000002 SizeRef=498,264 Selected=0xE601B12F + DockNode ID=0x0000000A Parent=0x00000002 SizeRef=498,969 Selected=0x8C72BEA8 + DockNode ID=0x00000006 Parent=0x00000003 SizeRef=629,1386 CentralNode=1 Selected=0xC450F867 + DockNode ID=0x00000004 Parent=0x08BD597D SizeRef=235,1386 Split=Y Selected=0x5E5F7166 + DockNode ID=0x00000007 Parent=0x00000004 SizeRef=235,305 Selected=0x5E5F7166 + DockNode ID=0x00000008 Parent=0x00000004 SizeRef=235,1079 Selected=0xA5FF3A7A + diff --git a/licenses/entt.md b/licenses/entt.md new file mode 100644 index 0000000..f032bd2 --- /dev/null +++ b/licenses/entt.md @@ -0,0 +1,24 @@ +This software uses the EnTT library. +The EnTT library is used under the MIT License, see bellow. + +The MIT License (MIT) + +Copyright (c) 2017-2026 Michele Caini, author of EnTT + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copy of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copy or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/licenses/fmt.md b/licenses/fmt.md new file mode 100644 index 0000000..795fdae --- /dev/null +++ b/licenses/fmt.md @@ -0,0 +1,20 @@ +Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/open_engine/include/open_engine.hpp b/open_engine/include/open_engine.hpp index bdd2244..77dd89e 100644 --- a/open_engine/include/open_engine.hpp +++ b/open_engine/include/open_engine.hpp @@ -2,8 +2,10 @@ #define OPEN_ENGINE_HPP #include "open_engine/orthographic_camera_controller.hpp" +#include "open_engine/scene/native_scriptable_entity.hpp" #include "open_engine/imgui/imgui_layer.hpp" #include "open_engine/events/key_event.hpp" +#include "open_engine/scene/components.hpp" #include "open_engine/application.hpp" #include "open_engine/ref_scope.hpp" #include "open_engine/logging.hpp" @@ -21,7 +23,9 @@ #include "open_engine/renderer/renderer2d.hpp" #include "open_engine/renderer/renderer.hpp" #include "open_engine/renderer/texture.hpp" +#include "open_engine/scene/components.hpp" #include "open_engine/renderer/buffer.hpp" #include "open_engine/renderer/shader.hpp" - +#include "open_engine/scene/entity.hpp" +#include "open_engine/scene/scene.hpp" #endif // OPEN_ENGINE_HPP diff --git a/open_engine/include/open_engine/application.hpp b/open_engine/include/open_engine/application.hpp index efb6ac1..666e8cb 100644 --- a/open_engine/include/open_engine/application.hpp +++ b/open_engine/include/open_engine/application.hpp @@ -22,6 +22,7 @@ namespace OpenEngine { void QueueLayerPop(Ref layer); void QueueOverlayPush(Ref layer); void QueueOverlayPop(Ref layer); + ImGuiLayer* GetImGuiLayer() { return imgui_layer.get(); }; inline static Application& Get() { return *instance; }; diff --git a/open_engine/include/open_engine/core.hpp b/open_engine/include/open_engine/core.hpp index 6091ce0..1c1d25e 100644 --- a/open_engine/include/open_engine/core.hpp +++ b/open_engine/include/open_engine/core.hpp @@ -14,6 +14,7 @@ #define BIT(x) (1 << x) -#define BIND_EVENT_FN(function) std::bind(&function, this, std::placeholders::_1) +//#define BIND_EVENT_FN(function) std::bind(&function, this, std::placeholders::_1) +#define BIND_EVENT_FN(fn) [this](auto&&...args) -> decltype(auto) { return this->fn(std::forward(args)...); } #endif // CORE_HPP diff --git a/open_engine/include/open_engine/imgui/imgui_layer.hpp b/open_engine/include/open_engine/imgui/imgui_layer.hpp index 7f8c452..176c5b1 100644 --- a/open_engine/include/open_engine/imgui/imgui_layer.hpp +++ b/open_engine/include/open_engine/imgui/imgui_layer.hpp @@ -1,6 +1,7 @@ #ifndef IMGUI_LAYER_HPP #define IMGUI_LAYER_HPP +#include "open_engine/events/key_event.hpp" #include "open_engine/events/event.hpp" #include "open_engine/layer.hpp" @@ -15,11 +16,15 @@ namespace OpenEngine { virtual void OnDetach() override; virtual void OnImGuiRender() override; virtual void OnEvent(Event& event) override; + bool CloseWindow(OpenEngine::KeyPressedEvent& event); + + void SetBlockEvents(bool block) { block_events = block; }; void Begin(); void End(); private: + bool block_events = false; float previous_frame_time = 0.0f; }; } diff --git a/open_engine/include/open_engine/orthographic_camera.hpp b/open_engine/include/open_engine/orthographic_camera.hpp index d70b4f0..ded36bf 100644 --- a/open_engine/include/open_engine/orthographic_camera.hpp +++ b/open_engine/include/open_engine/orthographic_camera.hpp @@ -1,5 +1,5 @@ -#ifndef CAMERA_HPP -#define CAMERA_HPP +#ifndef ORTHOGRAPHIC_CAMERA_HPP +#define ORTHOGRAPHIC_CAMERA_HPP #include #include @@ -33,4 +33,4 @@ namespace OpenEngine { }; } -#endif // CAMERA_HPP +#endif // ORTHOGRAPHIC_CAMERA_HPP diff --git a/open_engine/include/open_engine/pch.hpp b/open_engine/include/open_engine/pch.hpp index 2d2b9cc..5708cfd 100644 --- a/open_engine/include/open_engine/pch.hpp +++ b/open_engine/include/open_engine/pch.hpp @@ -7,6 +7,7 @@ #include #include +#include #include #include diff --git a/open_engine/include/open_engine/renderer/camera.hpp b/open_engine/include/open_engine/renderer/camera.hpp new file mode 100644 index 0000000..0391bba --- /dev/null +++ b/open_engine/include/open_engine/renderer/camera.hpp @@ -0,0 +1,26 @@ +#ifndef CAMERA_HPP +#define CAMERA_HPP + +#include + +namespace OpenEngine { + + class Camera + { + public: + Camera() = default; + ~Camera() = default; + Camera(const glm::mat4& projection) + : projection(projection) + { + }; + + const glm::mat4& GetProjection() const { return projection; }; + + protected: + glm::mat4 projection = glm::mat4(1.0f); + }; + +} + +#endif // CAMERA_HPP diff --git a/open_engine/include/open_engine/renderer/renderer2d.hpp b/open_engine/include/open_engine/renderer/renderer2d.hpp index f66f024..8b09c26 100644 --- a/open_engine/include/open_engine/renderer/renderer2d.hpp +++ b/open_engine/include/open_engine/renderer/renderer2d.hpp @@ -1,9 +1,10 @@ #ifndef RENDERER2D_HPP #define RENDERER2D_HPP -#include "open_engine/orthographic_camera.hpp" #include "open_engine/renderer/subtexture2d.hpp" +#include "open_engine/orthographic_camera.hpp" #include "open_engine/renderer/texture.hpp" +#include "open_engine/renderer/camera.hpp" #include "open_engine/ref_scope.hpp" #include @@ -32,6 +33,7 @@ namespace OpenEngine { static void Shutdown(); static void BeginScene(const OrthographicCamera& camera); + static void BeginScene(const Camera& camera, const glm::mat4& transform); static void EndScene(); static void Flush(); @@ -39,6 +41,10 @@ namespace OpenEngine { static void DrawQuad(const Transform& transform_data, const Ref& texture, float tiling_factor = 1.0f); static void DrawQuad(const Transform& transform_data, const Ref& subtexture, float tiling_factor = 1.0f); + static void DrawQuad(const glm::mat4& transform, const glm::vec4& color); + static void DrawQuad(const glm::mat4& transform, const Ref& texture, float tiling_factor = 1.0f); + static void DrawQuad(const glm::mat4& transform, const Ref& subtexture, float tiling_factor = 1.0f); + static void ResetStats(); static const Statistics& GetStats(); diff --git a/open_engine/include/open_engine/scene/components.hpp b/open_engine/include/open_engine/scene/components.hpp new file mode 100644 index 0000000..d94c1e5 --- /dev/null +++ b/open_engine/include/open_engine/scene/components.hpp @@ -0,0 +1,151 @@ +#ifndef COMPONENTS_HPP +#define COMPONENTS_HPP + +#include "open_engine/scene/native_scriptable_entity.hpp" +#include "open_engine/scene/scene_camera.hpp" + +#include +#include +#include +#include +#include + +namespace OpenEngine { + + struct TagComponent + { + std::string tag; + + TagComponent() = default; + TagComponent(const TagComponent&) = default; + TagComponent(const std::string& tag) + : tag(tag) {} + + void OnImGuiRender(Entity& entity) + { + char buffer[256]; + std::memset(buffer, 0, sizeof(buffer)); + std::strncpy(buffer, tag.c_str(), sizeof(buffer)); + + ImGui::Text("Name"); + if (ImGui::InputText("##tagEditor", buffer, sizeof(buffer), ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_AutoSelectAll) + || (!ImGui::IsItemActive() && ImGui::IsMouseClicked(0))) + tag = buffer; + }; + }; + + struct TransformComponent + { + glm::mat4 transform{ 1.0f }; + + TransformComponent() = default; + TransformComponent(const TransformComponent&) = default; + TransformComponent(const glm::mat4& transform_mat) + : transform(transform_mat) {} + + operator glm::mat4() { return transform; }; + operator const glm::mat4() const { return transform; }; + + void OnImGuiRender(Entity& entity) + { + ImGui::DragFloat3("Position", glm::value_ptr(transform[3]), 0.1f); + }; + }; + + struct SpriteRendererComponent + { + glm::vec4 color{ 1.0f, 1.0f, 1.0f, 1.0f }; + + SpriteRendererComponent() = default; + SpriteRendererComponent(const SpriteRendererComponent&) = default; + SpriteRendererComponent(const glm::vec4& color) + : color(color) {} + + void OnImGuiRender(Entity& entity) + { + ImGui::ColorEdit4("color", glm::value_ptr(color)); + }; + }; + + struct CameraComponent + { + SceneCamera camera; + bool primary = true; + bool fixed_aspect_ratio = false; + + CameraComponent() = default; + CameraComponent(const CameraComponent&) = default; + + void OnImGuiRender(Entity& entity) + { + ImGui::Checkbox("Is primary", &primary); + const char* projection_type_strings[] = {"Perspective", "Orthographic"}; + const char* current_projection_type = projection_type_strings[(int)camera.GetProjectionType()]; + + if (ImGui::BeginCombo("Projection", current_projection_type)) { + for (int i = 0; i < 2; i++) { + bool is_selected = current_projection_type == projection_type_strings[i]; + if (ImGui::Selectable(projection_type_strings[i], is_selected)) { + current_projection_type = projection_type_strings[i]; + camera.SetProjectionType((SceneCamera::ProjectionType)i); + } + + if (is_selected) + ImGui::SetItemDefaultFocus(); + } + ImGui::EndCombo(); + } + + if (camera.GetProjectionType() == SceneCamera::ProjectionType::Perspective) { + float fov = camera.GetVerticalFov(); + if (ImGui::DragFloat("Fov", &fov, 0.1f)) + camera.SetVerticalFov(fov); + + float near_clip = camera.GetPerspectiveNearClip(); + if (ImGui::DragFloat("Near clip", &near_clip, 0.1f)) + camera.SetPerspectiveNearClip(near_clip); + + float far_clip = camera.GetPerspectiveFarClip(); + if (ImGui::DragFloat("Far clip", &far_clip, 0.1f)) + camera.SetPerspectiveFarClip(far_clip); + } + else if (camera.GetProjectionType() == SceneCamera::ProjectionType::Orthographic) { + ImGui::Checkbox("Has fixed aspect ratio", &fixed_aspect_ratio); + float ortho_size = camera.GetOrthographicSize(); + if (ImGui::DragFloat("Orthographic size", &ortho_size, 0.1f)) + camera.SetOrthographicSize(ortho_size); + + float near_clip = camera.GetOrthographicNearClip(); + if (ImGui::DragFloat("Near clip", &near_clip, 0.1f)) + camera.SetOrthographicNearClip(near_clip); + + float far_clip = camera.GetOrthographicFarClip(); + if (ImGui::DragFloat("Far clip", &far_clip, 0.1f)) + camera.SetOrthographicFarClip(far_clip); + } + + }; + }; + + struct NativeScriptComponent + { + NativeScriptableEntity* instance = nullptr; + + NativeScriptableEntity* (*InstanciateScript)(); + void (*DestroyInstanceScript)(NativeScriptComponent*); + + template + void Bind() + { + InstanciateScript = []() { return static_cast(new T()); }; + DestroyInstanceScript = [](NativeScriptComponent* nsc) + { + (delete nsc->instance); + nsc->instance = nullptr; + }; + } + }; + +} + +#endif // COMPONENTS_HPP diff --git a/open_engine/include/open_engine/scene/entity.hpp b/open_engine/include/open_engine/scene/entity.hpp new file mode 100644 index 0000000..2b690f0 --- /dev/null +++ b/open_engine/include/open_engine/scene/entity.hpp @@ -0,0 +1,64 @@ +#ifndef ENTITY_HPP +#define ENTITY_HPP + +#include "open_engine/core.hpp" + +#include "open_engine/scene/scene.hpp" + +#include +#include + +namespace OpenEngine { + class Entity + { + public: + Entity() = default; + Entity(entt::entity handle, Scene* scene); + Entity(const Entity& other) = default; + + template + T& AddComponents(Args&&... args) + { + OE_ASSERT(!HasComponent(), "Entity already has component."); + return scene->registry.emplace(handle, std::forward(args)...); + }; + + template + T& GetComponents(Args&&... args) + { + OE_ASSERT(HasComponent(), "Entity doesn't have component."); + return scene->registry.get(handle); + }; + + template + void RemoveComponents() + { + OE_ASSERT(HasComponent(), "Entity doesn't have component."); + scene->registry.remove(handle); + }; + + template + bool HasComponent() + { + return scene->registry.all_of(handle); + }; + + operator bool() const { return handle != entt::null; }; + operator uint32_t() const { return (uint32_t)handle; }; + + bool operator ==(const Entity& other) const { + return handle == other.handle && scene == other.scene; + }; + + bool operator !=(const Entity& other) const { + return !operator==(other); + }; + + private: + entt::entity handle { entt::null }; + OpenEngine::Scene* scene = nullptr; + + }; +} + +#endif // ENTITY_HPP diff --git a/open_engine/include/open_engine/scene/native_scriptable_entity.hpp b/open_engine/include/open_engine/scene/native_scriptable_entity.hpp new file mode 100644 index 0000000..1671077 --- /dev/null +++ b/open_engine/include/open_engine/scene/native_scriptable_entity.hpp @@ -0,0 +1,27 @@ +#ifndef NATIVE_SCRIPTABLE_ENTITY_HPP +#define NATIVE_SCRIPTABLE_ENTITY_HPP + +#include "open_engine/scene/entity.hpp" + +namespace OpenEngine { + + class NativeScriptableEntity + { + public: + virtual ~NativeScriptableEntity() = default; + template + T& GetComponent() { return entity.GetComponents(); }; + + protected: + virtual void OnCreate() {}; + virtual void OnUpdate() {}; + virtual void OnDestroy() {}; + + private: + Entity entity; + + friend class Scene; + }; +} + +#endif // NATIVE_SCRIPTABLE_ENTITY_HPP diff --git a/open_engine/include/open_engine/scene/scene.hpp b/open_engine/include/open_engine/scene/scene.hpp new file mode 100644 index 0000000..59c0e36 --- /dev/null +++ b/open_engine/include/open_engine/scene/scene.hpp @@ -0,0 +1,33 @@ +#ifndef SCENE_HPP +#define SCENE_HPP + +#include +#include + +namespace OpenEngine { + + class Entity; + + class Scene + { + public: + Scene() {}; + ~Scene() = default; + + Entity CreateEntity(const std::string& name = std::string()); + + void OnUpdate(); + void OnViewportResize(uint32_t width, uint32_t height); + + entt::registry& GetRegistry() { return registry; }; + + private: + entt::registry registry; + + uint32_t viewport_width = 0, viewport_height = 0; + + friend class Entity; + }; +} + +#endif // SCENE_HPP diff --git a/open_engine/include/open_engine/scene/scene_camera.hpp b/open_engine/include/open_engine/scene/scene_camera.hpp new file mode 100644 index 0000000..026275f --- /dev/null +++ b/open_engine/include/open_engine/scene/scene_camera.hpp @@ -0,0 +1,74 @@ +#ifndef SCENE_CAMERA_HPP +#define SCENE_CAMERA_HPP + +#include "open_engine/renderer/camera.hpp" +#include + +namespace OpenEngine { + class SceneCamera : public Camera + { + public: + enum class ProjectionType { Perspective = 0, Orthographic = 1 }; + public: + SceneCamera(); + virtual ~SceneCamera() = default; + + void SetViewportSize(uint32_t width, uint32_t height); + + ProjectionType GetProjectionType() { return projection_type; }; + void SetProjectionType(ProjectionType type) { + projection_type = type; + RecalculateProjection(); + }; + + void SetOrthographic(float size, float near_clip, float far_clip); + void SetPerspective(float fov, float near_clip, float far_clip); + + float GetOrthographicSize() const { return orthographic_size; } + void SetOrthographicSize(float size) { + orthographic_size = size; + RecalculateProjection(); + }; + float GetOrthographicNearClip() const { return orthographic_near; }; + void SetOrthographicNearClip(float near) { + orthographic_near = near; + RecalculateProjection(); + }; + float GetOrthographicFarClip() const { return orthographic_far; }; + void SetOrthographicFarClip(float far) { + orthographic_far = far; + RecalculateProjection(); + }; + + float GetVerticalFov() const { return fov; } + void SetVerticalFov(float fov_value) { + fov = fov_value; + RecalculateProjection(); + }; + float GetPerspectiveNearClip() const { return perspective_near; }; + void SetPerspectiveNearClip(float near) { + perspective_near = near; + RecalculateProjection(); + }; + float GetPerspectiveFarClip() const { return perspective_far; }; + void SetPerspectiveFarClip(float far) { + perspective_far = far; + RecalculateProjection(); + }; + + private: + void RecalculateProjection(); + + private: + ProjectionType projection_type = ProjectionType::Orthographic; + float orthographic_size = 10.0f; + float orthographic_near = -1.0f, orthographic_far = 1.0f; + + float fov = 45.0f; + float perspective_near = 0.01f, perspective_far = 1000.0f; + + float aspect_ratio = 0.0f; + }; +} + +#endif // SCENE_CAMERA_HPP diff --git a/open_engine/src/open_engine/imgui/imgui_layer.cpp b/open_engine/src/open_engine/imgui/imgui_layer.cpp index 16822cf..1b94f24 100644 --- a/open_engine/src/open_engine/imgui/imgui_layer.cpp +++ b/open_engine/src/open_engine/imgui/imgui_layer.cpp @@ -1,14 +1,14 @@ -#include "imgui.h" -#include "instrumentor.hpp" #include #include #include #include +#include #include #include +#include "imgui.h" #include #include @@ -340,10 +340,10 @@ namespace OpenEngine { void ImGuiLayer::OnEvent(Event& event) { - /* - ImGuiIO& io = ImGui::GetIO(); - event.handled |= event.IsInCategory(EventCategoryMouse) & io.WantCaptureMouse; - event.handled |= event.IsInCategory(EventCategoryKeyboard) & io.WantCaptureKeyboard; - */ + if (block_events) { + ImGuiIO& io = ImGui::GetIO(); + event.handled |= event.IsInCategory(EventCategoryMouse) & io.WantCaptureMouse; + event.handled |= event.IsInCategory(EventCategoryKeyboard) & io.WantCaptureKeyboard; + } } } diff --git a/open_engine/src/open_engine/opengl/opengl_framebuffer.cpp b/open_engine/src/open_engine/opengl/opengl_framebuffer.cpp index cbe08c7..0eb322e 100644 --- a/open_engine/src/open_engine/opengl/opengl_framebuffer.cpp +++ b/open_engine/src/open_engine/opengl/opengl_framebuffer.cpp @@ -1,3 +1,4 @@ +#include "logging.hpp" #include #include @@ -6,6 +7,7 @@ #include namespace OpenEngine { + static const uint32_t max_framebuffer_size = 8192; OpenGLFramebuffer::OpenGLFramebuffer(const FramebufferSpecification& spec) : specs(spec) { @@ -61,6 +63,10 @@ namespace OpenEngine { void OpenGLFramebuffer::Resize(uint32_t width, uint32_t height) { + if (width == 0 || height == 0 || width > max_framebuffer_size || height > max_framebuffer_size) { + OE_CORE_WARN("Attempted to resize framebuffer to {} {}", width, height); + return; + } specs.width = width; specs.height = height; diff --git a/open_engine/src/open_engine/renderer/camera.cpp b/open_engine/src/open_engine/renderer/camera.cpp new file mode 100644 index 0000000..4482aad --- /dev/null +++ b/open_engine/src/open_engine/renderer/camera.cpp @@ -0,0 +1,7 @@ +#include + +#include + +namespace OpenEngine { + +} diff --git a/open_engine/src/open_engine/renderer/renderer2d.cpp b/open_engine/src/open_engine/renderer/renderer2d.cpp index 6bf5cc2..e276df8 100755 --- a/open_engine/src/open_engine/renderer/renderer2d.cpp +++ b/open_engine/src/open_engine/renderer/renderer2d.cpp @@ -7,10 +7,11 @@ #include #include -#include -#include -#include +#include #include +#include +#include +#include namespace OpenEngine { struct QuadVertex @@ -135,6 +136,21 @@ namespace OpenEngine { renderer_data.texture_slot_index = 1; } + void Renderer2D::BeginScene(const Camera& camera, const glm::mat4& transform) + { + OE_PROFILE_FUNCTION(); + + glm::mat4 view_projection = camera.GetProjection() * glm::inverse(transform); + + renderer_data.texture_shader->Bind(); + renderer_data.texture_shader->SetMat4("u_ViewProjection", view_projection); + + renderer_data.quad_index_count = 0; + renderer_data.quad_vertex_ptr = renderer_data.quad_vertex_base; + + renderer_data.texture_slot_index = 1; + } + void Renderer2D::EndScene() { OE_PROFILE_FUNCTION(); @@ -196,17 +212,7 @@ namespace OpenEngine { * glm::rotate(glm::mat4(1.0f), glm::radians(transform_data.rotation), { 0.0f, 0.0f, 1.0f }) * glm::scale(glm::mat4(1.0f), transform_data.size); - for (size_t i = 0; i < quad_vertex_count; i++) { - renderer_data.quad_vertex_ptr->position = transform * renderer_data.quad_vertex_positioning[i]; - renderer_data.quad_vertex_ptr->color = color; - renderer_data.quad_vertex_ptr->tex_coord = texture_coords[i]; - renderer_data.quad_vertex_ptr->tex_index = texture_index; - renderer_data.quad_vertex_ptr->tiling_factor = tiling_factor; - renderer_data.quad_vertex_ptr++; - } - - renderer_data.quad_index_count += 6; - renderer_data.stats.quad_count++; + DrawQuad(transform, texture); } void Renderer2D::DrawQuad(const Transform& transform_data, const Ref& subtexture, float tiling_factor) @@ -246,17 +252,7 @@ namespace OpenEngine { * glm::rotate(glm::mat4(1.0f), glm::radians(transform_data.rotation), { 0.0f, 0.0f, 1.0f }) * glm::scale(glm::mat4(1.0f), transform_data.size); - for (size_t i = 0; i < quad_vertex_count; i++) { - renderer_data.quad_vertex_ptr->position = transform * renderer_data.quad_vertex_positioning[i]; - renderer_data.quad_vertex_ptr->color = color; - renderer_data.quad_vertex_ptr->tex_coord = texture_coords[i]; - renderer_data.quad_vertex_ptr->tex_index = texture_index; - renderer_data.quad_vertex_ptr->tiling_factor = tiling_factor; - renderer_data.quad_vertex_ptr++; - } - - renderer_data.quad_index_count += 6; - renderer_data.stats.quad_count++; + DrawQuad(transform, subtexture); } void Renderer2D::DrawQuad(const Transform& transform_data, const glm::vec4& color) @@ -277,6 +273,104 @@ namespace OpenEngine { * glm::scale(glm::mat4(1.0f), transform_data.size); + DrawQuad(transform, color); + } + + void Renderer2D::DrawQuad(const glm::mat4& transform, const Ref& texture, float tiling_factor) + { + OE_PROFILE_FUNCTION(); + + constexpr size_t quad_vertex_count = 4; + constexpr glm::vec2 texture_coords[] = { { 0.0f, 0.0f }, { 1.0f, 0.0f }, { 1.0f, 1.0f }, { 0.0f, 1.0f }}; + + if (renderer_data.quad_index_count >= Renderer2DData::max_indices) + FlushAndReset(); + + constexpr glm::vec4 color = glm::vec4(1.0f); + + float texture_index = 0; + for (uint32_t i = 1; i < renderer_data.texture_slot_index; i++) { + if (*renderer_data.texture_slots[i].get() == *texture.get()) { + texture_index = (float)i; + break; + } + } + + if (texture_index == 0) { + if (renderer_data.texture_slot_index >= Renderer2DData::max_texture_slots) + FlushAndReset(); + + texture_index = (float)renderer_data.texture_slot_index; + renderer_data.texture_slots[renderer_data.texture_slot_index] = texture; + renderer_data.texture_slot_index++; + } + + for (size_t i = 0; i < quad_vertex_count; i++) { + renderer_data.quad_vertex_ptr->position = transform * renderer_data.quad_vertex_positioning[i]; + renderer_data.quad_vertex_ptr->color = color; + renderer_data.quad_vertex_ptr->tex_coord = texture_coords[i]; + renderer_data.quad_vertex_ptr->tex_index = texture_index; + renderer_data.quad_vertex_ptr->tiling_factor = tiling_factor; + renderer_data.quad_vertex_ptr++; + } + + renderer_data.quad_index_count += 6; + renderer_data.stats.quad_count++; + } + + void Renderer2D::DrawQuad(const glm::mat4& transform, const Ref& subtexture, float tiling_factor) + { + OE_PROFILE_FUNCTION(); + + constexpr size_t quad_vertex_count = 4; + const glm::vec2* texture_coords = subtexture->GetCoords(); + const Ref& texture = subtexture->GetTexture(); + + if (renderer_data.quad_index_count >= Renderer2DData::max_indices) + FlushAndReset(); + + constexpr glm::vec4 color = glm::vec4(1.0f); + + float texture_index = 0; + for (uint32_t i = 1; i < renderer_data.texture_slot_index; i++) { + if (*renderer_data.texture_slots[i].get() == *texture.get()) { + texture_index = (float)i; + break; + } + } + + if (texture_index == 0) { + if (renderer_data.texture_slot_index >= Renderer2DData::max_texture_slots) + FlushAndReset(); + + texture_index = (float)renderer_data.texture_slot_index; + renderer_data.texture_slots[renderer_data.texture_slot_index] = texture; + renderer_data.texture_slot_index++; + } + + for (size_t i = 0; i < quad_vertex_count; i++) { + renderer_data.quad_vertex_ptr->position = transform * renderer_data.quad_vertex_positioning[i]; + renderer_data.quad_vertex_ptr->color = color; + renderer_data.quad_vertex_ptr->tex_coord = texture_coords[i]; + renderer_data.quad_vertex_ptr->tex_index = texture_index; + renderer_data.quad_vertex_ptr->tiling_factor = tiling_factor; + renderer_data.quad_vertex_ptr++; + } + + renderer_data.quad_index_count += 6; + renderer_data.stats.quad_count++; + } + + void Renderer2D::DrawQuad(const glm::mat4& transform, const glm::vec4& color) + { + OE_PROFILE_FUNCTION(); + + constexpr size_t quad_vertex_count = 4; + constexpr glm::vec2 texture_coords[] = { { 0.0f, 0.0f }, { 1.0f, 0.0f }, { 1.0f, 1.0f }, { 0.0f, 1.0f }}; + + if (renderer_data.quad_index_count >= Renderer2DData::max_indices) + FlushAndReset(); + for (size_t i = 0; i < quad_vertex_count; i++) { renderer_data.quad_vertex_ptr->position = transform * renderer_data.quad_vertex_positioning[i]; renderer_data.quad_vertex_ptr->color = color; @@ -290,6 +384,7 @@ namespace OpenEngine { renderer_data.stats.quad_count++; } + void Renderer2D::ResetStats() { memset(&renderer_data.stats, 0, sizeof(Statistics)); diff --git a/open_engine/src/open_engine/scene/camera.cpp b/open_engine/src/open_engine/scene/camera.cpp new file mode 100755 index 0000000..7d19685 --- /dev/null +++ b/open_engine/src/open_engine/scene/camera.cpp @@ -0,0 +1,60 @@ +#include +#include + +#include +#include + +namespace OpenEngine { + SceneCamera::SceneCamera() + { + RecalculateProjection(); + } + + void SceneCamera::SetOrthographic(float size, float near_clip, float far_clip) + { + projection_type = ProjectionType::Orthographic; + orthographic_size = size; + orthographic_near = near_clip; + orthographic_far = far_clip; + + RecalculateProjection(); + } + + void SceneCamera::SetPerspective(float fov_value, float near_clip, float far_clip) + { + projection_type = ProjectionType::Perspective; + fov = fov_value; + perspective_near = near_clip; + perspective_far = far_clip; + + RecalculateProjection(); + } + + void SceneCamera::SetViewportSize(uint32_t width, uint32_t height) + { + aspect_ratio = (float)width / (float)height; + + RecalculateProjection(); + } + + void SceneCamera::RecalculateProjection() + { + + if (projection_type == ProjectionType::Orthographic) { + float ortho_left = -orthographic_size * aspect_ratio * 0.5; + float ortho_right = orthographic_size * aspect_ratio * 0.5; + float ortho_bottom = orthographic_size * -0.5; + float ortho_top = orthographic_size * 0.5; + + projection = glm::ortho( + ortho_left, ortho_right, + ortho_bottom, ortho_top, + orthographic_near, orthographic_far); + } + else if (projection_type == ProjectionType::Perspective) { + projection = glm::perspective( + glm::radians(fov), aspect_ratio, + perspective_near, perspective_far); + } + } +} diff --git a/open_engine/src/open_engine/scene/entity.cpp b/open_engine/src/open_engine/scene/entity.cpp new file mode 100644 index 0000000..fbf7a4e --- /dev/null +++ b/open_engine/src/open_engine/scene/entity.cpp @@ -0,0 +1,10 @@ +#include + +#include + +namespace OpenEngine { + Entity::Entity(entt::entity handle, Scene* scene) + : handle(handle), scene(scene) + { + } +} diff --git a/open_engine/src/open_engine/scene/native_scriptable_entity.cpp b/open_engine/src/open_engine/scene/native_scriptable_entity.cpp new file mode 100644 index 0000000..3fb58b3 --- /dev/null +++ b/open_engine/src/open_engine/scene/native_scriptable_entity.cpp @@ -0,0 +1 @@ +#include diff --git a/open_engine/src/open_engine/scene/scene.cpp b/open_engine/src/open_engine/scene/scene.cpp new file mode 100644 index 0000000..36834cc --- /dev/null +++ b/open_engine/src/open_engine/scene/scene.cpp @@ -0,0 +1,78 @@ +#include + +#include +#include + +#include +#include + +namespace OpenEngine { + Entity Scene::CreateEntity(const std::string& name) + { + Entity entity = { registry.create(), this }; + + auto& tag = entity.AddComponents(); + tag.tag = name.empty() ? "Entity" : name; + + return entity; + } + + void Scene::OnUpdate() + { + { + OE_PROFILE_SCOPE("Updating Sripts"); + + registry.view().each([=, this](auto entity, auto& nsc) + { + if (!nsc.instance) { + nsc.instance = nsc.InstanciateScript(); + nsc.instance->entity = Entity{ entity, this }; + nsc.instance->OnCreate(); + } + + nsc.instance->OnUpdate(); + }); + } + + Camera* main_camera = nullptr; + glm::mat4* main_transform = nullptr; + { + auto camera_view = registry.view(); + for (auto entity : camera_view) { + auto [camera_component, transform] = camera_view.get(entity); + + if (camera_component.primary) { + main_camera = &camera_component.camera; + main_transform = &transform.transform; + break; + } + } + + if (main_camera) { + Renderer2D::BeginScene(main_camera->GetProjection(), *main_transform); + + auto view = registry.view(); + + for (const auto& entity : view) { + auto [transform, sprite] = view.get(entity); + Renderer2D::DrawQuad(transform.transform, sprite.color); + } + + Renderer2D::EndScene(); + } + } + } + + void Scene::OnViewportResize(uint32_t width, uint32_t height) + { + viewport_width = width; + viewport_height = height; + + auto view = registry.view(); + for (auto entity : view) { + auto& camera_comp = view.get(entity); + if (!camera_comp.fixed_aspect_ratio) + camera_comp.camera.SetViewportSize(width, height); + } + } +}