Added right click menu on scene view. and fixed rendering

This commit is contained in:
OusmBlueNinja 2025-04-12 20:26:47 -05:00
parent 02594dc015
commit 7945ff246b
12 changed files with 449 additions and 86 deletions

View File

@ -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.

View File

@ -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

View File

@ -0,0 +1,9 @@
#version 330 core
in vec2 vUV;
out vec4 FragColor;
uniform sampler2D uTex;
void main() {
FragColor = texture(uTex, vUV);
}

View File

@ -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);
}

View File

@ -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);

View File

@ -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;

View File

@ -26,17 +26,20 @@
static std::vector<std::shared_ptr<Object>> objects;
static std::shared_ptr<Object> 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,17 +76,22 @@ 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);
@ -92,12 +103,45 @@ void Engine::Run() {
}
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<Object>("NewObject");
obj->AddComponent<SpriteComponent>();
objects.push_back(obj);
selected = obj;
ImGui::OpenPopup("RenameObject");
}
ImGui::Separator();
if (ImGui::MenuItem("Create New Sprite Object"))
{
auto obj = std::make_shared<Object>("NewSprite");
obj->AddComponent<SpriteComponent>();
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<SpriteComponent>())
selected->AddComponent<SpriteComponent>();
}
// Show SpriteComponent UI
if (auto sprite = selected->GetComponent<SpriteComponent>()) {
if (auto sprite = selected->GetComponent<SpriteComponent>())
{
ImGui::Separator();
ImGui::Text("Sprite Component");
@ -122,38 +168,47 @@ 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<SpriteComponent>();
}
}
} else {
}
else
{
ImGui::Text("No object selected.");
}
ImGui::End();
// Viewport
ImGui::Begin("Viewport", nullptr, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse);
ImVec2 size = ImGui::GetContentRegionAvail();
Renderer::Resize((int)size.x, (int)size.y);
Renderer::Begin();
for (auto& obj : objects) {
if (auto sprite = obj->GetComponent<SpriteComponent>()) {
for (auto &obj : objects)
{
if (auto sprite = obj->GetComponent<SpriteComponent>())
{
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<Object>& obj) {
void Engine::DrawObjectNode(const std::shared_ptr<Object> &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<Object> &o)
{ return o == obj; }),
objects.end());
}
ImGui::EndPopup();
return; // Don't draw deleted node
}
if (ImGui::MenuItem("Create Child"))
{
auto child = std::make_shared<Object>("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<Object>*)payload->Data;
if (ImGui::BeginDragDropTarget())
{
if (const ImGuiPayload *payload = ImGui::AcceptDragDropPayload("OBJECT"))
{
auto dragged = *(std::shared_ptr<Object> *)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<const unsigned char*>(sceneString.c_str()), sceneString.size(), hash);
SHA256(reinterpret_cast<const unsigned char *>(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<const unsigned char*>(sceneString.c_str()), sceneString.size(), hash);
SHA256(reinterpret_cast<const unsigned char *>(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<std::string>();
}
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<std::string>() != "0.1.0") {
if (root["engine_version"].as<std::string>() != "0.1.0")
{
std::cerr << "[LoadScene] Version mismatch! Expected 0.1.0, got " << root["engine_version"].as<std::string>() << "\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<Object>("(loaded)");
const auto &objectArray = root["objects"];
for (const auto &node : objectArray)
{
auto obj = std::make_shared<Object>("[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<std::string>() << "\n";
}
void Engine::Shutdown() {
void Engine::Shutdown()
{
ImGui_ImplOpenGL3_Shutdown();
ImGui_ImplGlfw_Shutdown();
ImGui::DestroyContext();

View File

@ -1,42 +1,73 @@
#include "Renderer.h"
#include "Components/SpriteComponent.h"
#include "utils/Shader.h"
#include <GL/glew.h>
#include <glm/glm.hpp>
#include <iostream>
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();
}

View File

@ -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);
};

View File

@ -1,7 +1,17 @@
#include "Engine.h"
#include <iostream>
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;
}

116
src/src/utils/Shader.cpp Normal file
View File

@ -0,0 +1,116 @@
#include "Shader.h"
#include <fstream>
#include <sstream>
#include <iostream>
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]);
}

36
src/src/utils/Shader.h Normal file
View File

@ -0,0 +1,36 @@
#pragma once
#include <string>
#include <unordered_map>
#include <glm/glm.hpp>
#include <gl/glew.h>
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<std::string, GLint> uniformCache;
std::string ReadFile(const std::string& path);
GLuint Compile(GLenum type, const std::string& source);
GLint GetUniformLocation(const std::string& name);
void Clear();
};