#include "render.h"
#include "GLFW/glfw3.h"
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
char* file_read(const char* path) {
FILE* f = fopen(path, "r");
char* buf = NULL;
if (!f) {
char data[512];
snprintf(data, 512, "Failed to open file at path '%s' (%i)", path, errno);
ERROR(data);
return buf;
}
fseek(f, 0, SEEK_END);
size_t length = ftell(f);
fseek(f, 0, SEEK_SET);
buf = malloc(length);
if (buf == NULL) return buf;
fread(buf, 1, length, f);
fclose(f);
return buf;
}
static float halfScreenTop[4 * 5] = {
1.0f, 1.0f, 0.0f, 0.0f, 1.0f,
1.0f, -1.0f, 0.0f, 1.0f, 1.0f,
-1.0f, -1.0f, 0.0f, 1.0f, 0.0f,
-1.0f, 1.0f, 0.0f, 0.0f, 0.0f
};
static unsigned int indices[6] = {
0, 1, 3,
1, 2, 3
};
void fb_resize(GLFWwindow* win, int w, int h) {
(void)win;
glViewport(0, 0, w, h);
}
renderer_t renderer_create(const char* title, u16 x, u16 y) {
if (glfwInit() == GL_FALSE) { // i.e. init. failed
const char* msg;
glfwGetError(&msg);
ERROR(msg);
exit(1);
}
INFO("GLFW ready!");
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
#ifdef __APPLE__
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
#endif
GLFWwindow* win = glfwCreateWindow(x, y, title, NULL, NULL);
if (win == NULL) {
const char* msg;
glfwGetError(&msg);
ERROR("Could not create a window handle:");
ERROR(msg);
glfwTerminate();
exit(1);
}
glfwSetFramebufferSizeCallback(win, fb_resize);
glfwMakeContextCurrent(win);
glfwSwapInterval(1);
int loadedVersion = gladLoadGL(glfwGetProcAddress);
if (loadedVersion == 0) {
const char* msg;
glfwGetError(&msg);
ERROR("GLAD could not initialise OpenGL function pointers.");
if (msg != NULL) {
WARN(msg);
}
WARN("Do you have an OpenGL-3.3-core compatible card and/or drivers?");
glfwTerminate();
exit(1);
}
INFO("GLAD Successfully loaded OpenGL-3.3-core");
int w, h;
glfwGetFramebufferSize(win, &w, &h);
glViewport(0, 0, w, h);
GLuint vao;
GLuint bufs[2];
glGenVertexArrays(1, &vao);
glGenBuffers(2, bufs);
glBindVertexArray(vao);
glBindBuffer(GL_ARRAY_BUFFER, bufs[0]);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bufs[1]);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
glBufferData(GL_ARRAY_BUFFER, sizeof(halfScreenTop), halfScreenTop, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(float) * 5, (void*)0);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 5, (void*)(sizeof(float) * 3));
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
GLuint text = 0;
// doc: glGenTextures is not documented as faillable anywhere so fuck it we ball :)
glGenTextures(0, &text);
glBindTexture(GL_TEXTURE_2D, text);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, placeholderTexture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
GLint vertS, fragS;
char* const pres_vs = file_read("present.vs");
char* const pres_fs = file_read("present.fs");
vertS = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertS, 1, (const char* const*)(&pres_vs), NULL);
glCompileShader(vertS);
GLint shaderOK = 1;
glGetShaderiv(vertS, GL_COMPILE_STATUS, &shaderOK);
if (!shaderOK) {
char err[512];
glGetShaderInfoLog(vertS, 512, NULL, err);
ERROR("Vertex shader compile failed! GL Reports:");
ERROR(err);
}
fragS = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragS, 1, (const char* const*)(&pres_fs), NULL);
glCompileShader(fragS);
shaderOK = 1;
glGetShaderiv(fragS, GL_COMPILE_STATUS, &shaderOK);
if (!shaderOK) {
char err[512];
glGetShaderInfoLog(fragS, 512, NULL, err);
ERROR("Fragment shader compile failed! GL Reports:");
ERROR(err);
}
GLuint program = glCreateProgram();
glAttachShader(program, vertS);
glAttachShader(program, fragS);
glLinkProgram(program);
GLint programOK = 1;
glGetProgramiv(program, GL_LINK_STATUS, &programOK);
if (!programOK) {
char err[512];
glGetProgramInfoLog(program, 512, NULL, err);
ERROR("Failed to link shader program, GL Reports:");
ERROR(err);
}
glDeleteShader(fragS);
glDeleteShader(vertS);
free(pres_vs);
free(pres_fs);
return (renderer_t){
.win = win,
.backingTexture = text,
.shaders = program,
.vao = vao,
.bufs = bufs,
};
}
int renderer_healthy(const renderer_t* self) {
glfwPollEvents();
return
!glfwWindowShouldClose(self->win);
}
void renderer_present(const renderer_t* self, const canvas_t* canvas) {
glBindTexture(GL_TEXTURE_2D, self->backingTexture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, canvas->w, canvas->h, 0, GL_RGBA, GL_UNSIGNED_BYTE, canvas->data);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, self->backingTexture);
glUseProgram(self->shaders);
glBindVertexArray(self->vao);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
glfwSwapBuffers(self->win);
}
void renderer_destroy(renderer_t* self) {
glDeleteProgram(self->shaders);
glDeleteBuffers(2, self->bufs);
glDeleteVertexArrays(1, &(self->vao));
glfwDestroyWindow(self->win);
glfwTerminate();
}