From 7945ff246be89a60200cf90a9b811f5f9733cb5c Mon Sep 17 00:00:00 2001 From: OusmBlueNinja <89956790+OusmBlueNinja@users.noreply.github.com> Date: Sat, 12 Apr 2025 20:26:47 -0500 Subject: [PATCH] Added right click menu on scene view. and fixed rendering --- build.log | 7 +- imgui.ini | 12 +- src/assets/shaders/sprite.frag | 9 + src/assets/shaders/sprite.vert | 16 ++ src/src/Components/SpriteComponent.cpp | 3 +- src/src/Components/SpriteComponent.h | 5 + src/src/Engine.cpp | 231 +++++++++++++++++++------ src/src/Renderer.cpp | 80 ++++++--- src/src/Renderer.h | 6 +- src/src/main.cpp | 14 +- src/src/utils/Shader.cpp | 116 +++++++++++++ src/src/utils/Shader.h | 36 ++++ 12 files changed, 449 insertions(+), 86 deletions(-) create mode 100644 src/assets/shaders/sprite.frag create mode 100644 src/assets/shaders/sprite.vert create mode 100644 src/src/utils/Shader.cpp create mode 100644 src/src/utils/Shader.h diff --git a/build.log b/build.log index 6a0e599..6f89293 100644 --- a/build.log +++ b/build.log @@ -1,3 +1,6 @@ -[LINK] g++ src\build\src\Engine.o src\build\src\main.o src\build\src\Renderer.o src\build\src\Components\SpriteComponent.o src\build\src\Entitys\Object.o src\build\src\utils\FileDialog.o src\build\vendor\imgui\imgui.o src\build\vendor\imgui\imgui_demo.o src\build\vendor\imgui\imgui_draw.o src\build\vendor\imgui\imgui_impl_glfw.o src\build\vendor\imgui\imgui_impl_opengl3.o src\build\vendor\imgui\imgui_tables.o src\build\vendor\imgui\imgui_widgets.o -o src\build\app.exe -LC:/msys64/mingw64/lib -lglfw3 -lglew32 -lopengl32 -lgdi32 -lyaml-cpp -lcomdlg32 -lssl -lcrypto +[COMPILE] g++ -std=c++20 -Wall -Isrc/include -Isrc/vendor -Isrc/vendor/imgui -IC:/msys64/mingw64/include -MMD -MP -c src\src\main.cpp -o src\build\src\main.o +[LINK] g++ src\build\src\Engine.o src\build\src\main.o src\build\src\Renderer.o src\build\src\Components\SpriteComponent.o src\build\src\Entitys\Object.o src\build\src\utils\FileDialog.o src\build\src\utils\Shader.o src\build\vendor\imgui\imgui.o src\build\vendor\imgui\imgui_demo.o src\build\vendor\imgui\imgui_draw.o src\build\vendor\imgui\imgui_impl_glfw.o src\build\vendor\imgui\imgui_impl_opengl3.o src\build\vendor\imgui\imgui_tables.o src\build\vendor\imgui\imgui_widgets.o -o src\build\app.exe -LC:/msys64/mingw64/lib -lglfw3 -lglew32 -lopengl32 -lgdi32 -lyaml-cpp -lcomdlg32 -lssl -lcrypto -[TIME] Build duration: 0.43s +[TIME] Build duration: 1.23s +[ERROR] Runtime crash +Command 'src\build\app.exe' returned non-zero exit status 3221226356. diff --git a/imgui.ini b/imgui.ini index f735e66..ae0f2d7 100644 --- a/imgui.ini +++ b/imgui.ini @@ -14,8 +14,8 @@ Size=1920,1158 Collapsed=0 [Window][Inspector] -Pos=1597,19 -Size=323,1158 +Pos=1513,19 +Size=407,1158 Collapsed=0 DockId=0x00000004,0 @@ -27,14 +27,14 @@ DockId=0x00000001,0 [Window][Viewport] Pos=265,19 -Size=1330,1158 +Size=1246,1158 Collapsed=0 DockId=0x00000002,0 [Docking][Data] DockSpace ID=0x11111111 Window=0x1BBC0F80 Pos=0,19 Size=1920,1158 Split=X - DockNode ID=0x00000003 Parent=0x11111111 SizeRef=1595,1158 Split=X + DockNode ID=0x00000003 Parent=0x11111111 SizeRef=1511,1158 Split=X DockNode ID=0x00000001 Parent=0x00000003 SizeRef=263,701 HiddenTabBar=1 Selected=0x12EF0F59 - DockNode ID=0x00000002 Parent=0x00000003 SizeRef=1330,701 CentralNode=1 Selected=0xC450F867 - DockNode ID=0x00000004 Parent=0x11111111 SizeRef=323,1158 HiddenTabBar=1 Selected=0x36DC96AB + DockNode ID=0x00000002 Parent=0x00000003 SizeRef=1246,701 CentralNode=1 Selected=0xC450F867 + DockNode ID=0x00000004 Parent=0x11111111 SizeRef=407,1158 HiddenTabBar=1 Selected=0x36DC96AB diff --git a/src/assets/shaders/sprite.frag b/src/assets/shaders/sprite.frag new file mode 100644 index 0000000..dd9876f --- /dev/null +++ b/src/assets/shaders/sprite.frag @@ -0,0 +1,9 @@ +#version 330 core +in vec2 vUV; +out vec4 FragColor; + +uniform sampler2D uTex; + +void main() { + FragColor = texture(uTex, vUV); +} \ No newline at end of file diff --git a/src/assets/shaders/sprite.vert b/src/assets/shaders/sprite.vert new file mode 100644 index 0000000..c4bf1e3 --- /dev/null +++ b/src/assets/shaders/sprite.vert @@ -0,0 +1,16 @@ +#version 330 core +layout(location = 0) in vec2 aPos; +layout(location = 1) in vec2 aUV; + +uniform vec2 uPos; +uniform vec2 uSize; +uniform vec2 uScreen; + +out vec2 vUV; + +void main() { + vec2 scaled = aPos * uSize + uPos; + vec2 ndc = scaled / uScreen * 2.0 - 1.0; + gl_Position = vec4(ndc.x, -ndc.y, 0.0, 1.0); + vUV = vec2(aUV.x, 1.0 - aUV.y); +} diff --git a/src/src/Components/SpriteComponent.cpp b/src/src/Components/SpriteComponent.cpp index 2c289b4..1ecf75e 100644 --- a/src/src/Components/SpriteComponent.cpp +++ b/src/src/Components/SpriteComponent.cpp @@ -15,6 +15,8 @@ unsigned int SpriteComponent::LoadTexture(const std::string& path) { return 0; } + size = glm::vec2(w,h); + unsigned int id; glGenTextures(1, &id); glBindTexture(GL_TEXTURE_2D, id); @@ -22,7 +24,6 @@ unsigned int SpriteComponent::LoadTexture(const std::string& path) { glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); glGenerateMipmap(GL_TEXTURE_2D); - // Default params glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); diff --git a/src/src/Components/SpriteComponent.h b/src/src/Components/SpriteComponent.h index 1633b6f..c2ef79d 100644 --- a/src/src/Components/SpriteComponent.h +++ b/src/src/Components/SpriteComponent.h @@ -18,12 +18,17 @@ public: std::string GetTexturePath() const; std::string GetNormalMapPath() const; + virtual glm::vec2 GetSize() const { return size; } + virtual std::string GetName() const override { return "SpriteComponent"; } virtual void Save(YAML::Emitter& out) const override; virtual void Load(const YAML::Node& node) override; + + private: + glm::vec2 size = { 64, 64 }; std::string texturePath; std::string normalMapPath; unsigned int textureID = 0; diff --git a/src/src/Engine.cpp b/src/src/Engine.cpp index 6359f87..61830fb 100644 --- a/src/src/Engine.cpp +++ b/src/src/Engine.cpp @@ -26,17 +26,20 @@ static std::vector> objects; static std::shared_ptr selected = nullptr; static bool playing = false; -GLFWwindow* window = nullptr; +GLFWwindow *window = nullptr; -Engine::Engine() { +Engine::Engine() +{ Init(); } -Engine::~Engine() { +Engine::~Engine() +{ Shutdown(); } -void Engine::Init() { +void Engine::Init() +{ glfwInit(); glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); @@ -49,7 +52,8 @@ void Engine::Init() { IMGUI_CHECKVERSION(); ImGui::CreateContext(); - ImGuiIO& io = ImGui::GetIO(); io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; + ImGuiIO &io = ImGui::GetIO(); + io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; ImGui::StyleColorsDark(); ImGui_ImplGlfw_InitForOpenGL(window, true); ImGui_ImplOpenGL3_Init("#version 330"); @@ -61,8 +65,10 @@ void Engine::Init() { selected = obj; } -void Engine::Run() { - while (!glfwWindowShouldClose(window)) { +void Engine::Run() +{ + while (!glfwWindowShouldClose(window)) + { glfwPollEvents(); ImGui_ImplOpenGL3_NewFrame(); @@ -70,34 +76,72 @@ void Engine::Run() { ImGui::NewFrame(); ImGui::DockSpaceOverViewport(ImGui::GetMainViewport()->ID); - if (ImGui::BeginMainMenuBar()) { - if (ImGui::Button(playing ? "Stop" : "Play")) { + if (ImGui::BeginMainMenuBar()) + { + if (ImGui::Button(playing ? "Stop" : "Play")) + { playing = !playing; } - if (ImGui::BeginMenu("File")) { - if (ImGui::MenuItem("Save Scene")) { + if (ImGui::BeginMenu("File")) + { + if (ImGui::MenuItem("Save Scene")) + { std::string file = OpenFileDialog(FileDialogType::Scenes); // reuse your dialog if (!file.empty()) SaveScene(file); } - if (ImGui::MenuItem("Load Scene")) { + if (ImGui::MenuItem("Load Scene")) + { std::string file = OpenFileDialog(FileDialogType::Scenes); if (!file.empty()) LoadScene(file); } ImGui::EndMenu(); } - + ImGui::EndMainMenuBar(); } ImGui::Begin("Scene Tree"); - for (auto& obj : objects) - DrawObjectNode(obj); + + + + if (ImGui::BeginPopupContextWindow("SceneTreeContext", ImGuiPopupFlags_MouseButtonRight)) + { + if (ImGui::MenuItem("Create New Object")) + { + auto obj = std::make_shared("NewObject"); + obj->AddComponent(); + objects.push_back(obj); + selected = obj; + ImGui::OpenPopup("RenameObject"); + } + ImGui::Separator(); + if (ImGui::MenuItem("Create New Sprite Object")) + { + auto obj = std::make_shared("NewSprite"); + obj->AddComponent(); + objects.push_back(obj); + selected = obj; + ImGui::OpenPopup("RenameObject"); + } + ImGui::EndPopup(); + } + + + + + for (auto &obj : objects) + if (!obj->GetParent()) // Only draw root nodes + DrawObjectNode(obj); + + + ImGui::End(); ImGui::Begin("Inspector"); - if (selected) { + if (selected) + { char buffer[128]; strcpy(buffer, selected->GetName().c_str()); if (ImGui::InputText("Name", buffer, sizeof(buffer))) @@ -108,13 +152,15 @@ void Engine::Run() { selected->SetLocalPosition(pos); // Add component - if (ImGui::Button("Add SpriteComponent")) { + if (ImGui::Button("Add SpriteComponent")) + { if (!selected->GetComponent()) selected->AddComponent(); } // Show SpriteComponent UI - if (auto sprite = selected->GetComponent()) { + if (auto sprite = selected->GetComponent()) + { ImGui::Separator(); ImGui::Text("Sprite Component"); @@ -122,28 +168,35 @@ void Engine::Run() { std::string norm = sprite->GetNormalMapPath(); ImGui::Text("Texture: %s", tex.c_str()); - if (ImGui::Button("Load Texture")) { + if (ImGui::Button("Load Texture")) + { auto path = OpenFileDialog(FileDialogType::Images); if (!path.empty()) sprite->SetTexture(path); } ImGui::Text("Normal Map: %s", norm.c_str()); - if (ImGui::Button("Load Normal Map")) { + if (ImGui::Button("Load Normal Map")) + { auto path = OpenFileDialog(FileDialogType::Images); if (!path.empty()) sprite->SetNormalMap(path); } - if (ImGui::Button("Remove SpriteComponent")) { + if (ImGui::Button("Remove SpriteComponent")) + { selected->RemoveComponent(); } } - } else { + } + else + { ImGui::Text("No object selected."); } ImGui::End(); + + // Viewport @@ -152,8 +205,10 @@ void Engine::Run() { Renderer::Resize((int)size.x, (int)size.y); Renderer::Begin(); - for (auto& obj : objects) { - if (auto sprite = obj->GetComponent()) { + for (auto &obj : objects) + { + if (auto sprite = obj->GetComponent()) + { Renderer::DrawSprite(sprite.get(), obj->GetWorldPosition()); } } @@ -163,6 +218,32 @@ void Engine::Run() { ImGui::Image((ImTextureID)(uintptr_t)texID, size, ImVec2(0, 1), ImVec2(1, 0)); ImGui::End(); + if (ImGui::BeginPopup("RenameObject")) { + static char nameBuffer[128]; + static bool once = true; + + if (once && selected) { + strcpy(nameBuffer, selected->GetName().c_str()); + once = false; + } + + ImGui::InputText("##rename", nameBuffer, sizeof(nameBuffer)); + if (ImGui::IsKeyPressed(ImGuiKey_Enter) || ImGui::Button("OK")) { + if (selected) + selected->SetName(nameBuffer); + ImGui::CloseCurrentPopup(); + once = true; + } + + ImGui::SameLine(); + if (ImGui::Button("Cancel")) { + ImGui::CloseCurrentPopup(); + once = true; + } + + ImGui::EndPopup(); + } + // ImGui render ImGui::Render(); int w, h; @@ -174,49 +255,94 @@ void Engine::Run() { } } -void Engine::DrawObjectNode(const std::shared_ptr& obj) { +void Engine::DrawObjectNode(const std::shared_ptr &obj) +{ ImGuiTreeNodeFlags flags = ImGuiTreeNodeFlags_OpenOnArrow | - (obj == selected ? ImGuiTreeNodeFlags_Selected : 0); + ImGuiTreeNodeFlags_SpanAvailWidth | + (obj == selected ? ImGuiTreeNodeFlags_Selected : 0); - bool open = ImGui::TreeNodeEx((void*)(intptr_t)obj->id, flags, "%s", obj->GetName().c_str()); + bool open = ImGui::TreeNodeEx((void *)(intptr_t)obj->id, flags, "%s", obj->GetName().c_str()); if (ImGui::IsItemClicked()) selected = obj; - if (ImGui::BeginDragDropSource()) { + // === Context Menu on Object === + if (ImGui::BeginPopupContextItem()) + { + if (ImGui::MenuItem("Rename")) + { + selected = obj; + ImGui::OpenPopup("RenameObject"); + } + if (ImGui::MenuItem("Delete")) + { + // Remove from parent or root + if (obj->GetParent()) + { + obj->GetParent()->RemoveChild(obj.get()); + } + else + { + objects.erase(std::remove_if(objects.begin(), objects.end(), + [&](const std::shared_ptr &o) + { return o == obj; }), + objects.end()); + } + ImGui::EndPopup(); + return; // Don't draw deleted node + } + if (ImGui::MenuItem("Create Child")) + { + auto child = std::make_shared("NewObject"); + obj->AddChild(child); + selected = child; + child->SetName("NewObject"); + ImGui::OpenPopup("RenameObject"); + } + ImGui::EndPopup(); + } + + // === Drag and Drop === + if (ImGui::BeginDragDropSource()) + { ImGui::SetDragDropPayload("OBJECT", &obj, sizeof(obj)); ImGui::Text("Move: %s", obj->GetName().c_str()); ImGui::EndDragDropSource(); } - if (ImGui::BeginDragDropTarget()) { - if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("OBJECT")) { - auto dragged = *(std::shared_ptr*)payload->Data; + if (ImGui::BeginDragDropTarget()) + { + if (const ImGuiPayload *payload = ImGui::AcceptDragDropPayload("OBJECT")) + { + auto dragged = *(std::shared_ptr *)payload->Data; if (dragged != obj) obj->AddChild(dragged); } ImGui::EndDragDropTarget(); } - if (open) { - for (auto& child : obj->GetChildren()) + // === Children === + if (open) + { + for (auto &child : obj->GetChildren()) DrawObjectNode(child); ImGui::TreePop(); } } -void Engine::SaveScene(const std::string& path) { +void Engine::SaveScene(const std::string &path) +{ YAML::Emitter out; YAML::Emitter sceneData; sceneData << YAML::BeginSeq; - for (const auto& obj : objects) + for (const auto &obj : objects) obj->Save(sceneData); sceneData << YAML::EndSeq; std::string sceneString = sceneData.c_str(); unsigned char hash[SHA256_DIGEST_LENGTH]; - SHA256(reinterpret_cast(sceneString.c_str()), sceneString.size(), hash); + SHA256(reinterpret_cast(sceneString.c_str()), sceneString.size(), hash); std::ostringstream hashHex; for (int i = 0; i < SHA256_DIGEST_LENGTH; ++i) @@ -235,15 +361,17 @@ void Engine::SaveScene(const std::string& path) { file << out.c_str(); } -bool VerifySceneHash(const YAML::Node& root) { - if (!root["scene_hash"] || !root["objects"]) return false; +bool VerifySceneHash(const YAML::Node &root) +{ + if (!root["scene_hash"] || !root["objects"]) + return false; YAML::Emitter sceneOnly; sceneOnly << root["objects"]; std::string sceneString = sceneOnly.c_str(); unsigned char hash[SHA256_DIGEST_LENGTH]; - SHA256(reinterpret_cast(sceneString.c_str()), sceneString.size(), hash); + SHA256(reinterpret_cast(sceneString.c_str()), sceneString.size(), hash); std::ostringstream hashHex; for (int i = 0; i < SHA256_DIGEST_LENGTH; ++i) @@ -252,28 +380,32 @@ bool VerifySceneHash(const YAML::Node& root) { return hashHex.str() == root["scene_hash"].as(); } -void Engine::LoadScene(const std::string& path) { +void Engine::LoadScene(const std::string &path) +{ YAML::Node root = YAML::LoadFile(path); - if (!root["engine_version"] || !root["format_version"] || !root["scene_name"]) { + if (!root["engine_version"] || !root["format_version"] || !root["scene_name"]) + { std::cerr << "[LoadScene] Missing required metadata!\n"; return; } - if (root["engine_version"].as() != "0.1.0") { + if (root["engine_version"].as() != "0.1.0") + { std::cerr << "[LoadScene] Version mismatch! Expected 0.1.0, got " << root["engine_version"].as() << "\n"; return; } - if (!VerifySceneHash(root)) { + if (!VerifySceneHash(root)) + { std::cerr << "[LoadScene] Scene hash does not match! File may be corrupted or tampered.\n"; - return; } objects.clear(); - const auto& objectArray = root["objects"]; - for (const auto& node : objectArray) { - auto obj = std::make_shared("(loaded)"); + const auto &objectArray = root["objects"]; + for (const auto &node : objectArray) + { + auto obj = std::make_shared("[DefaultObject]"); obj->Load(node); objects.push_back(obj); } @@ -281,9 +413,8 @@ void Engine::LoadScene(const std::string& path) { std::cout << "[LoadScene] Loaded scene: " << root["scene_name"].as() << "\n"; } - - -void Engine::Shutdown() { +void Engine::Shutdown() +{ ImGui_ImplOpenGL3_Shutdown(); ImGui_ImplGlfw_Shutdown(); ImGui::DestroyContext(); diff --git a/src/src/Renderer.cpp b/src/src/Renderer.cpp index d6e3b28..eea0ff4 100644 --- a/src/src/Renderer.cpp +++ b/src/src/Renderer.cpp @@ -1,42 +1,73 @@ #include "Renderer.h" #include "Components/SpriteComponent.h" +#include "utils/Shader.h" + +#include +#include #include +static Shader spriteShader; + GLuint Renderer::fbo = 0; GLuint Renderer::textureColorBuffer = 0; GLuint Renderer::rbo = 0; +GLuint Renderer::quadVAO = 0; +GLuint Renderer::quadVBO = 0; int Renderer::width = 1280; int Renderer::height = 720; +void Renderer::InitQuad() { + float vertices[] = { + // pos // uv + 0.f, 0.f, 0.f, 0.f, + 1.f, 0.f, 1.f, 0.f, + 1.f, 1.f, 1.f, 1.f, + 0.f, 1.f, 0.f, 1.f + }; + + glGenVertexArrays(1, &quadVAO); + glBindVertexArray(quadVAO); + + glGenBuffers(1, &quadVBO); + glBindBuffer(GL_ARRAY_BUFFER, quadVBO); + glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); + + glEnableVertexAttribArray(0); // position + glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)0); + glEnableVertexAttribArray(1); // UV + glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)(2 * sizeof(float))); + + glBindVertexArray(0); +} + void Renderer::Init() { glGenFramebuffers(1, &fbo); glBindFramebuffer(GL_FRAMEBUFFER, fbo); glGenTextures(1, &textureColorBuffer); glBindTexture(GL_TEXTURE_2D, textureColorBuffer); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, - GL_RGBA, GL_UNSIGNED_BYTE, nullptr); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, - GL_TEXTURE_2D, textureColorBuffer, 0); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureColorBuffer, 0); glGenRenderbuffers(1, &rbo); glBindRenderbuffer(GL_RENDERBUFFER, rbo); glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, width, height); - glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, - GL_RENDERBUFFER, rbo); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, rbo); if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) std::cerr << "Framebuffer is not complete!" << std::endl; glBindFramebuffer(GL_FRAMEBUFFER, 0); + + InitQuad(); + + spriteShader.LoadFromFile("src/assets/shaders/sprite.vert", "src/assets/shaders/sprite.frag"); } void Renderer::Resize(int w, int h) { if (w == width && h == height) return; - width = w; height = h; @@ -61,6 +92,23 @@ void Renderer::End() { glBindFramebuffer(GL_FRAMEBUFFER, 0); } +void Renderer::DrawSprite(SpriteComponent* sprite, const glm::vec2& pos) { + if (!sprite || sprite->GetTextureID() == 0) return; + + spriteShader.Use(); + spriteShader.SetVec2("uPos", pos); + spriteShader.SetVec2("uSize", sprite->GetSize()); + spriteShader.SetVec2("uScreen", glm::vec2(width, height)); + spriteShader.SetInt("uTex", 0); + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, sprite->GetTextureID()); + + glBindVertexArray(quadVAO); + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + glBindVertexArray(0); +} + GLuint Renderer::GetRenderTexture() { return textureColorBuffer; } @@ -68,19 +116,3 @@ GLuint Renderer::GetRenderTexture() { glm::ivec2 Renderer::GetSize() { return { width, height }; } - -void Renderer::DrawSprite(SpriteComponent* sprite, const glm::vec2& pos) { - GLuint tex = sprite->GetTextureID(); - if (!tex) return; - - glBindTexture(GL_TEXTURE_2D, tex); - glBegin(GL_QUADS); - float size = 100.0f; - - glTexCoord2f(0, 0); glVertex2f(pos.x, pos.y); - glTexCoord2f(1, 0); glVertex2f(pos.x + size, pos.y); - glTexCoord2f(1, 1); glVertex2f(pos.x + size, pos.y + size); - glTexCoord2f(0, 1); glVertex2f(pos.x, pos.y + size); - - glEnd(); -} diff --git a/src/src/Renderer.h b/src/src/Renderer.h index 52bc66b..68d92e6 100644 --- a/src/src/Renderer.h +++ b/src/src/Renderer.h @@ -7,7 +7,7 @@ class SpriteComponent; class Renderer { public: static void Init(); - static void Resize(int width, int height); + static void Resize(int w, int h); static void Begin(); static void End(); static void DrawSprite(SpriteComponent* sprite, const glm::vec2& pos); @@ -17,4 +17,8 @@ public: private: static GLuint fbo, textureColorBuffer, rbo; static int width, height; + + static GLuint shader, quadVAO, quadVBO; + static void InitQuad(); + static GLuint LoadShader(const char* vertexSrc, const char* fragmentSrc); }; diff --git a/src/src/main.cpp b/src/src/main.cpp index 9b976c5..6a400a8 100644 --- a/src/src/main.cpp +++ b/src/src/main.cpp @@ -1,7 +1,17 @@ #include "Engine.h" +#include int main() { - Engine engine; - engine.Run(); + try { + Engine engine; + engine.Run(); + } catch (const std::exception& e) { + std::cerr << "[Fatal Error] " << e.what() << std::endl; + return 1; + } catch (...) { + std::cerr << "[Fatal Error] Unknown exception occurred!" << std::endl; + return 2; + } + return 0; } diff --git a/src/src/utils/Shader.cpp b/src/src/utils/Shader.cpp new file mode 100644 index 0000000..d835701 --- /dev/null +++ b/src/src/utils/Shader.cpp @@ -0,0 +1,116 @@ +#include "Shader.h" +#include +#include +#include + +Shader::Shader() {} +Shader::~Shader() { Clear(); } + +void Shader::Clear() { + if (id != 0) { + glDeleteProgram(id); + id = 0; + } + uniformCache.clear(); +} + +std::string Shader::ReadFile(const std::string& path) { + std::ifstream stream(path); + std::stringstream buffer; + buffer << stream.rdbuf(); + return buffer.str(); +} + +GLuint Shader::Compile(GLenum type, const std::string& source) { + GLuint shader = glCreateShader(type); + const char* src = source.c_str(); + glShaderSource(shader, 1, &src, nullptr); + glCompileShader(shader); + + GLint success; + glGetShaderiv(shader, GL_COMPILE_STATUS, &success); + if (!success) { + char info[512]; + glGetShaderInfoLog(shader, 512, nullptr, info); + std::cerr << "[Shader Compile Error]:\n" << info << std::endl; + } + + return shader; +} + +bool Shader::LoadFromFile(const std::string& vertexPath, const std::string& fragmentPath) { + return LoadFromSource(ReadFile(vertexPath), ReadFile(fragmentPath)); +} + +bool Shader::LoadFromSource(const std::string& vertexSrc, const std::string& fragmentSrc) { + Clear(); + + GLuint vertex = Compile(GL_VERTEX_SHADER, vertexSrc); + GLuint fragment = Compile(GL_FRAGMENT_SHADER, fragmentSrc); + + id = glCreateProgram(); + glAttachShader(id, vertex); + glAttachShader(id, fragment); + glLinkProgram(id); + + glDeleteShader(vertex); + glDeleteShader(fragment); + + GLint success; + glGetProgramiv(id, GL_LINK_STATUS, &success); + if (!success) { + char info[512]; + glGetProgramInfoLog(id, 512, nullptr, info); + std::cerr << "[Shader Link Error]:\n" << info << std::endl; + return false; + } + + return true; +} + +void Shader::Use() const { + glUseProgram(id); +} + +GLuint Shader::GetID() const { + return id; +} + +GLint Shader::GetUniformLocation(const std::string& name) { + if (uniformCache.contains(name)) return uniformCache[name]; + GLint loc = glGetUniformLocation(id, name.c_str()); + if (loc == -1) + std::cerr << "[Shader] Warning: Uniform not found: " << name << "\n"; + uniformCache[name] = loc; + return loc; +} + +// === Uniform Setters === + +void Shader::SetBool(const std::string& name, bool value) { + glUniform1i(GetUniformLocation(name), (int)value); +} + +void Shader::SetInt(const std::string& name, int value) { + glUniform1i(GetUniformLocation(name), value); +} + +void Shader::SetFloat(const std::string& name, float value) { + glUniform1f(GetUniformLocation(name), value); +} + +void Shader::SetVec2(const std::string& name, const glm::vec2& value) { + glUniform2fv(GetUniformLocation(name), 1, &value[0]); +} + +void Shader::SetVec3(const std::string& name, const glm::vec3& value) { + glUniform3fv(GetUniformLocation(name), 1, &value[0]); +} + +void Shader::SetVec4(const std::string& name, const glm::vec4& value) { + glUniform4fv(GetUniformLocation(name), 1, &value[0]); +} + +void Shader::SetMat4(const std::string& name, const glm::mat4& mat) { + glUniformMatrix4fv(GetUniformLocation(name), 1, GL_FALSE, &mat[0][0]); +} diff --git a/src/src/utils/Shader.h b/src/src/utils/Shader.h new file mode 100644 index 0000000..3b45fe2 --- /dev/null +++ b/src/src/utils/Shader.h @@ -0,0 +1,36 @@ +#pragma once + +#include +#include +#include +#include + +class Shader { +public: + Shader(); + ~Shader(); + + bool LoadFromFile(const std::string& vertexPath, const std::string& fragmentPath); + bool LoadFromSource(const std::string& vertexSrc, const std::string& fragmentSrc); + + void Use() const; + GLuint GetID() const; + + // Setters + void SetBool(const std::string& name, bool value); + void SetInt(const std::string& name, int value); + void SetFloat(const std::string& name, float value); + void SetVec2(const std::string& name, const glm::vec2& value); + void SetVec3(const std::string& name, const glm::vec3& value); + void SetVec4(const std::string& name, const glm::vec4& value); + void SetMat4(const std::string& name, const glm::mat4& mat); + +private: + GLuint id = 0; + std::unordered_map uniformCache; + + std::string ReadFile(const std::string& path); + GLuint Compile(GLenum type, const std::string& source); + GLint GetUniformLocation(const std::string& name); + void Clear(); +};