diff --git a/application/include/sandbox2d.hpp b/application/include/sandbox2d.hpp index 5a149d7..405224d 100755 --- a/application/include/sandbox2d.hpp +++ b/application/include/sandbox2d.hpp @@ -5,6 +5,8 @@ #include #include "imgui.h" +#include "open_engine/core/time.hpp" +#include "open_engine/logging.hpp" #include "open_engine/renderer/renderer2d.hpp" #include @@ -49,13 +51,20 @@ class Sandbox2DLayer : public OpenEngine::Layer OpenEngine::RenderCommand::Clear(); OpenEngine::Renderer2D::BeginScene(camera.GetCamera()); } + static float angle = 0.0f; + float ts = OpenEngine::Time::DeltaTime(); + + if (angle >= 360) + angle = 0; + + angle += (ts * 500.0f); { OE_PROFILE_SCOPE("Drawing Quads"); - OpenEngine::Transform tr1 = {glm::vec3(0.5f, 0.5f, -0.2f), glm::vec3(0.3f), 20.0f}; - OpenEngine::Transform tr2 = {glm::vec3(-0.2f, -0.2f, -0.1f), glm::vec3(0.5f, 0.2f, 1.0f), 10.0f}; - OpenEngine::Transform tr3 = {glm::vec3(0.0f, 0.0f, 0.1f), glm::vec3(1.0f, 1.0f, 1.0f), 45.0f}; + OpenEngine::Transform tr1 = {glm::vec3(0.5f, 0.5f, 0.2f), glm::vec3(0.3f), 20.0f}; + OpenEngine::Transform tr2 = {glm::vec3(-0.2f, -0.2f, 0.1f), glm::vec3(0.5f, 0.2f, 1.0f), angle}; + OpenEngine::Transform tr3 = {glm::vec3(0.0f, 0.0f, -0.1f), glm::vec3(1.0f, 1.0f, 1.0f), 45.0f}; - OpenEngine::Renderer2D::DrawQuad({glm::vec3(0.5f, 0.5f, 1.0f), glm::vec3(0.3f), 20.0f}, glm::vec4(color[0], color[1], color[2], color[3])); + OpenEngine::Renderer2D::DrawQuad({glm::vec3(0.5f, 0.5f, 0.0f), glm::vec3(0.3f), 20.0f}, glm::vec4(color[0], color[1], color[2], color[3])); OpenEngine::Renderer2D::DrawQuad(tr2, {0.5f, 0.3f, 0.8f, 1.0f}); OpenEngine::Renderer2D::DrawQuad(tr3, face); } diff --git a/application/include/shmup.hpp b/application/include/shmup.hpp index 1e80f46..2dc94dc 100755 --- a/application/include/shmup.hpp +++ b/application/include/shmup.hpp @@ -193,8 +193,8 @@ class Shmup : public OpenEngine::Layer OE_PROFILE_FUNCTION() { OE_PROFILE_SCOPE("Setting up Rendering"); - //OpenEngine::RenderCommand::SetClearColor({0.11f, 0.11f, 0.15f, 1.0f}); - OpenEngine::RenderCommand::SetClearColor({1.0f, 0.11f, 0.15f, 1.0f}); + OpenEngine::RenderCommand::SetClearColor({0.11f, 0.11f, 0.15f, 1.0f}); + //OpenEngine::RenderCommand::SetClearColor({1.0f, 0.11f, 0.15f, 1.0f}); OpenEngine::RenderCommand::Clear(); OpenEngine::Renderer2D::BeginScene(camera.GetCamera()); } diff --git a/editor/include/control_layer.hpp b/editor/include/control_layer.hpp new file mode 100644 index 0000000..9924a9e --- /dev/null +++ b/editor/include/control_layer.hpp @@ -0,0 +1,27 @@ +#ifndef LAYER_SWITCHER_HPP +#define LAYER_SWITCHER_HPP + +#include + +class ControlLayer : public OpenEngine::Layer +{ + public: + ControlLayer(OpenEngine::Ref layer); + ControlLayer(); + ~ControlLayer() = default; + + void OnUpdate() override; + void OnEvent(OpenEngine::Event& event) override; + void OnImGuiRender() override; + void OnAttach() override; + void OnDetach() override; + + private: + bool SwitchLayer(OpenEngine::KeyPressedEvent& event); + bool SwitchExistingLayer(OpenEngine::KeyPressedEvent& event); + bool StopRunning(OpenEngine::KeyPressedEvent& event); + + OpenEngine::Ref active_layer; +}; + +#endif // LAYER_SWITCHER_HPP diff --git a/editor/include/editor.hpp b/editor/include/editor.hpp new file mode 100755 index 0000000..6701efa --- /dev/null +++ b/editor/include/editor.hpp @@ -0,0 +1,169 @@ +#ifndef EDITOR_HPP +#define EDITOR_HPP + +#include "open_engine/renderer/renderer2d.hpp" +#include + +#include +#include +#include +#include +#include +#include + +class EditorLayer : public OpenEngine::Layer +{ + public: + EditorLayer() + : OpenEngine::Layer("editor_layer"), + camera(1280.0f / 720.0f, 1.0f) + { + } + ~EditorLayer() {}; + + void OnAttach() override + { + OE_PROFILE_FUNCTION(); + { + OE_PROFILE_SCOPE("Texture2D Creation"); + face = OpenEngine::Texture2D::Create("assets/textures/awesomeface.png"); + } + + OpenEngine::FramebufferSpecification specs; + specs.width = 1280; + specs.height = 720; + + framebuffer = OpenEngine::FrameBuffer::Create(specs); + } + + void OnDetach() override + { + } + + void OnUpdate() override + { + + OpenEngine::Renderer2D::ResetStats(); + OE_PROFILE_FUNCTION() + { + camera.OnUpdate(); + framebuffer->Bind(); + + OE_PROFILE_SCOPE("Setting up Rendering"); + + OpenEngine::RenderCommand::SetClearColor({0.11f, 0.11f, 0.15f, 1.0f}); + OpenEngine::RenderCommand::Clear(); + OpenEngine::Renderer2D::BeginScene(camera.GetCamera()); + } + { + OE_PROFILE_SCOPE("Drawing Quads"); + OpenEngine::Transform tr1 = {glm::vec3(0.0f, 0.0f, -0.1f), glm::vec3(1.0f, 1.0f, 0.0f), angle}; + OpenEngine::Transform tr3 = {glm::vec3(-0.5f, -0.5f, 0.0f), glm::vec3(1.0f, 0.5f, 0.0f), angle * 0.5f}; + OpenEngine::Renderer2D::DrawQuad(tr1, face); + OpenEngine::Renderer2D::DrawQuad(tr3, rectangle_color); + } + + OpenEngine::Renderer2D::EndScene(); + + OpenEngine::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}; + OpenEngine::Renderer2D::DrawQuad({{x, y, 0.0f}, glm::vec3(0.45f, 0.45f, 0.0f)}, gradient_color); + + } + } + + OpenEngine::Renderer2D::EndScene(); + + framebuffer->Unbind(); + } + + void OnEvent(OpenEngine::Event& event) override + { + OE_PROFILE_FUNCTION(); + + { + OE_PROFILE_SCOPE("Camera OnEvent"); + camera.OnEvent(event); + } + } + + void OnImGuiRender() override + { + OE_PROFILE_FUNCTION(); + + ImGui::DockSpaceOverViewport(); + + ImGui::Begin("Square settings"); + + ImGui::SliderFloat("Angle", &angle, -360, 360); + ImGui::ColorPicker4("Rectangle color", &rectangle_color[0]); + + for (auto& result : profiling_results) + { + char label[50]; + strcpy(label, "%.3fms "); + strcat(label, result.name); + ImGui::Text(label, result.duration); + } + + profiling_results.clear(); + + 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 }; + + camera.OnResize(viewport_panel_size.x, viewport_panel_size.y); + } + uint32_t texture_id = framebuffer->GetColorAttachmentRendererID(); + ImGui::Image((void*)texture_id, ImVec2{ viewport_size.x, viewport_size.y }, ImVec2{ 0, 1 }, ImVec2{ 1, 0 }); + ImGui::End(); + ImGui::PopStyleVar(); + + auto stats = OpenEngine::Renderer2D::GetStats(); + static float time = 0; + + time += OpenEngine::Time::DeltaTime(); + + if (time >= 1) { + time = 0; + } + + 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::End(); + } + + private: + float angle = 0.0f; + OpenEngine::Ref face; + + OpenEngine::OrthographicCameraController camera; + + std::vector profiling_results; + + glm::vec2 viewport_size = { 0.0f, 0.0f }; + OpenEngine::Ref framebuffer; + glm::vec4 rectangle_color = {0.8f, 0.2f, 0.7f, 1.0f}; +}; + +class EditorApp : public OpenEngine::Application +{ + public: + EditorApp(); + ~EditorApp(); +}; + +#endif // EDITOR_HPP diff --git a/editor/include/modding.hpp b/editor/include/modding.hpp new file mode 100644 index 0000000..435f827 --- /dev/null +++ b/editor/include/modding.hpp @@ -0,0 +1,152 @@ +#ifndef MODDING_HPP +#define MODDING_HPP + +#include +#include +#include + +#include "/usr/share/dotnet/packs/Microsoft.NETCore.App.Host.arch-x64/10.0.0/runtimes/arch-x64/native/nethost.h" +#include "/usr/share/dotnet/packs/Microsoft.NETCore.App.Host.arch-x64/10.0.0/runtimes/arch-x64/native/hostfxr.h" +#include "/usr/share/dotnet/packs/Microsoft.NETCore.App.Host.arch-x64/10.0.0/runtimes/arch-x64/native/coreclr_delegates.h" + +class Modding : public OpenEngine::Layer +{ + public: + Modding() + : OpenEngine::Layer("modding") + { + }; + ~Modding() + { + }; + + void* get_export(void* sample_lib, const char* name) { + // dlsym is the Linux version of GetProcAddress + void* f = dlsym(sample_lib, name); + if (!f) { + OE_ERROR("Failed to find symbol: {} Error: {}", name, dlerror()); + } + return f; + }; + + // Using the nethost library, discover the location of hostfxr and get exports + bool load_hostfxr() + { + // Pre-allocate a large buffer for the path to hostfxr + char_t buffer[250]; + size_t buffer_size = sizeof(buffer) / sizeof(char_t); + int rc = get_hostfxr_path(buffer, &buffer_size, nullptr); + if (rc != 0) + return false; + + // Load hostfxr and get desired exports + auto* lib = dlopen(buffer, RTLD_NOW); + init_fptr = (hostfxr_initialize_for_runtime_config_fn)get_export(lib, "hostfxr_initialize_for_runtime_config"); + get_delegate_fptr = (hostfxr_get_runtime_delegate_fn)get_export(lib, "hostfxr_get_runtime_delegate"); + close_fptr = (hostfxr_close_fn)get_export(lib, "hostfxr_close"); + + return (init_fptr && get_delegate_fptr && close_fptr); + }; + + load_assembly_and_get_function_pointer_fn get_dotnet_load_assembly(const char_t *config_path) + { + // Load .NET Core + void *load_assembly_and_get_function_pointer = nullptr; + hostfxr_handle cxt = nullptr; + int rc = init_fptr(config_path, nullptr, &cxt); + if (rc != 0 || cxt == nullptr) + { + OE_ERROR("Init failed"); + close_fptr(cxt); + return nullptr; + } + + // Get the load assembly function pointer + rc = get_delegate_fptr( + cxt, + hdt_load_assembly_and_get_function_pointer, + &load_assembly_and_get_function_pointer); + if (rc != 0 || load_assembly_and_get_function_pointer == nullptr) + OE_ERROR("Get delegate failed"); + + close_fptr(cxt); + return (load_assembly_and_get_function_pointer_fn)load_assembly_and_get_function_pointer; + } + + component_entry_point_fn LoadMethod(const char* method) + { + component_entry_point_fn method_ptr; + + int rc = load_assembly_and_get_function_pointer( + dotnet_lib_path, + dotnet_type, + method, + UNMANAGEDCALLERSONLY_METHOD /*delegate_type_name*/, + nullptr, + (void**)&method_ptr); + + if (rc != 0) { + OE_ERROR("Failed to load method {}", method); + return nullptr; + } + + return method_ptr; + } + + void OnAttach() override + { + load_hostfxr(); + + load_assembly_and_get_function_pointer = get_dotnet_load_assembly("assets/scripts/mod_loader.runtimeconfig.json"); + component_entry_point_fn init = LoadMethod("Init"); + + init(nullptr, 0); + + hello = LoadMethod("Hello"); + + load_mod = LoadMethod("LoadMod"); + load_mod(nullptr, 0); + } + + void OnDetach() override + { + } + + void OnUpdate() override + { + static float time = 0; + + time += OpenEngine::Time::DeltaTime(); + + if (time >= 1) { + hello(nullptr, 0); + time = 0; + } + } + + void OnEvent(OpenEngine::Event& event) override + { + //OpenEngine::EventDispatcher dispatcher(event); + + //dispatcher.Dispatch(BIND_EVENT_FN(Sandbox2DLayer::StopRunning)); + } + + void OnImGuiRender() override + { + } + + private: + hostfxr_initialize_for_runtime_config_fn init_fptr; + hostfxr_get_runtime_delegate_fn get_delegate_fptr; + hostfxr_close_fn close_fptr; + load_assembly_and_get_function_pointer_fn load_assembly_and_get_function_pointer; + + component_entry_point_fn hello = nullptr; + component_entry_point_fn load_mod = nullptr; + + const char_t* dotnet_lib_path = "assets/scripts/mod_loader.dll"; + const char_t* dotnet_type = "OpenEngine.ModLoader, mod_loader"; + const char_t* dotnet_type_method = "Init"; +}; + +#endif // MODDING_HPP diff --git a/editor/include/overlay.hpp b/editor/include/overlay.hpp new file mode 100644 index 0000000..67ccd6f --- /dev/null +++ b/editor/include/overlay.hpp @@ -0,0 +1,252 @@ +#ifndef OVERLAY_HPP +#define OVERLAY_HPP + +/* + """ + Portions of this software are copyright © 2025 The FreeType + Project (https://freetype.org). All rights reserved. + """ +*/ + +#include + +#include "shmup.hpp" +#include +#include "freetype/fttypes.h" +#include +#include FT_FREETYPE_H +#include +#include + + +struct Character { + unsigned int texture_id; // ID handle of the glyph texture + glm::ivec2 size; // Size of glyph + glm::ivec2 bearing; // Offset from baseline to left/top of glyph + FT_Long advance; // Offset to advance to next glyph +}; + +class Overlay : public OpenEngine::Layer +{ + public: + Overlay(OpenEngine::Ref& shmup) + : game_layer(shmup) + {}; + + ~Overlay() {}; + + void RenderText(std::string text, float x, float y, float scale, glm::vec3 color) + { + // activate corresponding render state + text_shader->Bind(); + text_shader->SetVec3("textColor", glm::vec3(color.x, color.y, color.z)); + glActiveTexture(GL_TEXTURE0); + glBindVertexArray(VAO); + + // iterate through all characters + std::string::const_iterator c; + for (c = text.begin(); c != text.end(); c++) + { + Character ch = characters[*c]; + + float xpos = x + ch.bearing.x * scale; + float ypos = y - (ch.size.y - ch.bearing.y) * scale; + + float w = ch.size.x * scale; + float h = ch.size.y * scale; + // update VBO for each character + float vertices[6][4] = { + { xpos, ypos + h, 0.0f, 0.0f }, + { xpos, ypos, 0.0f, 1.0f }, + { xpos + w, ypos, 1.0f, 1.0f }, + + { xpos, ypos + h, 0.0f, 0.0f }, + { xpos + w, ypos, 1.0f, 1.0f }, + { xpos + w, ypos + h, 1.0f, 0.0f } + }; + // render glyph texture over quad + glBindTexture(GL_TEXTURE_2D, ch.texture_id); + // update content of VBO memory + glBindBuffer(GL_ARRAY_BUFFER, VBO); + glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices); + glBindBuffer(GL_ARRAY_BUFFER, 0); + // render quad + glDrawArrays(GL_TRIANGLES, 0, 6); + // now advance cursors for next glyph (note that advance is number of 1/64 pixels) + x += (ch.advance >> 6) * scale; // bitshift by 6 to get value in pixels (2^6 = 64) + } + glBindVertexArray(0); + glBindTexture(GL_TEXTURE_2D, 0); + } + + glm::vec2 GetTextSize(const std::string& text, float scale) + { + float width = 0.0f; + float height = 0.0f; + + for (char c : text) + { + Character ch = characters[c]; + width += (ch.advance >> 6) * scale; // Accumulate total width + + // Track the maximum height + float char_height = ch.size.y * scale; + if (char_height > height) + height = char_height; + } + + return glm::vec2(width, height); + } + + void RenderTextCentered(std::string text, float center_x, float center_y, float scale, glm::vec3 color) + { + glm::vec2 text_size = GetTextSize(text, scale); + + // Calculate top-left position to make text centered + float x = center_x - (text_size.x / 2.0f); + float y = center_y - (text_size.y / 2.0f); + + RenderText(text, x, y, scale, color); + } + + private: + void OnAttach() override + { + OE_INFO("Loading Freetype..."); + + int error; + + FT_Library ft; + error = FT_Init_FreeType(&ft); + if (error != 0) { + OE_ERROR("FREETYPE: Could not init FreeType Library"); + return; + } + + FT_Face face; + error = FT_New_Face(ft, "/usr/share/fonts/TTF/JetBrainsMono-Regular.ttf", 0, &face); + if (error != 0) { + OE_ERROR("FREETYPE: Failed to load font"); + return; + } + + FT_Set_Pixel_Sizes(face, 0, 48); + + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + + for (unsigned char c = 0; c < 128; c++) + { + // load character glyph + if (FT_Load_Char(face, c, FT_LOAD_RENDER)) + { + OE_ERROR("FREETYTPE: Failed to load Glyph: {}", c); + continue; + } + + // generate texture + unsigned int texture; + glGenTextures(1, &texture); + glBindTexture(GL_TEXTURE_2D, texture); + glTexImage2D( + GL_TEXTURE_2D, + 0, + GL_RED, + face->glyph->bitmap.width, + face->glyph->bitmap.rows, + 0, + GL_RED, + GL_UNSIGNED_BYTE, + face->glyph->bitmap.buffer + ); + // set texture options + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + // now store character for later use + Character character = { + texture, + glm::ivec2(face->glyph->bitmap.width, face->glyph->bitmap.rows), + glm::ivec2(face->glyph->bitmap_left, face->glyph->bitmap_top), + face->glyph->advance.x + }; + characters.insert(std::pair(c, character)); + } + + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + + FT_Done_Face(face); + FT_Done_FreeType(ft); + + text_shader = OpenEngine::Shader::Create("assets/shaders/text.glsl"); + + glGenVertexArrays(1, &VAO); + glGenBuffers(1, &VBO); + glBindVertexArray(VAO); + glBindBuffer(GL_ARRAY_BUFFER, VBO); + glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 6 * 4, NULL, GL_DYNAMIC_DRAW); + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 4 * sizeof(float), 0); + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindVertexArray(0); + }; + + void OnUpdate() override + { + text_shader->Bind(); + int width = OpenEngine::Application::Get().GetWindow().GetWidth(); + int height = OpenEngine::Application::Get().GetWindow().GetHeight(); + + projection = glm::ortho(0.0f, static_cast(width), + 0.0f, static_cast(height)); + + text_shader->SetMat4("u_ViewProjection", projection); + + float x = height - 40; + float y = 0; + + switch (game_layer->GetGameState()) { + case GameState::GameOver: + { + auto& window = OpenEngine::Application::Get().GetWindow(); + int width = window.GetWidth(); + int height = window.GetHeight(); + + float x = width / 2.0f; + float y = height / 2.0f; + RenderTextCentered("Game Over!", x, y, 1.0f, glm::vec3(0.8f, 0.2f, 0.3f)); + break; + } + case GameState::Paused: + RenderTextCentered("Paused", x, y, 1.0f, glm::vec3(0.8f, 0.2f, 0.3f)); + break; + case GameState::Playing: + break; + } + + std::string s_score = "Score: " + std::to_string(game_layer->GetScore()); + std::string s_lives = std::to_string(game_layer->GetLives()) + " lives"; + RenderText(s_score, 10, OpenEngine::Application::Get().GetWindow().GetHeight() - 50, 1.0f, {0.8f, 0.2f, 0.3f}); + RenderText( + s_lives, + OpenEngine::Application::Get().GetWindow().GetWidth() - 220, + OpenEngine::Application::Get().GetWindow().GetHeight() - 50, + 1.0f, + {0.8f, 0.2f, 0.3f}); + + }; + + void OnEvent(OpenEngine::Event& event) override + { + }; + + private: + std::map characters; + OpenEngine::Ref text_shader; + glm::mat4 projection; + OpenEngine::Ref game_layer; + + unsigned int VAO, VBO; +}; + +#endif // OVERLAY_HPP diff --git a/editor/include/sandbox2d.hpp b/editor/include/sandbox2d.hpp new file mode 100755 index 0000000..0abb22c --- /dev/null +++ b/editor/include/sandbox2d.hpp @@ -0,0 +1,130 @@ +#ifndef SANDBOX2D_HPP +#define SANDBOX2D_HPP + +#include + +#include +#include "imgui.h" + +#include +#include + +class Sandbox2DLayer : public OpenEngine::Layer +{ + public: + Sandbox2DLayer() + : OpenEngine::Layer("sandbox_2d"), + camera((float)OpenEngine::Application::Get().GetWindow().GetWidth() / + OpenEngine::Application::Get().GetWindow().GetHeight(), 1.0f) + { + } + ~Sandbox2DLayer() {}; + + void OnAttach() override + { + OE_PROFILE_FUNCTION(); + bindings = { + {"fwd/bckwd", 1}, + {"right/left", 0}, + {"yaw", 2} + }; + + { + OE_PROFILE_SCOPE("Texture2D Creation"); + face = OpenEngine::Texture2D::Create("assets/textures/awesomeface.png"); + } + } + + void OnDetach() override + { + } + + void OnUpdate() override + { + OE_PROFILE_FUNCTION() + { + OE_PROFILE_SCOPE("Setting up Rendering"); + OpenEngine::RenderCommand::SetClearColor({0.11f, 0.11f, 0.15f, 1.0f}); + OpenEngine::RenderCommand::Clear(); + OpenEngine::Renderer2D::BeginScene(camera.GetCamera()); + } + static float angle = 0.0f; + float ts = OpenEngine::Time::DeltaTime(); + + if (angle >= 360) + angle = 0; + + angle += (ts * 500.0f); + { + OE_PROFILE_SCOPE("Drawing Quads"); + OpenEngine::Transform tr1 = {glm::vec3(0.5f, 0.5f, 0.2f), glm::vec3(0.3f), 20.0f}; + OpenEngine::Transform tr2 = {glm::vec3(-0.2f, -0.2f, 0.1f), glm::vec3(0.5f, 0.2f, 1.0f), angle}; + OpenEngine::Transform tr3 = {glm::vec3(0.0f, 0.0f, -0.1f), glm::vec3(1.0f, 1.0f, 1.0f), 45.0f}; + + OpenEngine::Renderer2D::DrawQuad({glm::vec3(0.5f, 0.5f, 0.0f), glm::vec3(0.3f), 20.0f}, glm::vec4(color[0], color[1], color[2], color[3])); + OpenEngine::Renderer2D::DrawQuad(tr2, {0.5f, 0.3f, 0.8f, 1.0f}); + OpenEngine::Renderer2D::DrawQuad(tr3, face); + } + + OpenEngine::Renderer2D::EndScene(); + } + + bool StopRunning(OpenEngine::KeyPressedEvent& event) + { + if (event.GetKeyCode() == OE_KEY_ESCAPE) { + OpenEngine::Application::Get().StopRunning(); + + return true; + } + + return false; + } + + void OnEvent(OpenEngine::Event& event) override + { + OE_PROFILE_FUNCTION(); + OpenEngine::EventDispatcher dispatcher(event); + + //dispatcher.Dispatch(BIND_EVENT_FN(Sandbox2DLayer::StopRunning)); + { + OE_PROFILE_SCOPE("Camera OnEvent"); + camera.OnEvent(event); + } + } + + void OnImGuiRender() override + { + OE_PROFILE_FUNCTION(); + + ImGui::Begin("Square settings"); + + ImGui::ColorEdit4("Square color", color); + + for (auto& result : profiling_results) + { + char label[50]; + strcpy(label, "%.3fms "); + strcat(label, result.name); + ImGui::Text(label, result.duration); + } + + profiling_results.clear(); + + ImGui::End(); + } + + private: + //OpenEngine::ShaderLibrary shader_library; + glm::vec3 square_pos = glm::vec3(0.0f); + glm::vec4 square_color = glm::vec4(1.0f); + + float color[4] = {0.5f, 0.3f, 0.4f, 1.0f}; + OpenEngine::Ref face; + + std::unordered_map bindings; + OpenEngine::OrthographicCameraController camera; + + std::vector profiling_results; +}; + +#endif // SANDBOX2D_HPP diff --git a/editor/include/shmup.hpp b/editor/include/shmup.hpp new file mode 100755 index 0000000..2dc94dc --- /dev/null +++ b/editor/include/shmup.hpp @@ -0,0 +1,386 @@ +#ifndef SHMUP_HPP +#define SHMUP_HPP + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +enum class GameState +{ + Playing, + Paused, + GameOver +}; + +class Shmup : public OpenEngine::Layer +{ + public: + Shmup() + : camera((float)OpenEngine::Application::Get().GetWindow().GetWidth() / + OpenEngine::Application::Get().GetWindow().GetHeight(), 1.0f) + { + } + ~Shmup() {}; + + void OnAttach() override + { + OE_PROFILE_FUNCTION(); + { + OE_PROFILE_SCOPE("Texture2D Creation"); + ship = OpenEngine::Texture2D::Create("assets/textures/shmup/ship.png"); + enemy_ship = OpenEngine::Texture2D::Create("assets/textures/shmup/enemy_ship.png"); + bullet_tx = OpenEngine::Texture2D::Create("assets/textures/shmup/bullet.png"); + } + + LoadMap(); + } + + void OnDetach() override + { + } + + struct Bounds { + glm::vec2 min; // Bottom-Left + glm::vec2 max; // Top-Right + }; + + Bounds GetBounds(glm::vec2 position, glm::vec2 scale) { + Bounds b; + // Because the original vertices are -0.5 to 0.5, + // the distance from center to edge is exactly scale * 0.5 + glm::vec2 halfScale = scale * 0.5f; + + b.min = position - halfScale; + b.max = position + halfScale; + return b; + } + + bool CheckCollision(const Bounds& a, const Bounds& b) { + bool overlapX = a.max.x >= b.min.x && b.max.x >= a.min.x; + bool overlapY = a.max.y >= b.min.y && b.max.y >= a.min.y; + + return overlapX && overlapY; + } + + void MovePlayerShip() + { + double delta = OpenEngine::Time::DeltaTime(); + if (OpenEngine::Input::IsKeyPressed(OpenEngine::KeyCode::Up)) { + tr1.position.y += 1 * delta; + } + if (OpenEngine::Input::IsKeyPressed(OpenEngine::KeyCode::Down)) { + tr1.position.y -= 1 * delta; + } + if (OpenEngine::Input::IsKeyPressed(OpenEngine::KeyCode::Right)) { + tr1.position.x += 1 * delta; + } + if (OpenEngine::Input::IsKeyPressed(OpenEngine::KeyCode::Left)) { + tr1.position.x -= 1 * delta; + } + + if (tr1.position.y <= -0.9) + tr1.position.y = -0.9; + if (tr1.position.y >= -0.3) + tr1.position.y = -0.3; + if (tr1.position.x <= -0.8) + tr1.position.x = -0.8; + if (tr1.position.x >= 0.8) + tr1.position.x = 0.8; + } + + bool MovePlayerShipDiscrete(OpenEngine::KeyPressedEvent& event) + { + if (state == GameState::Playing) { + double delta = OpenEngine::Time::DeltaTime(); + if (event.GetKeyCode() == OpenEngine::KeyCode::Right) { + tr1.position.x += 0.1f; + return true; + } + if (event.GetKeyCode() == OpenEngine::KeyCode::Left) { + tr1.position.x -= 0.1f; + return true; + } + + if (tr1.position.y <= -0.9) + tr1.position.y = -0.9; + if (tr1.position.y >= -0.3) + tr1.position.y = -0.3; + if (tr1.position.x <= -0.8) + tr1.position.x = -0.8; + if (tr1.position.x >= 0.8) + tr1.position.x = 0.8; + } + + return false; + } + + void MoveEntities(double time_step) { + double delta = time_step; + + // Updating enemy positions + for (auto& enemy : enemies) { + enemy.position.y -= 0.3 * delta; + if (state == GameState::Playing && (enemy.position.y <= -1.1 || CheckCollision( + GetBounds(enemy.position, enemy.size), + GetBounds(tr1.position, tr1.size)))) + lives--; + } + + // Updating friendly bullets positions + for (auto& bullet : bullets) { + bullet.position.y += 1.1 * delta; + } + } + + void UpdateEntity() + { + double delta = OpenEngine::Time::DeltaTime(); + + MoveEntities(delta); + + // Deletes enemies offscreen + std::erase_if(enemies, [](const auto& enemy) { + return enemy.position.y <= -1.1f; + }); + // Deletes bullets offscreen + std::erase_if(bullets, [](const auto& bullet) { + return bullet.position.y >= 1.2f; + }); + + // Lowers lives upon collision between player and enemy + for (auto& enemy : enemies) { + if (state == GameState::Playing) { + if (enemy.position.y <= -1.1) + lives--; + + if (CheckCollision( + GetBounds(enemy.position, enemy.size), + GetBounds(tr1.position, tr1.size))) + lives = 0; + } + } + + // Deletes enemy and bullet and increases score after collision + for (auto& bullet : bullets) { + for (auto& enemy : enemies) { + Bounds bullet_bounds = GetBounds(bullet.position, bullet.size); + Bounds enemy_bounds = GetBounds(enemy.position, enemy.size); + + if (CheckCollision(bullet_bounds, enemy_bounds)) { + std::erase_if(bullets, [&](const OpenEngine::Transform& _bullet) { + return _bullet.position == bullet.position; + }); + std::erase_if(enemies, [&](const auto& _enemy) { + return _enemy.position == enemy.position; + }); + score++; + } + } + } + + } + + void Render() + { + OE_PROFILE_FUNCTION() + { + OE_PROFILE_SCOPE("Setting up Rendering"); + OpenEngine::RenderCommand::SetClearColor({0.11f, 0.11f, 0.15f, 1.0f}); + //OpenEngine::RenderCommand::SetClearColor({1.0f, 0.11f, 0.15f, 1.0f}); + OpenEngine::RenderCommand::Clear(); + OpenEngine::Renderer2D::BeginScene(camera.GetCamera()); + } + { + OE_PROFILE_SCOPE("Drawing Quads"); + OpenEngine::Renderer2D::DrawQuad(tr1, ship); + + for (auto& enemy : enemies) + OpenEngine::Renderer2D::DrawQuad(enemy, enemy_ship); + for (auto& bullet : bullets) + OpenEngine::Renderer2D::DrawQuad(bullet, bullet_tx); + } + + OpenEngine::Renderer2D::EndScene(); + + } + + void OnUpdate() override + { + float delta = OpenEngine::Time::DeltaTime(); + + Render(); + + UpdateEntity(); + + if (state == GameState::Playing) { + if (spawn_cooldown >= spawn_min_time) { + //SpawnEnemyRandom(); + ReadMap(); + spawn_cooldown = 0; + if (spawn_min_time > 0.5) + spawn_min_time -= 0.01; + } + + //MovePlayerShip(); + + if (shooting) + SpawnBullet(); + + shot_cooldown += delta; + spawn_cooldown += delta; + } + + if (lives <= 0) + state = GameState::GameOver; + } + + void Reset() + { + tr1 = {glm::vec3(0.0f, -0.9f, -0.1f), glm::vec3(0.1f), 0.0f}; + + score = 0; + lives = 5; + + bullets.clear(); + enemies.clear(); + + state = GameState::Playing; + } + + void SpawnEnemyRandom() + { + static std::uniform_int_distribution dist{-8, 8}; + + SpawnEnemy(dist(rd)); + }; + + void SpawnEnemy(int x) + { + OpenEngine::Transform enemy_tr({{x/10.0f, 1.2f, 0.1f}, glm::vec3(0.1f), 180.0f}); + enemies.emplace_back(enemy_tr); + }; + + void LoadMap(const char* path = "assets/maps/lvl1.txt") + { + std::string buffer; + + std::ifstream map_file(path); + + while (std::getline(map_file, buffer)) { + map.emplace_back(buffer); + } + }; + + void ReadMap() + { + static int line_n = 0; + if (line_n >= map.size()) + return; + + auto line = map[line_n]; + + int position = -8; + for (int i = 0; i < line.length(); i++) { + if (line[i] == '#') + SpawnEnemy(position); + position++; + } + + line_n++; + }; + + void SpawnBullet() + { + if (shot_cooldown >= 0.2) { + OpenEngine::Transform bullet_tr; + bullet_tr.size = glm::vec3(0.03f); + bullet_tr.position.x = tr1.position.x; + bullet_tr.position.y = tr1.position.y + 0.1; + + bullets.emplace_back(bullet_tr); + + shot_cooldown = 0; + } + } + + bool ProcessKeyPressedEvents(OpenEngine::KeyPressedEvent& event) + { + if (event.GetKeyCode() == OpenEngine::KeyCode::Space) { + shooting = true; + return true; + } + if (state == GameState::GameOver && event.GetKeyCode() == OpenEngine::KeyCode::Enter) { + Reset(); + return true; + } + + return false; + }; + + bool ProcessKeyReleased(OpenEngine::KeyReleasedEvent& event) + { + if (event.GetKeyCode() == OpenEngine::KeyCode::Space) + shooting = false; + + return false; + } + + void OnEvent(OpenEngine::Event& event) override + { + OE_PROFILE_FUNCTION(); + + OpenEngine::EventDispatcher dispatcher(event); + + dispatcher.Dispatch(BIND_EVENT_FN(Shmup::MovePlayerShipDiscrete)); + dispatcher.Dispatch(BIND_EVENT_FN(Shmup::ProcessKeyPressedEvents)); + dispatcher.Dispatch(BIND_EVENT_FN(Shmup::ProcessKeyReleased)); + { + OE_PROFILE_SCOPE("Camera OnEvent"); + camera.OnEvent(event); + } + } + + //void OnImGuiRender() override + //{ + // ImGuiIO io = ImGui::GetIO(); + // static auto m_Font = io.Fonts->AddFontFromFileTTF("/usr/share/fonts/TTF/JetBrainsMono-Regular.ttf", 120.0f); + + // ImGui::GetForegroundDrawList()->AddText(m_Font, 48.0f, {5.0f, 5.0f}, 0xffffffff, "test"); + //} + + GameState GetGameState() { return state; }; + int GetScore() { return score; }; + int GetLives() { return lives; }; + + private: + OpenEngine::Ref ship; + OpenEngine::Ref enemy_ship; + OpenEngine::Ref bullet_tx; + OpenEngine::Transform tr1 = {glm::vec3(0.0f, -0.9f, -0.1f), glm::vec3(0.1f), 0.0f}; + std::vector enemies, bullets; + int score = 0; + int lives = 5; + + bool shooting = false; + float spawn_cooldown = 0, shot_cooldown = 0; + float spawn_min_time = 1.0f; + std::vector map; + + std::random_device rd; + std::mt19937 e{rd()}; // or std::default_random_engine e{rd()}; + + OpenEngine::OrthographicCameraController camera; + GameState state = GameState::Playing; + + //OpenEngine::Ref overlay; +}; + +#endif // SHMUP_HPP diff --git a/editor/src/control_layer.cpp b/editor/src/control_layer.cpp new file mode 100644 index 0000000..208c6df --- /dev/null +++ b/editor/src/control_layer.cpp @@ -0,0 +1,44 @@ +#include +#include +#include +#include + +ControlLayer::ControlLayer(OpenEngine::Ref layer) + : active_layer(layer), OpenEngine::Layer("control_layer") +{ +} + +void ControlLayer::OnUpdate() +{ +} + +bool ControlLayer::StopRunning(OpenEngine::KeyPressedEvent& event) +{ + if (event.GetKeyCode() == OE_KEY_ESCAPE) { + OpenEngine::Application::Get().StopRunning(); + + return true; + } + + return false; +} + + +void ControlLayer::OnEvent(OpenEngine::Event& event) +{ + OpenEngine::EventDispatcher dispatcher(event); + + dispatcher.Dispatch(BIND_EVENT_FN(ControlLayer::StopRunning)); +} + +void ControlLayer::OnImGuiRender() +{ +} + +void ControlLayer::OnAttach() +{ +} + +void ControlLayer::OnDetach() +{ +} diff --git a/editor/src/editor.cpp b/editor/src/editor.cpp new file mode 100644 index 0000000..e50eec9 --- /dev/null +++ b/editor/src/editor.cpp @@ -0,0 +1,31 @@ +#include + +#include +#include + +#include +#include +#include + +#include + +EditorApp::EditorApp() +{ + OpenEngine::Ref initial_layer = std::make_shared(); + OpenEngine::Ref control_layer = std::make_shared(initial_layer); + OpenEngine::Ref modding_layer = std::make_shared(); + + QueueLayerPush(initial_layer); + QueueLayerPush(control_layer); + QueueLayerPush(modding_layer); +} + +EditorApp::~EditorApp() +{ +} + +OpenEngine::Application* OpenEngine::CreateApplication() +{ + OE_INFO("Editor Starting..."); + return new EditorApp(); +}