diff --git a/include/graphics.hpp b/include/graphics.hpp new file mode 100644 index 0000000..83a6a80 --- /dev/null +++ b/include/graphics.hpp @@ -0,0 +1,45 @@ +#ifndef GRAPHICS_HPP +#define GRAPHICS_HPP +#define GL_GLEXT_PROTOTYPES + +// Includes +#include "imgui.h" +#include "imgui_impl_sdl3.h" +#include "imgui_impl_opengl3.h" +#include +#include "window.hpp" + +// Constants +const int open_gl_major_version = 3; +const int open_gl_minor_version = 3; +const int enable_vsync = 1; + +// Variables +extern Uint32 last_time; +extern Uint32 current_time; +extern float frame_time; +extern float fps; +extern float frame_time_graph[100]; +extern SDL_GLContext gl_context; + +extern ImGuiIO& io; +extern ImGuiStyle& style; + +// Functions +int initialize_opengl(); +void cleanup_opengl(); + +// ImGui +int initialize_imgui(); +void cleanup_imgui(); +void new_frame_imgui(); +void frame_time_ui(); + +// Frame time calculation +void calc_frame_time(); + +// Shader management +unsigned int compile_shader(const char* shader_source, GLenum shader_type); +unsigned int create_program(unsigned int vertex_shader, unsigned int fragment_shader); + +#endif \ No newline at end of file diff --git a/include/main.hpp b/include/main.hpp index 1709fd7..a3130dd 100644 --- a/include/main.hpp +++ b/include/main.hpp @@ -1,44 +1,16 @@ #ifndef MAIN_HPP #define MAIN_HPP -// ImGui -#include "imgui.h" -#include "imgui_impl_sdl3.h" -#include "imgui_impl_opengl3.h" - -// SDL3 and OpenGL -#include -#include - -// C++ Standard Library +// Includes #include -#include -#include - -// Structs - - +#include "window.hpp" +#include "graphics.hpp" +#include "utils.hpp" // Variables -int window_width = 1920; -int window_height = 1080; - -float main_scale; -SDL_WindowFlags window_flags = SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_HIDDEN | SDL_WINDOW_HIGH_PIXEL_DENSITY; -SDL_Window* window; - -bool running; -SDL_Event event; - -Uint32 last_time; -Uint32 current_time; -float frame_time; -float fps; -float frame_time_graph[100] = {0.0f}; +extern bool running; // Functions int main(int argc, char* argv[]); -int initialize(); -void cleanup(); #endif \ No newline at end of file diff --git a/include/utils.hpp b/include/utils.hpp new file mode 100644 index 0000000..31a1172 --- /dev/null +++ b/include/utils.hpp @@ -0,0 +1,11 @@ +#ifndef UTILS_HPP +#define UTILS_HPP + +// Includes +#include +#include + +// Function +char* read_file(const char* file_path); + +#endif \ No newline at end of file diff --git a/include/window.hpp b/include/window.hpp new file mode 100644 index 0000000..31e8467 --- /dev/null +++ b/include/window.hpp @@ -0,0 +1,22 @@ +#ifndef WINDOW_HPP +#define WINDOW_HPP + +// Includes +#include "iostream" +#include + +// Constants +const int window_width = 800; +const int window_height = 600; + +// Variables +const SDL_WindowFlags window_flags = SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_HIDDEN | SDL_WINDOW_HIGH_PIXEL_DENSITY; +extern float main_scale; +extern SDL_Window* window; +extern SDL_Event event; + +// Functions +int initialize_sdl(); +void cleanup_sdl(); + +#endif \ No newline at end of file diff --git a/shaders/triangle.frag b/shaders/triangle.frag new file mode 100644 index 0000000..5a2ecf0 --- /dev/null +++ b/shaders/triangle.frag @@ -0,0 +1,8 @@ +#version 330 core + +out vec4 FragColor; +in vec3 color; // Input color from the vertex shader + +void main() { + FragColor = vec4(color, 1.0); // Set the fragment color to the input color +} \ No newline at end of file diff --git a/shaders/triangle.vert b/shaders/triangle.vert new file mode 100644 index 0000000..760e898 --- /dev/null +++ b/shaders/triangle.vert @@ -0,0 +1,9 @@ +#version 330 core + +layout(location = 0) in vec3 position; // Vertex position +out vec3 color; // Output color to the fragment shader + +void main() { + gl_Position = vec4(position, 1.0); // Set the position of the vertex + color = position + vec3(0.5); // Set the color based on the vertex position +} \ No newline at end of file diff --git a/src/graphics.cpp b/src/graphics.cpp new file mode 100644 index 0000000..0532001 --- /dev/null +++ b/src/graphics.cpp @@ -0,0 +1,158 @@ +#include "graphics.hpp" + +// Variables +Uint32 last_time; +Uint32 current_time; +float frame_time; +float fps; +float frame_time_graph[100]; + +SDL_GLContext gl_context; + +// Functions +int initialize_opengl() { + // Initialize OpenGL + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, open_gl_major_version); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, open_gl_minor_version); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); + + // Initialize OpenGL context + gl_context = SDL_GL_CreateContext(window); + if (!gl_context) { + std::cerr << "OpenGL context could not be created! SDL_Error: " << SDL_GetError() << std::endl; + return 1; // OpenGL context creation failed + } + + // Initialize OpenGL settings for the window + SDL_GL_MakeCurrent(window, gl_context); + SDL_GL_SetSwapInterval(enable_vsync); + SDL_SetWindowPosition(window, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED); + SDL_ShowWindow(window); + + glViewport(0, 0, (int)(window_width * main_scale), (int)(window_height * main_scale)); + + return 0; // OpenGL initialization successful +} + +void cleanup_opengl() { + // Destroy OpenGL context + if (gl_context) { + SDL_GL_MakeCurrent(NULL, NULL); + SDL_GL_DestroyContext(gl_context); + gl_context = nullptr; + } + std::cout << "OpenGL cleanup successful." << std::endl; +} + +unsigned int compile_shader(const char* shader_source, GLenum shader_type) { + unsigned int shader = glCreateShader(shader_type); + if (shader == 0) { + std::cerr << "Failed to create shader." << std::endl; + return 0; // Shader creation failed + } + + glShaderSource(shader, 1, &shader_source, nullptr); + glCompileShader(shader); + + // Check for compilation errors + int success; + glGetShaderiv(shader, GL_COMPILE_STATUS, &success); + if (!success) { + char info_log[512]; + glGetShaderInfoLog(shader, sizeof(info_log), nullptr, info_log); + std::cerr << "Shader compilation error: " << info_log << std::endl; + glDeleteShader(shader); + return 0; // Shader compilation failed + } + + return shader; // Shader compiled successfully +} + +unsigned int create_program(unsigned int vertex_shader, unsigned int fragment_shader) { + unsigned int program = glCreateProgram(); + if (program == 0) { + std::cerr << "Failed to create shader program." << std::endl; + return 0; // Program creation failed + } + + glAttachShader(program, vertex_shader); + glAttachShader(program, fragment_shader); + glLinkProgram(program); + + // Check for linking errors + int success; + glGetProgramiv(program, GL_LINK_STATUS, &success); + if (!success) { + char info_log[512]; + glGetProgramInfoLog(program, sizeof(info_log), nullptr, info_log); + std::cerr << "Shader program linking error: " << info_log << std::endl; + glDeleteProgram(program); + return 0; // Program linking failed + } + + return program; // Program created successfully +} + +int initialize_imgui() { + IMGUI_CHECKVERSION(); + ImGui::CreateContext(); + ImGuiIO& io = ImGui::GetIO(); (void)io; + io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; + io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; + + ImGui::StyleColorsDark(); + ImGuiStyle& style = ImGui::GetStyle(); + style.ScaleAllSizes(main_scale); + style.FontScaleDpi = main_scale; + + if (!ImGui_ImplSDL3_InitForOpenGL(window, gl_context)) { + std::cerr << "ImGui_ImplSDL3_InitForOpenGL failed!" << std::endl; + return 1; // Initialization failed + } + + char glsl_version[32]; + snprintf(glsl_version, sizeof(glsl_version), "#version %d%d0", open_gl_major_version, open_gl_minor_version); + if (!ImGui_ImplOpenGL3_Init(glsl_version)) { + std::cerr << "ImGui_ImplOpenGL3_Init failed!" << std::endl; + ImGui_ImplSDL3_Shutdown(); + ImGui::DestroyContext(); + return 1; // Initialization failed + } + + return 0; +} + +void cleanup_imgui() { + ImGui_ImplOpenGL3_Shutdown(); + ImGui_ImplSDL3_Shutdown(); + ImGui::DestroyContext(); + std::cout << "ImGui cleanup successful." << std::endl; +} + +void calc_frame_time() { + // Calculate frame time + current_time = SDL_GetTicks(); + frame_time = (current_time - last_time) / 1000.0f; // Convert milliseconds to seconds + fps = 1.0f / frame_time; + last_time = current_time; + + // Update frame time graph + for (int i = 99; i > 0; --i) { + frame_time_graph[i] = frame_time_graph[i - 1]; // Shift old values + } + frame_time_graph[0] = frame_time; // Add new frame time +} + +void new_frame_imgui() { + ImGui_ImplOpenGL3_NewFrame(); + ImGui_ImplSDL3_NewFrame(); + ImGui::NewFrame(); +} + +void frame_time_ui() { + ImGui::Begin("Frame Time"); + ImGui::Text("Current FPS: %.2f", fps); + ImGui::Text("Current Frame Time: %.3f ms", frame_time * 1000.0f); + ImGui::PlotLines("##Frame Time Graph", frame_time_graph, 100, 0, "Frame Time (ms)", 0.0f, 0.1f, ImVec2(200, 80)); + ImGui::End(); +} \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 85a5911..ef8a41d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,170 +1,135 @@ #include "main.hpp" +#include - -// Init -int initialize() { - // Initialize SDL - if (!SDL_Init(SDL_INIT_VIDEO)) { - std::cerr << "SDL could not initialize! SDL_Error: " << SDL_GetError() << std::endl; - return 1; // Initialization failed - } - - // Create a window with OpenGL context - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); - SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); - SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); - SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8); - - main_scale = SDL_GetDisplayContentScale(SDL_GetPrimaryDisplay()); - window = SDL_CreateWindow("Untitled", (int)(window_width * main_scale), (int)(window_height * main_scale), window_flags); - if (!window) { - std::cerr << "Window could not be created! SDL_Error: " << SDL_GetError() << std::endl; - SDL_Quit(); - return 1; // Window creation failed - } - - // Initialize OpenGL context - SDL_GLContext gl_context = SDL_GL_CreateContext(window); - if (!gl_context) { - std::cerr << "OpenGL context could not be created! SDL_Error: " << SDL_GetError() << std::endl; - SDL_DestroyWindow(window); - SDL_Quit(); - return 1; // OpenGL context creation failed - } - - SDL_GL_MakeCurrent(window, gl_context); - SDL_GL_SetSwapInterval(1); // Enable VSync - SDL_SetWindowPosition(window, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED); - SDL_ShowWindow(window); - - // Initialize ImGui - IMGUI_CHECKVERSION(); - ImGui::CreateContext(); - ImGuiIO& io = ImGui::GetIO(); (void)io; - io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls - - // Setup Dear ImGui style - ImGui::StyleColorsDark(); - - // Setup scale - ImGuiStyle& style = ImGui::GetStyle(); - style.ScaleAllSizes(main_scale); - style.FontScaleDpi = main_scale; - - // Setup Platform/Renderer bindings - if (!ImGui_ImplSDL3_InitForOpenGL(window, gl_context)) { - std::cerr << "ImGui_ImplSDL3_InitForOpenGL failed!" << std::endl; - SDL_GL_DestroyContext(gl_context); - SDL_DestroyWindow(window); - SDL_Quit(); - return 1; // Initialization failed - } - - if (!ImGui_ImplOpenGL3_Init("#version 330")) { - std::cerr << "ImGui_ImplOpenGL3_Init failed!" << std::endl; - ImGui_ImplSDL3_Shutdown(); - SDL_GL_DestroyContext(gl_context); - SDL_DestroyWindow(window); - SDL_Quit(); - return 1; // Initialization failed - } - - return 0; // Initialization successful -} - - -// Cleanup -void cleanup() { - // Shutdown ImGui - ImGui_ImplOpenGL3_Shutdown(); - ImGui_ImplSDL3_Shutdown(); - ImGui::DestroyContext(); - - // Destroy OpenGL context - SDL_GLContext gl_context = SDL_GL_GetCurrentContext(); - if (gl_context) { - SDL_GL_MakeCurrent(NULL, NULL); - SDL_GL_DestroyContext(gl_context); - } - - // Destroy window - if (window) { - SDL_DestroyWindow(window); - window = nullptr; - } - - // Quit SDL - SDL_Quit(); - std::cout << "Cleanup successful." << std::endl; -} - +// Variables +bool running = true; // Main int main(int argc, char* argv[]) { - // Initialize - if (initialize() != 0) { - std::cerr << "Initialization failed." << std::endl; - return -1; // Initialization failed + // Initialize SDL + if (initialize_sdl() != 0) { + std::cerr << "SDL initialization failed." << std::endl; + return 1; // SDL initialization failed } + // Initialize OpenGL + if (initialize_opengl() != 0) { + std::cerr << "OpenGL initialization failed." << std::endl; + cleanup_sdl(); + return 1; // OpenGL initialization failed + } + + // Initialize ImGui + if (initialize_imgui() != 0) { + std::cerr << "ImGui initialization failed." << std::endl; + cleanup_opengl(); + cleanup_sdl(); + return 1; // ImGui initialization failed + } + + // Triangle vertices + float vertices[] = { + 0.5f, 0.5f, 0.0f, // top right + 0.5f, -0.5f, 0.0f, // bottom right + -0.5f, -0.5f, 0.0f, // bottom left + -0.5f, 0.5f, 0.0f // top left + }; + + unsigned int indices[] = { + 0, 1, 3, // first triangle + 1, 2, 3 // second triangle + }; + + // Vertex buffer object (VBO) and vertex array object (VAO) and element buffer object (EBO) + unsigned int VBO, VAO, EBO; + + glGenVertexArrays(1, &VAO); // Generate a vertex array object + glGenBuffers(1, &VBO); // Generate a vertex buffer object + glGenBuffers(1, &EBO); // Generate an element buffer object + + glBindVertexArray(VAO); // Bind the vertex array object + glBindBuffer(GL_ARRAY_BUFFER, VBO); // Bind the vertex buffer object + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO); // Bind the element buffer object + + // Upload vertex data to the buffer + glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW); + + // Set up vertex attributes + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0); + glEnableVertexAttribArray(0); + + glBindVertexArray(0); // Unbind the vertex array object + + // Load shaders + const char* vertex_shader_source = read_file("shaders/triangle.vert"); + const char* fragment_shader_source = read_file("shaders/triangle.frag"); + + unsigned int vertex_shader = compile_shader(vertex_shader_source, GL_VERTEX_SHADER); + unsigned int fragment_shader = compile_shader(fragment_shader_source, GL_FRAGMENT_SHADER); + unsigned int shader_program = create_program(vertex_shader, fragment_shader); + + // Set the shader program + glUseProgram(shader_program); + + // Wireframe toggle + bool wireframe_mode = false; + // Main loop - running = true; - last_time = SDL_GetTicks(); while (running) { // Handle events while (SDL_PollEvent(&event)) { - ImGui_ImplSDL3_ProcessEvent(&event); + ImGui_ImplSDL3_ProcessEvent(&event); // Process ImGui events if (event.type == SDL_EVENT_QUIT) { running = false; // Exit loop on quit event } } // Start the ImGui frame - ImGui_ImplOpenGL3_NewFrame(); - ImGui_ImplSDL3_NewFrame(); - ImGui::NewFrame(); + new_frame_imgui(); - // Show a simple ImGui window - ImGui::Begin("Hello, World!"); - ImGui::Text("This is a simple ImGui window."); - if (ImGui::Button("Close")) { - running = false; // Close button clicked + // Frame time UI + frame_time_ui(); + + // Toggle wireframe mode ui + ImGui::Begin("Wireframe Mode"); + if (ImGui::Checkbox("Enable Wireframe Mode", &wireframe_mode)) { + if (wireframe_mode) { + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); // Enable wireframe mode + } else { + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); // Disable wireframe mode + } } ImGui::End(); - // Display FPS in the ImGui window - ImGui::Begin("FPS Counter"); - ImGui::Text("Current FPS: %.2f", fps); - // Frame time graph - ImGui::PlotLines("##FrameTime", frame_time_graph, 100, 0, "Frame Time (ms)", 0.0f, 0.1f, ImVec2(200, 80)); - ImGui::Text("Current Time: %u ms", current_time); - - ImGui::End(); - // Render ImGui::Render(); - glViewport(0, 0, (int)(window_width * main_scale), (int)(window_height * main_scale)); - glClearColor(0.45f, 0.55f, 0.60f, 1.00f); + glClearColor(0.2f, 0.3f, 0.3f, 1.0f); glClear(GL_COLOR_BUFFER_BIT); + + glBindVertexArray(VAO); // Bind the vertex array object + glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); // Draw the triangles + ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); SDL_GL_SwapWindow(window); // Calculate FPS - current_time = SDL_GetTicks(); - frame_time = (current_time - last_time) / 1000.0f; // Convert milliseconds to seconds - fps = 1.0f / frame_time; - last_time = current_time; - - // Update frame time graph - for (int i = 99; i > 0; --i) { - frame_time_graph[i] = frame_time_graph[i - 1]; // Shift old values - } - frame_time_graph[0] = frame_time; // Add new frame time + calc_frame_time(); } // Cleanup - cleanup(); + glDeleteVertexArrays(1, &VAO); + glDeleteBuffers(1, &VBO); + glDeleteBuffers(1, &EBO); + glDeleteProgram(shader_program); + glDeleteShader(vertex_shader); + glDeleteShader(fragment_shader); + free((void*)vertex_shader_source); + free((void*)fragment_shader_source); + cleanup_imgui(); + cleanup_opengl(); + cleanup_sdl(); + std::cout << "Application exited successfully." << std::endl; + return 0; // Exit code } \ No newline at end of file diff --git a/src/utils.cpp b/src/utils.cpp new file mode 100644 index 0000000..c632783 --- /dev/null +++ b/src/utils.cpp @@ -0,0 +1,24 @@ +#include "utils.hpp" + +char* read_file(const char* file_path) { + std::ifstream file(file_path); + if (!file) { + std::cerr << "Could not open file: " << file_path << std::endl; + return nullptr; // Return null if file cannot be opened + } + + file.seekg(0, std::ios::end); + size_t file_size = file.tellg(); + file.seekg(0, std::ios::beg); + + char* buffer = new char[file_size + 1]; + if (!buffer) { + std::cerr << "Memory allocation failed" << std::endl; + return nullptr; + } + + file.read(buffer, file_size); + buffer[file_size] = '\0'; // Null-terminate the string + + return buffer; +} \ No newline at end of file diff --git a/src/window.cpp b/src/window.cpp new file mode 100644 index 0000000..d7547f9 --- /dev/null +++ b/src/window.cpp @@ -0,0 +1,37 @@ +#include "window.hpp" + +// Variables +float main_scale; +SDL_Window* window; +SDL_Event event; + +// Functions +int initialize_sdl() { + // Initialize SDL + if (!SDL_Init(SDL_INIT_VIDEO)) { + std::cerr << "SDL could not initialize! SDL_Error: " << SDL_GetError() << std::endl; + return 1; // Initialization failed + } + + main_scale = SDL_GetDisplayContentScale(SDL_GetPrimaryDisplay()); + window = SDL_CreateWindow("Untitled", (int)(window_width * main_scale), (int)(window_height * main_scale), window_flags); + if (!window) { + std::cerr << "Window could not be created! SDL_Error: " << SDL_GetError() << std::endl; + SDL_Quit(); + return 1; // Window creation failed + } + + return 0; // Initialization successful +} + +void cleanup_sdl() { + if (window) { + // Destroy window + SDL_DestroyWindow(window); + window = nullptr; + } + + // Quit SDL + SDL_Quit(); + std::cout << "SDL cleanup successful." << std::endl; +} \ No newline at end of file