diff options
author | Phil Jones <philj56@gmail.com> | 2021-10-26 23:15:46 +0100 |
---|---|---|
committer | Phil Jones <philj56@gmail.com> | 2021-10-26 23:15:46 +0100 |
commit | 42679f7e53364e8de8de665ac7ba9b7e2dd1970b (patch) | |
tree | 14e145ea833097aba91f516f4cd124e49f5a2a14 /src/gl.c | |
parent | c1e7ea47b25cdf7bf316ce522f9aa438b0b657c7 (diff) |
Add PNG background.
Diffstat (limited to 'src/gl.c')
-rw-r--r-- | src/gl.c | 177 |
1 files changed, 177 insertions, 0 deletions
diff --git a/src/gl.c b/src/gl.c new file mode 100644 index 0000000..8187438 --- /dev/null +++ b/src/gl.c @@ -0,0 +1,177 @@ +#include <epoxy/gl.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "client.h" +#include "gl.h" +#include "log.h" + +#define max(a, b) ((a) > (b) ? (a) : (b)) + +static void load_shader(GLuint shader, const char *filename); +static GLuint create_shader_program(const char *vert, const char *frag); + +void gl_initialise(struct client_state *state) +{ + if (state->background_image.buffer == NULL) { + return; + } + + struct gl *gl = &state->gl; + + /* Compile and link the shader programs */ + gl->shader = create_shader_program( + SHADER_PATH "vert.vert", + SHADER_PATH "frag.frag" + ); + glUseProgram(gl->shader); + glUniform1i(glGetUniformLocation(gl->shader, "tex"), 0); + + /* Create a vertex buffer for a quad filling the screen */ + float vertices[] = { + // Position Texcoords + -1.0f, 1.0f, 0.0f, 0.0f, // Top-left + 1.0f, 1.0f, 1.0f, 0.0f, // Top-right + 1.0f, -1.0f, 1.0f, 1.0f, // Bottom-right + -1.0f, -1.0f, 0.0f, 1.0f // Bottom-left + }; + + glGenBuffers(1, &gl->vbo); + glBindBuffer(GL_ARRAY_BUFFER, gl->vbo); + glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); + + /* Create a vertex array and enable vertex attributes for the shaders. */ + glGenVertexArrays(1, &gl->vao); + glBindVertexArray(gl->vao); + + GLint posAttrib = glGetAttribLocation(gl->shader, "position"); + glVertexAttribPointer(posAttrib, 2, GL_FLOAT, GL_FALSE, 4*sizeof(float), 0); + glEnableVertexAttribArray(posAttrib); + + GLint texAttrib = glGetAttribLocation(gl->shader, "texcoord"); + glVertexAttribPointer(texAttrib, 2, GL_FLOAT, GL_FALSE, 4*sizeof(float), (void *)(2*sizeof(float))); + glEnableVertexAttribArray(texAttrib); + + glBindVertexArray(0); + + /* Create the element buffer object that will actually be drawn via + * glDrawElements(). */ + GLuint elements[] = { + 0, 1, 2, + 2, 3, 0 + }; + + glGenBuffers(1, &gl->ebo); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, gl->ebo); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(elements), elements, GL_STATIC_DRAW); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + glBindBuffer(GL_ARRAY_BUFFER, 0); + + /* Create the texture we'll draw to */ + glGenTextures(1, &gl->texture); + glBindTexture(GL_TEXTURE_2D, gl->texture); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, state->background_image.width, state->background_image.height, 0, GL_RGBA, + GL_UNSIGNED_BYTE, (GLvoid *)state->background_image.buffer); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + /* Bind the actual bits we'll be using to render */ + glBindVertexArray(gl->vao); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, gl->ebo); +} + +void gl_draw(struct client_state *state) +{ + glClearColor( + state->background_color.r, + state->background_color.g, + state->background_color.b, + state->background_color.a + ); + glClear(GL_COLOR_BUFFER_BIT); + + if (state->background_image.buffer == NULL) { + return; + } + + double scale = max( + (double)state->width / state->background_image.width, + (double)state->height / state->background_image.height + ); + uint32_t width = (uint32_t)(scale * state->background_image.width); + uint32_t height = (uint32_t)(scale * state->background_image.height); + int32_t x = -((int32_t)width - (int32_t)state->width) / 2; + int32_t y = -((int32_t)height - (int32_t)state->height) / 2; + printf("%d, %d\n", x, y); + glViewport(x, y, width, height); + glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); +} + +void load_shader(GLuint shader, const char *filename) +{ + errno = 0; + FILE *fp = fopen(filename, "rb"); + if (!fp) { + log_error("Failed to load shader %s: %s.\n", filename, strerror(errno)); + exit(EXIT_FAILURE); + } + if (fseek(fp, 0, SEEK_END) != 0) { + log_error("Failed to load shader %s: %s.\n", filename, strerror(errno)); + fclose(fp); + exit(EXIT_FAILURE); + } + long size = ftell(fp); + if (size <= 0) { + log_error("Failed to load shader %s: %s.\n", filename, strerror(errno)); + fclose(fp); + exit(EXIT_FAILURE); + } + unsigned long usize = (unsigned long) size; + GLchar *source = malloc(usize + 1); + rewind(fp); + if (fread(source, 1, usize, fp) != usize) { + log_error("Failed to load shader %s: %s.\n", filename, strerror(errno)); + fclose(fp); + exit(EXIT_FAILURE); + } + fclose(fp); + source[usize] = '\0'; + glShaderSource(shader, 1, (const GLchar *const *)&source, NULL); + free(source); + + glCompileShader(shader); + + GLint status; + glGetShaderiv(shader, GL_COMPILE_STATUS, &status); + if (status != GL_TRUE) { + log_error("Failed to compile shader %s!\n", filename); + + GLint info_length = 0; + glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &info_length); + if (info_length > 1) { + char *log = malloc((unsigned)info_length * sizeof(*log)); + glGetShaderInfoLog(shader, info_length, NULL, log); + log_error("\t%s\n", log); + free(log); + } + exit(EXIT_FAILURE); + } +} + +GLuint create_shader_program(const char *vert, const char *frag) +{ + GLuint vertex_shader = glCreateShader(GL_VERTEX_SHADER); + load_shader(vertex_shader, vert); + + GLuint fragment_shader = glCreateShader(GL_FRAGMENT_SHADER); + load_shader(fragment_shader, frag); + + GLuint shader = glCreateProgram(); + glAttachShader(shader, vertex_shader); + glAttachShader(shader, fragment_shader); + glLinkProgram(shader); + return shader; +} |