diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..e4fbba0 --- /dev/null +++ b/.vscode/c_cpp_properties.json @@ -0,0 +1,16 @@ +{ + "configurations": [ + { + "name": "linux-gcc-x64", + "includePath": [ + "${workspaceFolder}/**" + ], + "defines": [], + "compilerPath": "/nix/store/xcn9p4xxfbvlkpah7pwchpav4ab9d135-gcc-wrapper-14-20241116/bin/gcc", + "cStandard": "c17", + "cppStandard": "gnu++17", + "intelliSenseMode": "linux-gcc-x64" + } + ], + "version": 4 +} \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..08caf88 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,55 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "type": "cppdbg", + "request": "launch", + "name": "Launch Program", + "program": "${workspaceFolder}/build/${input:executableName}", + "preLaunchTask": "build", + "args": [], + "stopAtEntry": false, + "cwd": "${workspaceFolder}", + "environment": [], + "externalConsole": false, + "MIMode": "gdb", + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + } + ], + "miDebuggerPath": "/usr/bin/gdb", + "logging": { + "engineLogging": true + } + }, + { + "name": "C/C++ Runner: Debug Session", + "type": "cppdbg", + "request": "launch", + "args": [], + "stopAtEntry": false, + "externalConsole": false, + "cwd": "/home/micqdf/opengl/cpp/src", + "program": "/home/micqdf/opengl/cpp/src/build/Debug/outDebug", + "MIMode": "gdb", + "miDebuggerPath": "/nix/store/xcn9p4xxfbvlkpah7pwchpav4ab9d135-gdb-wrapper-14-20241116/bin/gcc", + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + } + ] + } + ], + "inputs": [ + { + "type": "promptString", + "id": "executableName", + "description": "Name of your executable" + } + ] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..50ea268 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,61 @@ +{ + "C_Cpp_Runner.cCompilerPath": "/nix/store/xcn9p4xxfbvlkpah7pwchpav4ab9d135-gcc-wrapper-14-20241116/bin/gcc", + "C_Cpp_Runner.cppCompilerPath": "/nix/store/xcn9p4xxfbvlkpah7pwchpav4ab9d135-g++-wrapper-14-20241116/bin/gcc", + "C_Cpp_Runner.debuggerPath": "/nix/store/xcn9p4xxfbvlkpah7pwchpav4ab9d135-gdb-wrapper-14-20241116/bin/gcc", + "C_Cpp_Runner.cStandard": "c17", + "C_Cpp_Runner.cppStandard": "gnu++17", + "C_Cpp_Runner.msvcBatchPath": "", + "C_Cpp_Runner.useMsvc": false, + "C_Cpp_Runner.warnings": [ + "-Wall", + "-Wextra", + "-Wpedantic", + "-Wshadow", + "-Wformat=2", + "-Wcast-align", + "-Wconversion", + "-Wsign-conversion", + "-Wnull-dereference" + ], + "C_Cpp_Runner.msvcWarnings": [ + "/W4", + "/permissive-", + "/w14242", + "/w14287", + "/w14296", + "/w14311", + "/w14826", + "/w44062", + "/w44242", + "/w14905", + "/w14906", + "/w14263", + "/w44265", + "/w14928" + ], + "C_Cpp_Runner.enableWarnings": true, + "C_Cpp_Runner.warningsAsError": false, + "C_Cpp_Runner.compilerArgs": [], + "C_Cpp_Runner.linkerArgs": [], + "C_Cpp_Runner.includePaths": [ + "${workspaceFolder}/**" + ], + "C_Cpp_Runner.includeSearch": [ + "*", + "**/*" + ], + "C_Cpp_Runner.excludeSearch": [ + "**/build", + "**/build/**", + "**/.*", + "**/.*/**", + "**/.vscode", + "**/.vscode/**" + ], + "C_Cpp_Runner.useAddressSanitizer": false, + "C_Cpp_Runner.useUndefinedSanitizer": false, + "C_Cpp_Runner.useLeakSanitizer": false, + "C_Cpp_Runner.showCompilationTime": false, + "C_Cpp_Runner.useLinkTimeOptimization": false, + "C_Cpp_Runner.msvcSecureNoWarnings": false +} \ No newline at end of file diff --git a/MakeFile b/MakeFile deleted file mode 100644 index f2b7558..0000000 --- a/MakeFile +++ /dev/null @@ -1,41 +0,0 @@ -# A simple Makefile for compiling small SDL projects - -# set the compiler -CC := clang - -# set the compiler flags -CFLAGS := `sdl2-config --libs --cflags` -ggdb3 -O0 --std=c99 -Wall -lSDL2_image -lm -# add header files here -HDRS := - -# add source files here -SRCS := #file-name.c - -# generate names of object files -OBJS := $(SRCS:.c=.o) - -# name of executable -EXEC := #name your executable file - -# default recipe -all: $(EXEC) - -showfont: showfont.c Makefile - $(CC) -o $@ $@.c $(CFLAGS) $(LIBS) - -glfont: glfont.c Makefile - $(CC) -o $@ $@.c $(CFLAGS) $(LIBS) - -# recipe for building the final executable -$(EXEC): $(OBJS) $(HDRS) Makefile - $(CC) -o $@ $(OBJS) $(CFLAGS) - -# recipe for building object files -#$(OBJS): $(@:.o=.c) $(HDRS) Makefile -# $(CC) -o $@ $(@:.o=.c) -c $(CFLAGS) - -# recipe to clean the workspace -clean: - rm -f $(EXEC) $(OBJS) - -.PHONY: all clean diff --git a/Makefile b/Makefile new file mode 100755 index 0000000..124b70a --- /dev/null +++ b/Makefile @@ -0,0 +1,21 @@ +CC = g++ +CFLAGS = -Iinclude -Wall -g +LDFLAGS = -lSDL2 -lGL -lGLEW + +SRC = src/main.cpp src/sdl.cpp +OBJ = $(SRC:.cpp=.o) +EXEC = opengl-app + +all: $(EXEC) + +$(EXEC): $(OBJ) + $(CC) -o $@ $^ $(LDFLAGS) + +%.o: %.cpp + $(CC) $(CFLAGS) -c $< -o $@ + +clean: + rm -f $(OBJ) $(EXEC) + +run: all + ./$(EXEC) \ No newline at end of file diff --git a/src/IndexBuffer.cpp b/src/IndexBuffer.cpp new file mode 100644 index 0000000..74c7fe6 --- /dev/null +++ b/src/IndexBuffer.cpp @@ -0,0 +1,27 @@ +#include "IndexBuffer.h" +#include "Renderer.h" + +IndexBuffer::IndexBuffer(const unsigned int* data, unsigned int count) + : m_Count(count) +{ + //ASSERT(sizeof(unsigned int) == sizeof(GLuint)); + // + GLCall(glGenBuffers(1, &m_RendererID)); + GLCall(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_RendererID)); //select buffer called 'buffer' + GLCall(glBufferData(GL_ELEMENT_ARRAY_BUFFER, count * sizeof(GLuint), data, GL_STATIC_DRAW)); // assigne buffer size, static as we use many times, but does not change +} + +IndexBuffer::~IndexBuffer() +{ + GLCall(glDeleteBuffers(1, &m_RendererID)); +} + +void IndexBuffer::Bind() const +{ + GLCall(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_RendererID)); //select buffer called 'buffer' +} + +void IndexBuffer::Unbind() const +{ + GLCall(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); //select buffer called 'buffer' +} diff --git a/src/IndexBuffer.h b/src/IndexBuffer.h new file mode 100644 index 0000000..3b663e4 --- /dev/null +++ b/src/IndexBuffer.h @@ -0,0 +1,16 @@ +#pragma once + +class IndexBuffer +{ +private: + unsigned int m_RendererID; + unsigned int m_Count; +public: + IndexBuffer(const unsigned int* data, unsigned int count); + ~IndexBuffer(); + + void Bind() const; + void Unbind() const; + + inline unsigned int GetCount() const { return m_Count; } +}; diff --git a/src/Renderer.cpp b/src/Renderer.cpp new file mode 100644 index 0000000..552d629 --- /dev/null +++ b/src/Renderer.cpp @@ -0,0 +1,19 @@ +#include "Renderer.h" + + + +void GLClearError() { + while (glGetError() != GL_NO_ERROR); + +} + +bool GLLogCall() { + Color::Modifier red(Color::FG_RED); + Color::Modifier def(Color::FG_DEFAULT); + while (GLenum error = glGetError()) { + std::cout << red << "[OpenGL Error] (" << error << ")" << def << std::endl; //if error, it will return a number, this needs to be converted to hex to then look up that value inn GL docs + return false; + } + return true; +} + diff --git a/src/Renderer.h b/src/Renderer.h new file mode 100644 index 0000000..4bc906f --- /dev/null +++ b/src/Renderer.h @@ -0,0 +1,37 @@ +#pragma once +#include "colormod.h" +#include + +#include + + +#if defined(_MSC_VER) // Microsoft Visual C++ + #include + #define DEBUG_BREAK() __debugbreak() +#elif defined(__i386__) || defined(__x86_64__) + // Use inline assembly for x86/x86_64 + #define DEBUG_BREAK() __asm__ volatile("int3") +#else + // Fallback on non-x86 platforms + #include + #define DEBUG_BREAK() raise(SIGTRAP) +#endif + +// ASSERT macro that shows file, line, and the failed expression +#define ASSERT(x) \ + do { \ + if (!(x)) { \ + std::cerr << "Assertion Failed: " << #x << '\n' \ + << "File: " << __FILE__ << '\n' \ + << "Line: " << __LINE__ << std::endl; \ + DEBUG_BREAK(); \ + } \ + } while (false) + +#define GLCall(x) GLClearError();\ + x;\ + ASSERT(GLLogCall()) + + +void GLClearError(); +bool GLLogCall(); diff --git a/src/VertexArray.cpp b/src/VertexArray.cpp new file mode 100644 index 0000000..c933449 --- /dev/null +++ b/src/VertexArray.cpp @@ -0,0 +1,39 @@ +#include "VertexArray.h" +#include "VertexBuffer.h" +#include "Renderer.h" +#include "VertexBufferLayout.h" + +VertexArray::VertexArray() +{ + GLCall(glGenVertexArrays(1, &m_RendererID)); + if (!m_RendererID) { + std::cerr << "Failed to generate VAO" << std::endl; + } + + +} +VertexArray::~VertexArray() +{ + GLCall(glDeleteVertexArrays(1, &m_RendererID)); +} + +void VertexArray::AddBuffer(const VertexBuffer& vb, const VertexBufferLayout& layout) +{ + Bind(); + vb.Bind(); + const auto& elements = layout.GetElements(); + GLsizei offset = 0; + for (unsigned int i = 0; i < elements.size(); i++) { + const auto& element = elements[i]; + GLCall(glEnableVertexAttribArray(i)); + GLCall(glVertexAttribPointer(i, element.count, element.type, element.normalized, layout.GetStride(), (const void*)offset)); + offset += element.count * VertexBufferElement::GetSizeOfType(element.type); + } +} + +void VertexArray::Bind() const { + GLCall(glBindVertexArray(m_RendererID)); +} +void VertexArray::Unbind() const { + GLCall(glBindVertexArray(0)); +} diff --git a/src/VertexArray.h b/src/VertexArray.h new file mode 100644 index 0000000..31a711e --- /dev/null +++ b/src/VertexArray.h @@ -0,0 +1,18 @@ +#pragma once +#include "VertexBuffer.h" +#include "VertexBufferLayout.h" + +class VertexArray +{ + private: + unsigned int m_RendererID; + + public: + VertexArray(); + ~VertexArray(); + void AddBuffer(const VertexBuffer& vb, const VertexBufferLayout& layout); + + void Bind() const; + void Unbind() const; +}; + diff --git a/src/VertexBuffer.cpp b/src/VertexBuffer.cpp new file mode 100644 index 0000000..12fb9d9 --- /dev/null +++ b/src/VertexBuffer.cpp @@ -0,0 +1,24 @@ +#include "VertexBuffer.h" +#include "Renderer.h" + +VertexBuffer::VertexBuffer(const void* data, unsigned int size) +{ + GLCall(glGenBuffers(1, &m_RendererID)); + GLCall(glBindBuffer(GL_ARRAY_BUFFER, m_RendererID)); //select buffer called 'buffer' + GLCall(glBufferData(GL_ARRAY_BUFFER, size, data, GL_STATIC_DRAW)); // assigne buffer size, static as we use many times, but does not change +} + +VertexBuffer::~VertexBuffer() +{ + GLCall(glDeleteBuffers(1, &m_RendererID)); +} + +void VertexBuffer::Bind() const +{ + GLCall(glBindBuffer(GL_ARRAY_BUFFER, m_RendererID)); //select buffer called 'buffer' +} + +void VertexBuffer::Unbind() const +{ + GLCall(glBindBuffer(GL_ARRAY_BUFFER, 0)); //select buffer called 'buffer' +} diff --git a/src/VertexBuffer.h b/src/VertexBuffer.h new file mode 100644 index 0000000..75d8907 --- /dev/null +++ b/src/VertexBuffer.h @@ -0,0 +1,13 @@ +#pragma once + +class VertexBuffer +{ +private: + unsigned int m_RendererID; +public: + VertexBuffer(const void* data, unsigned int size); + ~VertexBuffer(); + + void Bind() const; + void Unbind() const; +}; diff --git a/src/VertexBufferLayout.h b/src/VertexBufferLayout.h new file mode 100644 index 0000000..27b18ba --- /dev/null +++ b/src/VertexBufferLayout.h @@ -0,0 +1,56 @@ +#ifndef VERTEXBUFFERLAYOUT_H +#define VERTEXBUFFERLAYOUT_H +#include +#include "Renderer.h" +struct VertexBufferElement { + unsigned int type; + unsigned int count; + unsigned char normalized; + + VertexBufferElement(unsigned int type, unsigned int count, unsigned char normalized) + : type(type), count(count), normalized(normalized) {} + + static unsigned int GetSizeOfType(unsigned int type) { + switch (type) { + case GL_FLOAT: return sizeof(GLfloat); + case GL_UNSIGNED_INT: return sizeof(GLuint); + case GL_UNSIGNED_BYTE: return sizeof(GLubyte); + } + return 0; + } +}; + +class VertexBufferLayout { +public: + VertexBufferLayout() : m_Stride(0) {} + + template + void Push(int count); + + inline const std::vector& GetElements() const { return m_Elements; } + inline unsigned int GetStride() const { return m_Stride; } + +private: + std::vector m_Elements; + unsigned int m_Stride; +}; + +// Explicit specializations +template<> +inline void VertexBufferLayout::Push(int count) { + m_Elements.push_back({ GL_FLOAT, static_cast(count), GL_FALSE }); + m_Stride += VertexBufferElement::GetSizeOfType(GL_FLOAT); +} + +template<> +inline void VertexBufferLayout::Push(int count) { + m_Elements.push_back({ GL_UNSIGNED_INT,static_cast(count), GL_FALSE }); + m_Stride += VertexBufferElement::GetSizeOfType(GL_UNSIGNED_INT); +} + +template<> +inline void VertexBufferLayout::Push(int count) { + m_Elements.push_back({ GL_UNSIGNED_BYTE, static_cast(count), GL_TRUE }); + m_Stride += VertexBufferElement::GetSizeOfType(GL_UNSIGNED_BYTE); +} +#endif // VERTEXBUFFERLAYOUT_H \ No newline at end of file diff --git a/src/colormod.h b/src/colormod.h index 181d550..5ba4e10 100644 --- a/src/colormod.h +++ b/src/colormod.h @@ -1,3 +1,5 @@ +#ifndef COLORMOD_H +#define COLORMOD_H #include namespace Color { enum Code { @@ -20,3 +22,4 @@ namespace Color { } }; } +#endif // COLORMOD_H \ No newline at end of file diff --git a/src/sdl.cpp b/src/sdl.cpp index bf8d963..bf76e53 100644 --- a/src/sdl.cpp +++ b/src/sdl.cpp @@ -1,5 +1,6 @@ #include // Include GLEW before ? #include "sdl.hpp" +#include "VertexBufferLayout.h" #include "colormod.h" #include #include @@ -54,9 +55,10 @@ SdlWindow::SdlWindow(const char* title, int width, int height) m_glContext(nullptr), m_windowedWidth(width), m_windowedHeight(height), - r(0.0f), + r(0.5f), location(), - increment(0.05f) + increment(0.05f), + ib(nullptr, 0) { // 1. Set attributes SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); @@ -95,6 +97,7 @@ SdlWindow::SdlWindow(const char* title, int width, int height) // 5. Set vsync (optional) SDL_GL_SetSwapInterval(1); //Vsync + SdlWindow::GLClearError(); // 6. Mark as running m_isRunning = true; @@ -111,28 +114,32 @@ SdlWindow::SdlWindow(const char* title, int width, int height) 0, 1, 2, 2, 3, 0 }; - //vertex buffer - unsigned int buffer; - GLCall(glGenBuffers(1, &buffer)); - GLCall(glBindBuffer(GL_ARRAY_BUFFER, buffer)); //select buffer called 'buffer' - GLCall(glBufferData(GL_ARRAY_BUFFER, 6 * 2 * sizeof(float), positions, GL_STATIC_DRAW)); // assigne buffer size, static as we use many times, but does not change - //vertext attributes / layout - GLCall(glEnableVertexAttribArray(0)); - GLCall(glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 2, 0)); - //indext beffer - unsigned int ibo; //indext buffer object - GLCall(glGenBuffers(1, &ibo)); - GLCall(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo)); //select buffer called 'buffer' - GLCall(glBufferData(GL_ELEMENT_ARRAY_BUFFER, 6 * sizeof(unsigned int), indices, GL_STATIC_DRAW)); // assigne buffer size, static as we use many times, but does not change + + unsigned int vao; //vertext array object + GLCall(glGenVertexArrays(1, &vao)); + GLCall(glBindVertexArray(vao)); + + VertexArray va; + VertexBuffer vb(positions, 4 * 2 * sizeof(float)); + VertexBufferLayout layout; + layout.Push(2); + va.AddBuffer(vb, layout); + + IndexBuffer ib(indices, 6); ShaderProgramSource source = parseShader("res/shaders/Basic.shader"); - unsigned int shader = createShader(source.VertexSource, source.FragmentSource); - GLCall(glUseProgram(shader)); + unsigned int m_ShaderID = createShader(source.VertexSource, source.FragmentSource); + GLCall(glUseProgram(m_ShaderID)); - GLCall(int location = glGetUniformLocation(shader, "u_Color")); + GLCall(int location = glGetUniformLocation(m_ShaderID, "u_Color")); ASSERT(location != -1); // -1 is an error GLCall(glUniform4f(location, 0.8f, 0.3f, 0.8f, 1.0f)); + GLCall(glBindVertexArray(0)); + GLCall(glUseProgram(0)); + GLCall(glBindBuffer(GL_ARRAY_BUFFER, 0)); + GLCall(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); + } @@ -153,7 +160,12 @@ SdlWindow::~SdlWindow() { SDL_DestroyWindow(m_window); m_window = nullptr; } + if (shader) { + glDeleteProgram(shader); + shader = 0; + } + delete &ib; SDL_Quit(); } @@ -226,10 +238,15 @@ void SdlWindow::update() { void SdlWindow::render() { // Use GL calls instead of SDL’s renderer GLCall(glClearColor(0.0f, 0.0f, 0.0f, 1.0f)); //background + // GLCall(glClear(GL_COLOR_BUFFER_BIT)); + + GLCall(glUseProgram(shader)); + GLCall(glUniform4f(location, r, 0.3f, 0.8f, 1.0f)); + va.Bind(); + ib.Bind(); // TODO: Draw with OpenGL here (shaders, triangles, etc.) - GLCall(glUniform4f( location, r, 0.3f, 0.8f, 1.0f)); //glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, nullptr); GLCall(glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, nullptr)); //macro assert for debugging diff --git a/src/sdl.hpp b/src/sdl.hpp index fc049f2..469a563 100644 --- a/src/sdl.hpp +++ b/src/sdl.hpp @@ -9,10 +9,10 @@ #include // or if using GLAD #endif -//#include -#include #include -#include +#include "IndexBuffer.h" +#include "VertexBufferLayout.h" +#include "VertexArray.h" // Forward declaration of classes and structs if needed // class SomethingElse; @@ -54,14 +54,19 @@ private: int m_windowedWidth; // stored width before fullscreen int m_windowedHeight; // stored height before fullscreen float r; - int location; SDL_GLContext m_glContext; float increment; // temp shader stuff std::string vetexShader; std::string fragmentShader; - unsigned int shader; - + unsigned int buffer; + unsigned int ibo; + unsigned int vao; + IndexBuffer ib; // pointer, no default constructor needed + unsigned int shader; + unsigned int location; + VertexBufferLayout layout; + VertexArray va; // Private methods void processEvents();