From 16783dc09035399c242374774161172a8a608e6e Mon Sep 17 00:00:00 2001 From: Abdelrahman Date: Sun, 15 Sep 2024 21:49:43 +0100 Subject: [PATCH] Draw first triangle --- src/main.cc | 134 ++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 130 insertions(+), 4 deletions(-) diff --git a/src/main.cc b/src/main.cc index fa2cbac..4109fed 100644 --- a/src/main.cc +++ b/src/main.cc @@ -6,6 +6,8 @@ #include #include #include +#include +#include #define WINDOW_WIDTH 1280 #define WINDOW_HEIGHT 720 @@ -22,11 +24,29 @@ struct App { SDL_Window *window; SDL_GLContext context; SDL_Event event; + + // Defines the different attributes in the vertex data and how to access them. + // Think of it as a specification or a C struct that defines the types of data stored for each + // vertex. + GLuint vao; + + // The buffer that contains all the vertex data + // e.g. assume 3 vertices with position, colour and uv + // ----------------------------------------------------------------------------------- + // | position1 | colour1 | uv1 | position2 | colour2 | uv2 | position3 | colour3 | uv3 | + // ----------------------------------------------------------------------------------- + GLuint vbo; + + GLuint shader_program; }; -int init (App &app); -void run_main_loop(App &app); -void cleanup (App &app); +int init (App &app); +void create_vertex_spec (App &app); +void create_graphics_pipeline(App &app); +void run_main_loop (App &app); +void cleanup (App &app); +GLuint create_shader_program (const std::string &vertex_shader_source, const std::string &fragment_shader_source); +GLuint compile_shader (GLuint type, const std::string &source); int main() { App app = {}; @@ -35,6 +55,10 @@ int main() { return result; } + create_vertex_spec(app); + + create_graphics_pipeline(app); + run_main_loop(app); cleanup(app); @@ -49,7 +73,7 @@ int init(App &app) { } uint32_t flags = SDL_WINDOW_SHOWN | SDL_WINDOW_OPENGL | SDL_WINDOW_ALLOW_HIGHDPI; - app.window = SDL_CreateWindow("Window", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, + app.window = SDL_CreateWindow("Window", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, WINDOW_WIDTH, WINDOW_HEIGHT, flags); if (!app.window) { fprintf(stderr, "Failed to create window: %s", SDL_GetError()); @@ -84,6 +108,57 @@ int init(App &app) { return EXIT_CODE_SUCCESS; } +void create_vertex_spec(App &app) { + const std::vector vertices = { + -0.8f, -0.8f, 0.0f, + 0.8f, -0.8f, 0.0f, + 0.0f, 0.8f, 0.0f, + }; + + glGenVertexArrays(1, &app.vao); + glBindVertexArray(app.vao); + + glGenBuffers(1, &app.vbo); + glBindBuffer(GL_ARRAY_BUFFER, app.vbo); + glBufferData(GL_ARRAY_BUFFER, + vertices.size() * sizeof(GLfloat), + (void *)vertices.data(), + GL_STATIC_DRAW + ); + + // Defines the vertex attributes and their indices. This defines the attribute for the currently + // bound VAO + glEnableVertexAttribArray(0); + + // Defines the number of components for the attribute, its type, the stride between the component + // for each vertex and the offset of the first instance of the component in the buffer + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void *)0); + + // Cleanup. The order matters. Unbind VAO first then disable the attributes. If you do it the + // other way around, the VAO will store that the attributes are disabled and nothing will be + // drawn during the rendering stage + glBindVertexArray(0); + glBindBuffer(GL_ARRAY_BUFFER, 0); + glDisableVertexAttribArray(0); +} + +void create_graphics_pipeline(App &app) { + const std::string vs_source = + "#version 460 core\n" + "in vec4 position;\n" + "void main() {\n" + " gl_Position = vec4(position.x, position.y, position.z, position.w);\n" + "}"; + const std::string fs_source = + "#version 460 core\n" + "out vec4 color;\n" + "void main() {\n" + " color = vec4(0.93f, 0.42f, 0.35f, 1.0f);\n" + "}"; + + app.shader_program = create_shader_program(vs_source, fs_source); +} + void run_main_loop(App &app) { bool running = true; @@ -96,6 +171,23 @@ void run_main_loop(App &app) { } } + // Pre draw setup + glDisable(GL_DEPTH_TEST); + glDisable(GL_CULL_FACE); + + glViewport (0, 0, WINDOW_WIDTH, WINDOW_HEIGHT); + glClearColor(0.36f, 0.34f, 0.42f, 1.0f); + glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT); + + glUseProgram(app.shader_program); + // End pre draw setup + + // Draw call + glBindVertexArray(app.vao); + glBindBuffer(GL_ARRAY_BUFFER, app.vbo); // Is this actually needed? + glDrawArrays(GL_TRIANGLES, 0, 3); + // End draw call + SDL_GL_SwapWindow(app.window); } } @@ -105,3 +197,37 @@ void cleanup(App &app) { SDL_DestroyWindow(app.window); SDL_Quit(); } + +GLuint create_shader_program(const std::string &vertex_shader_source, const std::string &fragment_shader_source) { + GLuint shader_program = glCreateProgram(); + GLuint vertex_shader = compile_shader(GL_VERTEX_SHADER, vertex_shader_source); + GLuint fragment_shader = compile_shader(GL_FRAGMENT_SHADER, fragment_shader_source); + + glAttachShader(shader_program, vertex_shader); + glAttachShader(shader_program, fragment_shader); + glLinkProgram (shader_program); + + // Validate the shader_program + glValidateProgram(shader_program); + + return shader_program; +} + +GLuint compile_shader(GLuint type, const std::string &source) { + GLuint shader = glCreateShader(type); + const char *shader_source = source.c_str(); + + glShaderSource (shader, 1, &shader_source, NULL); + glCompileShader(shader); + + GLint compile_status; + glGetShaderiv(shader, GL_COMPILE_STATUS, &compile_status); + if (compile_status != GL_TRUE) { + GLsizei log_length = 0; + GLchar message[1024]; + glGetShaderInfoLog(shader, 1024, &log_length, message); + fprintf(stderr, "%s\n", message); + } + + return shader; +}