diff --git a/CMakeLists.txt b/CMakeLists.txt index 496ea18..ec999f8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -80,6 +80,13 @@ target_include_directories(glad PUBLIC third_party/glad/include) add_library(Engine STATIC engine/src/core/Window.h engine/src/core/Window.cpp + engine/src/core/Application.cpp + engine/src/core/Application.h + engine/src/core/EngineConfig.h + engine/src/utils/openglWrapper/GLStateCache.cpp + engine/src/utils/openglWrapper/GLStateCache.h + engine/src/utils/openglWrapper/GLRenderState.cpp + engine/src/utils/openglWrapper/GLRenderState.h ) target_include_directories(Engine PUBLIC engine/src) target_link_libraries(Engine PUBLIC OpenGL::GL glfw glad glm::glm imgui) @@ -87,6 +94,8 @@ target_link_libraries(Engine PUBLIC OpenGL::GL glfw glad glm::glm imgui) # --- Executable --- add_executable(ColorRace game/src/main.cpp + game/src/ColorRaceApp.cpp + game/src/ColorRaceApp.h ) target_link_libraries(ColorRace PRIVATE diff --git a/engine/src/core/Application.cpp b/engine/src/core/Application.cpp new file mode 100644 index 0000000..7f205d4 --- /dev/null +++ b/engine/src/core/Application.cpp @@ -0,0 +1,37 @@ +// +// Created by sebastian on 29.07.26. +// + +#include "Application.h" +#include +#include + +#include "utils/openglWrapper/GLStateCache.h" + +namespace engine { + Application::Application(const EngineConfig& config) : m_config(config) { + m_window = std::make_unique(config); + } + + Application::~Application() = default; + + void Application::run() { + auto lastTime = static_cast(glfwGetTime()); + + while (!m_window->shouldClose()) { + const auto currentTime = static_cast(glfwGetTime()); + const float deltaTime = currentTime - lastTime; + lastTime = currentTime; + + m_window->pollEvents(); + + onUpdate(deltaTime); + + GLStateCache::clearFrameBuffer(m_config.clearColor); + + onRender(); + + m_window->swapBuffers(); + } + } +} diff --git a/engine/src/core/Application.h b/engine/src/core/Application.h new file mode 100644 index 0000000..2a26d65 --- /dev/null +++ b/engine/src/core/Application.h @@ -0,0 +1,37 @@ +// +// Created by sebastian on 29.07.26. +// + +#ifndef COLORRACE_APPLICATION_H +#define COLORRACE_APPLICATION_H +#include +#include + +#include "EngineConfig.h" +#include "Window.h" + + + +namespace engine { + class Application { + public: + Application(const EngineConfig& config); + virtual ~Application(); + + void run(); + + protected: + virtual void onUpdate(float deltaTime) {} + virtual void onRender() {} + + private: + EngineConfig m_config; + std::unique_ptr m_window; + }; + +} + + + + +#endif //COLORRACE_APPLICATION_H diff --git a/engine/src/core/EngineConfig.h b/engine/src/core/EngineConfig.h new file mode 100644 index 0000000..ad17123 --- /dev/null +++ b/engine/src/core/EngineConfig.h @@ -0,0 +1,28 @@ +// +// Created by sebastian on 29.07.26. +// + +#ifndef COLORRACE_ENGINECONFIG_H +#define COLORRACE_ENGINECONFIG_H +#include + +#include "glm/vec4.hpp" + +namespace engine { + struct EngineConfig { + int windowWidth = 1280; + int windowHeight = 720; + std::string windowTitle = "Engine"; + + bool fullscreen = false; + bool vsync = true; + bool msaa = true; + int msaaSamples = 4; + + glm::vec4 clearColor = {0.1f, 0.1f, 0.15f, 1.0f}; + + bool debugContext = true; // GL_DEBUG_OUTPUT an/aus + }; +} + +#endif //COLORRACE_ENGINECONFIG_H diff --git a/engine/src/core/Window.cpp b/engine/src/core/Window.cpp index 904daa8..1ebdc42 100644 --- a/engine/src/core/Window.cpp +++ b/engine/src/core/Window.cpp @@ -6,8 +6,10 @@ #include +#include "EngineConfig.h" #include "glad/glad.h" #include "GLFW/glfw3.h" +#include "utils/openglWrapper/GLStateCache.h" namespace engine { static void GLAPIENTRY DebugCallback(GLenum source, GLenum type, GLuint id, @@ -17,31 +19,47 @@ namespace engine { std::cerr << "[GL DEBUG] " << message << "\n"; } - Window::Window(int width, int height, const std::string& title) { + Window::Window(const EngineConfig& config) { if (!glfwInit()) throw std::runtime_error("GLFW init failed"); glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 6); glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); - glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GLFW_TRUE); + glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, config.debugContext ? GLFW_TRUE : GLFW_FALSE); - m_handle = glfwCreateWindow(width, height, title.c_str(), nullptr, nullptr); + if (config.msaa) { + glfwWindowHint(GLFW_SAMPLES, config.msaaSamples); + } + + GLFWmonitor* monitor = config.fullscreen ? glfwGetPrimaryMonitor() : nullptr; + m_handle = glfwCreateWindow(config.windowWidth, config.windowHeight, config.windowTitle.c_str(), monitor, nullptr); if (!m_handle) { glfwTerminate(); throw std::runtime_error("Window creation failed"); } glfwMakeContextCurrent(m_handle); + glfwSwapInterval(config.vsync ? 1 : 0); // VSync if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) { throw std::runtime_error("GLAD init failed"); } - glEnable(GL_DEBUG_OUTPUT); - glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS); - glDebugMessageCallback(DebugCallback, nullptr); + GLStateCache::init(); - glViewport(0, 0, width, height); + if (config.debugContext) { + glEnable(GL_DEBUG_OUTPUT); + glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS); + glDebugMessageCallback(DebugCallback, nullptr); + } + + if (config.msaa) { + glEnable(GL_MULTISAMPLE); + } + + + + glViewport(0, 0, config.windowWidth, config.windowHeight); } Window::~Window() { diff --git a/engine/src/core/Window.h b/engine/src/core/Window.h index 5f244ac..1b79c76 100644 --- a/engine/src/core/Window.h +++ b/engine/src/core/Window.h @@ -8,12 +8,14 @@ #pragma once #include +#include "EngineConfig.h" + struct GLFWwindow; namespace engine { class Window { public: - Window(int width, int height, const std::string& title); + Window(const EngineConfig& config); ~Window(); bool shouldClose() const; diff --git a/engine/src/utils/openglWrapper/GLRenderState.cpp b/engine/src/utils/openglWrapper/GLRenderState.cpp new file mode 100644 index 0000000..ce22657 --- /dev/null +++ b/engine/src/utils/openglWrapper/GLRenderState.cpp @@ -0,0 +1,17 @@ +// +// Created by sebastian on 29.07.26. +// + +#include "GLRenderState.h" + +#include "GLStateCache.h" + +namespace engine { + void GLRenderState::apply() const { + GLStateCache::setFaceCulling(backfaceCulling || frontFaceCulling, frontFaceCulling); + GLStateCache::setAntialiasing(antialiasing); + GLStateCache::setAlphaBlending(alphaBlending); + GLStateCache::setDepthTest(depthTesting); + GLStateCache::setDepthWriting(depthWriting); + } +} diff --git a/engine/src/utils/openglWrapper/GLRenderState.h b/engine/src/utils/openglWrapper/GLRenderState.h new file mode 100644 index 0000000..16da361 --- /dev/null +++ b/engine/src/utils/openglWrapper/GLRenderState.h @@ -0,0 +1,22 @@ +// +// Created by sebastian on 29.07.26. +// + +#ifndef COLORRACE_GLRENDERSTATE_H +#define COLORRACE_GLRENDERSTATE_H + +namespace engine { + struct GLRenderState { + bool backfaceCulling = false; + bool frontFaceCulling = false; + bool alphaBlending = false; + bool antialiasing = false; + bool depthTesting = true; // in 3D meist sinnvoller Default + bool depthWriting = true; + + void apply() const; + }; + +} + +#endif //COLORRACE_GLRENDERSTATE_H diff --git a/engine/src/utils/openglWrapper/GLStateCache.cpp b/engine/src/utils/openglWrapper/GLStateCache.cpp new file mode 100644 index 0000000..144ba88 --- /dev/null +++ b/engine/src/utils/openglWrapper/GLStateCache.cpp @@ -0,0 +1,115 @@ +// +// Created by sebastian on 29.07.26. +// + +#include "GLStateCache.h" + +#include + +namespace engine { + void GLStateCache::init() { + glDisable(GL_CULL_FACE); + glDisable(GL_MULTISAMPLE); + glDisable(GL_BLEND); + glDisable(GL_DEPTH_TEST); + glDepthMask(GL_TRUE); + glDisable(GL_SCISSOR_TEST); + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + } + + void GLStateCache::setFaceCulling(bool enabled, bool frontFace) { + if (enabled) { + if (!s_cullingFaces) { + glEnable(GL_CULL_FACE); + s_cullingFaces = true; + } + if (frontFace != s_frontFaceCulling) { + glCullFace(frontFace ? GL_FRONT : GL_BACK); + s_frontFaceCulling = frontFace; + } + } else if (s_cullingFaces) { + glDisable(GL_CULL_FACE); + s_cullingFaces = false; + } + } + + void GLStateCache::setAntialiasing(bool enabled) { + if (enabled == s_antialiasing) return; + enabled ? glEnable(GL_MULTISAMPLE) : glDisable(GL_MULTISAMPLE); + s_antialiasing = enabled; + } + + void GLStateCache::setAlphaBlending(bool enabled) { + if (enabled == s_alphaBlending && !s_additiveBlending) return; + if (enabled) { + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + s_alphaBlending = true; + s_additiveBlending = false; + } else if (s_alphaBlending) { + glDisable(GL_BLEND); + s_alphaBlending = false; + } + } + + void GLStateCache::setAdditiveBlending(bool enabled) { + if (enabled && !s_additiveBlending) { + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE); + s_additiveBlending = true; + s_alphaBlending = false; + } else if (!enabled && s_additiveBlending) { + glDisable(GL_BLEND); + s_additiveBlending = false; + } + } + + void GLStateCache::setDepthTest(bool enabled) { + if (enabled == s_depthTest) return; + enabled ? glEnable(GL_DEPTH_TEST) : glDisable(GL_DEPTH_TEST); + s_depthTest = enabled; + } + + void GLStateCache::setDepthWriting(bool enabled) { + if (enabled == s_depthWriting) return; + glDepthMask(enabled ? GL_TRUE : GL_FALSE); + s_depthWriting = enabled; + } + + void GLStateCache::setScissorTest(bool enabled, int x, int y, int width, int height) { + if (enabled && !s_scissorTest) { + glEnable(GL_SCISSOR_TEST); + s_scissorTest = true; + } else if (!enabled && s_scissorTest) { + glDisable(GL_SCISSOR_TEST); + s_scissorTest = false; + return; + } + if (enabled && (x != s_scissorBounds[0] || y != s_scissorBounds[1] || + width != s_scissorBounds[2] || height != s_scissorBounds[3])) { + glScissor(x, y, width, height); + s_scissorBounds[0] = x; s_scissorBounds[1] = y; + s_scissorBounds[2] = width; s_scissorBounds[3] = height; + } + } + + void GLStateCache::setWireframe(bool enabled) { + if (enabled == s_wireframe) return; + glPolygonMode(GL_FRONT_AND_BACK, enabled ? GL_LINE : GL_FILL); + s_wireframe = enabled; + } + + void GLStateCache::bindTexture2D(unsigned int textureId, int slot) { + glActiveTexture(GL_TEXTURE0 + slot); + glBindTexture(GL_TEXTURE_2D, textureId); + } + + void GLStateCache::clearColorBuffer() { glClear(GL_COLOR_BUFFER_BIT); } + void GLStateCache::clearDepthBuffer() { glClear(GL_DEPTH_BUFFER_BIT); } + void GLStateCache::clearFrameBuffer() { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); } + + void GLStateCache::clearFrameBuffer(glm::vec4 color) { + glClearColor(color.r, color.g, color.b, color.a); + clearFrameBuffer(); + } +} diff --git a/engine/src/utils/openglWrapper/GLStateCache.h b/engine/src/utils/openglWrapper/GLStateCache.h new file mode 100644 index 0000000..d2abde3 --- /dev/null +++ b/engine/src/utils/openglWrapper/GLStateCache.h @@ -0,0 +1,44 @@ +// +// Created by sebastian on 29.07.26. +// + +#ifndef COLORRACE_GLSTATECACHE_H +#define COLORRACE_GLSTATECACHE_H +#include "glm/vec4.hpp" + +namespace engine { + class GLStateCache { + public: + static void init(); // einmal nach GLAD-Init aufrufen, um Startzustand zu setzen + + static void setFaceCulling(bool enabled, bool frontFace); + static void setAntialiasing(bool enabled); + static void setAlphaBlending(bool enabled); + static void setAdditiveBlending(bool enabled); + static void setDepthTest(bool enabled); + static void setDepthWriting(bool enabled); + static void setScissorTest(bool enabled, int x = 0, int y = 0, int width = 0, int height = 0); + static void setWireframe(bool enabled); + + static void bindTexture2D(unsigned int textureId, int slot); + + static void clearColorBuffer(); + static void clearDepthBuffer(); + static void clearFrameBuffer(); + static void clearFrameBuffer(glm::vec4 color); + private: + inline static bool s_cullingFaces = false; + inline static bool s_frontFaceCulling = false; + inline static bool s_antialiasing = false; + inline static bool s_alphaBlending = false; + inline static bool s_additiveBlending = false; + inline static bool s_depthTest = false; + inline static bool s_depthWriting = true; + inline static bool s_scissorTest = false; + inline static bool s_wireframe = false; + inline static int s_scissorBounds[4] = {0, 0, 0, 0}; + inline static glm::vec4 s_clearColor = {0.0f, 0.0f, 0.0f, 1.0f}; + }; +} + +#endif //COLORRACE_GLSTATECACHE_H diff --git a/game/src/ColorRaceApp.cpp b/game/src/ColorRaceApp.cpp new file mode 100644 index 0000000..10f14f2 --- /dev/null +++ b/game/src/ColorRaceApp.cpp @@ -0,0 +1,12 @@ +// +// Created by sebastian on 29.07.26. +// + +#include "ColorRaceApp.h" +void ColorRaceApp::onUpdate(float deltaTime) { + +} + +void ColorRaceApp::onRender() { + +} \ No newline at end of file diff --git a/game/src/ColorRaceApp.h b/game/src/ColorRaceApp.h new file mode 100644 index 0000000..4806efb --- /dev/null +++ b/game/src/ColorRaceApp.h @@ -0,0 +1,29 @@ +// +// Created by sebastian on 29.07.26. +// + +#ifndef COLORRACE_COLORRACEAPP_H +#define COLORRACE_COLORRACEAPP_H +#include "core/Application.h" + + +class ColorRaceApp : public engine::Application { +public: + static engine::EngineConfig makeConfig() { + engine::EngineConfig config; + + config.windowWidth = 1280; + config.windowHeight = 720; + config.windowTitle = "ColorRace"; + config.clearColor = glm::vec4(0.1f, 0.1f, 0.15f, 1.0f); + config.vsync = true; + return config; + } + ColorRaceApp() : Application(makeConfig()) {} +protected: + void onUpdate(float deltaTime) override; + void onRender() override; +}; + + +#endif //COLORRACE_COLORRACEAPP_H diff --git a/game/src/main.cpp b/game/src/main.cpp index a7ccc46..9534b7b 100644 --- a/game/src/main.cpp +++ b/game/src/main.cpp @@ -1,20 +1,8 @@ -#include -#include "core/Window.h" -#include "glad/glad.h" - - +#include "ColorRaceApp.h" // TIP To Run code, press or click the icon in the gutter. int main() { - engine::Window window(1280, 720, "ColorRace"); - - while (!window.shouldClose()) { - window.pollEvents(); - - glClearColor(0.1f, 0.1f, 0.15f, 1.0f); - glClear(GL_COLOR_BUFFER_BIT); - - window.swapBuffers(); - } + ColorRaceApp app; + app.run(); return 0; } \ No newline at end of file