|
|
@@ -1,12 +1,157 @@
|
|
|
#include "Window.h"
|
|
|
|
|
|
+#include <GL/glew.h>
|
|
|
#include <GLFW/glfw3.h>
|
|
|
#include <stdio.h>
|
|
|
+#include <stdlib.h>
|
|
|
+#include <string.h>
|
|
|
|
|
|
#define SET_ERROR(format, ...) \
|
|
|
snprintf(e.text, sizeof(e.text), format __VA_OPT__(, ) __VA_ARGS__);
|
|
|
|
|
|
+#define WIDTH 240
|
|
|
+#define HEIGHT 160
|
|
|
+#define STR(x) STR2(x)
|
|
|
+#define STR2(x) #x
|
|
|
+
|
|
|
+typedef struct {
|
|
|
+ i32 key;
|
|
|
+ i32 downTime;
|
|
|
+ bool isDown;
|
|
|
+ char name[7];
|
|
|
+} ButtonData;
|
|
|
+
|
|
|
+static_assert(sizeof(ButtonData) == 16);
|
|
|
+
|
|
|
static GLFWwindow* window = nullptr;
|
|
|
+static GLuint program = 0;
|
|
|
+static GLuint vShader = 0;
|
|
|
+static GLuint fShader = 0;
|
|
|
+static GLuint vbo = 0;
|
|
|
+static GLuint vba = 0;
|
|
|
+static GLuint texture = 0;
|
|
|
+static int width = 0;
|
|
|
+static int height = 0;
|
|
|
+static u8 pixels[HEIGHT][WIDTH][3] = {};
|
|
|
+
|
|
|
+static ButtonData buttons[] = {
|
|
|
+ [LEFT] = {0, 0, 0, "Left"}, [RIGHT] = {0, 0, 0, "Right"},
|
|
|
+ [UP] = {0, 0, 0, "Up"}, [DOWN] = {0, 0, 0, "Down"},
|
|
|
+ [SELECT] = {0, 0, 0, "Select"}, [START] = {0, 0, 0, "Start"},
|
|
|
+ [A] = {0, 0, 0, "A"}, [B] = {0, 0, 0, "B"},
|
|
|
+ [X] = {0, 0, 0, "X"}, [Y] = {0, 0, 0, "Y"},
|
|
|
+ [L] = {0, 0, 0, "L"}, [R] = {0, 0, 0, "R"}};
|
|
|
+static constexpr size_t BUTTONS = sizeof(buttons) / sizeof(ButtonData);
|
|
|
+
|
|
|
+// clang-format off
|
|
|
+static const GLchar* VERTEX_SHADER_CODE =
|
|
|
+ "#version 430\n"
|
|
|
+ "layout (location = 0) in vec2 pos;\n"
|
|
|
+ "uniform vec2 size;\n"
|
|
|
+ "out vec2 tex;\n"
|
|
|
+ "void main(void) {\n"
|
|
|
+ " tex = pos;\n"
|
|
|
+ " vec2 w = vec2(" STR(WIDTH) ", " STR(HEIGHT) ");\n"
|
|
|
+ " ivec2 f = ivec2(size / w);\n"
|
|
|
+ " f = ivec2(min(f.x, f.y), min(f.x, f.y));\n"
|
|
|
+ " vec2 f2 = (f * w) / size;\n"
|
|
|
+ " gl_Position = vec4(pos * f2, 0.0, 1.0);\n"
|
|
|
+ "}\n";
|
|
|
+// clang-format on
|
|
|
+
|
|
|
+static const GLchar* FRAGMENT_SHADER_CODE =
|
|
|
+ "#version 430\n"
|
|
|
+ "layout (binding = 0) uniform sampler2D image;\n"
|
|
|
+ "in vec2 tex;\n"
|
|
|
+ "out vec4 color;\n"
|
|
|
+ "void main(void) {\n"
|
|
|
+ " vec2 c = (tex + vec2(1)) * 0.5;\n"
|
|
|
+ " color = texture(image, vec2(c.x, 1 - c.y));\n"
|
|
|
+ "}\n";
|
|
|
+
|
|
|
+static void onKeyEvent(GLFWwindow*, int key, int, int action, int) {
|
|
|
+ if(action == GLFW_RELEASE) {
|
|
|
+ ButtonData* end = buttons + BUTTONS;
|
|
|
+ for(ButtonData* c = buttons; c != end; c++) {
|
|
|
+ if(c->key == key) {
|
|
|
+ c->isDown = false;
|
|
|
+ c->downTime = 0;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else if(action == GLFW_PRESS) {
|
|
|
+ ButtonData* end = buttons + BUTTONS;
|
|
|
+ for(ButtonData* c = buttons; c != end; c++) {
|
|
|
+ if(c->key == key) {
|
|
|
+ c->isDown = true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+static void onWindowResize(GLFWwindow*, int w, int h) {
|
|
|
+ width = w;
|
|
|
+ height = h;
|
|
|
+}
|
|
|
+
|
|
|
+static void tickButtons() {
|
|
|
+ ButtonData* end = buttons + BUTTONS;
|
|
|
+ for(ButtonData* c = buttons; c != end; c++) {
|
|
|
+ c->downTime += c->isDown;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+i32 isButtonDown(Button b) {
|
|
|
+ return buttons[b].isDown;
|
|
|
+}
|
|
|
+
|
|
|
+i32 getButtonDownTime(Button b) {
|
|
|
+ return buttons[b].downTime;
|
|
|
+}
|
|
|
+
|
|
|
+static Error checkShaderErrors(const char* name, GLuint shader) {
|
|
|
+ Error e = {};
|
|
|
+ GLint compiled = 0;
|
|
|
+ glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
|
|
|
+ if(compiled == 0) {
|
|
|
+ GLchar buffer[512];
|
|
|
+ glGetShaderInfoLog(shader, sizeof(buffer), nullptr, buffer);
|
|
|
+ SET_ERROR("Failed %s shader compilation: %s\n", name, buffer);
|
|
|
+ }
|
|
|
+ return e;
|
|
|
+}
|
|
|
+
|
|
|
+static Error compileProgram() {
|
|
|
+ vShader = glCreateShader(GL_VERTEX_SHADER);
|
|
|
+ glShaderSource(vShader, 1, &VERTEX_SHADER_CODE, NULL);
|
|
|
+ glCompileShader(vShader);
|
|
|
+ Error e = checkShaderErrors("vertex", vShader);
|
|
|
+ if(hasError(&e)) {
|
|
|
+ return e;
|
|
|
+ }
|
|
|
+
|
|
|
+ fShader = glCreateShader(GL_FRAGMENT_SHADER);
|
|
|
+ glShaderSource(fShader, 1, &FRAGMENT_SHADER_CODE, NULL);
|
|
|
+ glCompileShader(fShader);
|
|
|
+ e = checkShaderErrors("fragment", fShader);
|
|
|
+ if(hasError(&e)) {
|
|
|
+ return e;
|
|
|
+ }
|
|
|
+
|
|
|
+ program = glCreateProgram();
|
|
|
+ glAttachShader(program, vShader);
|
|
|
+ glAttachShader(program, fShader);
|
|
|
+ glLinkProgram(program);
|
|
|
+ GLint compiled = 0;
|
|
|
+ glGetProgramiv(program, GL_LINK_STATUS, &compiled);
|
|
|
+ if(compiled == 0) {
|
|
|
+ GLchar buffer[512];
|
|
|
+ glGetProgramInfoLog(program, sizeof(buffer), nullptr, buffer);
|
|
|
+ SET_ERROR("Failed program linking: %s\n", buffer);
|
|
|
+ return e;
|
|
|
+ }
|
|
|
+ glUseProgram(program);
|
|
|
+ return e;
|
|
|
+}
|
|
|
|
|
|
Error windowInit(const WindowSettings* ws) {
|
|
|
Error e = {};
|
|
|
@@ -14,26 +159,107 @@ Error windowInit(const WindowSettings* ws) {
|
|
|
SET_ERROR("Init window failed");
|
|
|
return e;
|
|
|
}
|
|
|
+ glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
|
|
|
+ glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
|
|
|
window =
|
|
|
glfwCreateWindow(ws->width, ws->height, ws->title, nullptr, nullptr);
|
|
|
if(window == nullptr) {
|
|
|
glfwTerminate();
|
|
|
- SET_ERROR("Create window failed\n");
|
|
|
+ SET_ERROR("Create window failed");
|
|
|
return e;
|
|
|
}
|
|
|
+ glfwSetKeyCallback(window, onKeyEvent);
|
|
|
glfwMakeContextCurrent(window);
|
|
|
+ glfwSwapInterval(1);
|
|
|
+
|
|
|
+ GLenum err = glewInit();
|
|
|
+ if(GLEW_OK != err) {
|
|
|
+ const char* error = (const char*)glewGetErrorString(err);
|
|
|
+ if(err != GLEW_ERROR_NO_GLX_DISPLAY ||
|
|
|
+ strcmp(error, "Unknown error") != 0) {
|
|
|
+ SET_ERROR("Could not initialize GLEW: %s", error);
|
|
|
+ return e;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ glfwSetFramebufferSizeCallback(window, onWindowResize);
|
|
|
+
|
|
|
+ e = compileProgram();
|
|
|
+ if(hasError(&e)) {
|
|
|
+ return e;
|
|
|
+ }
|
|
|
+ glGenBuffers(1, &vbo);
|
|
|
+ glGenVertexArrays(1, &vba);
|
|
|
+
|
|
|
+ glBindBuffer(GL_ARRAY_BUFFER, vbo);
|
|
|
+ glBindVertexArray(vba);
|
|
|
+
|
|
|
+ glVertexAttribPointer(0, 2, GL_FLOAT, false, sizeof(float) * 2, (GLvoid*)0);
|
|
|
+ glEnableVertexAttribArray(0);
|
|
|
+
|
|
|
+ float data[][2] = {{-1.0f, -1.0f}, {1.0f, -1.0f}, {-1.0f, 1.0f},
|
|
|
+ {-1.0f, 1.0f}, {1.0f, 1.0f}, {1.0f, -1.0f}};
|
|
|
+ glBufferData(GL_ARRAY_BUFFER, sizeof(data), data, GL_STATIC_DRAW);
|
|
|
+
|
|
|
+ glGenTextures(1, &texture);
|
|
|
+ glBindTexture(GL_TEXTURE_2D, texture);
|
|
|
+ glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, WIDTH, HEIGHT);
|
|
|
+ glTextureParameteri(texture, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
|
|
+ glTextureParameteri(texture, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
|
|
return e;
|
|
|
}
|
|
|
|
|
|
void windowDestroy() {
|
|
|
+ glDeleteBuffers(1, &vbo);
|
|
|
+ glDeleteVertexArrays(1, &vba);
|
|
|
+ glDeleteShader(vShader);
|
|
|
+ glDeleteShader(fShader);
|
|
|
+ glDeleteProgram(program);
|
|
|
glfwDestroyWindow(window);
|
|
|
glfwTerminate();
|
|
|
}
|
|
|
|
|
|
void windowNextFrame() {
|
|
|
glClear(GL_COLOR_BUFFER_BIT);
|
|
|
+ glViewport(0, 0, width, height);
|
|
|
+ glActiveTexture(GL_TEXTURE0);
|
|
|
+ glBindTexture(GL_TEXTURE_2D, texture);
|
|
|
+ pixels[5][5][0] = 255;
|
|
|
+ pixels[5][5][1] = 255;
|
|
|
+ pixels[5][5][2] = 255;
|
|
|
+ for(int x = 0; x < WIDTH; x++) {
|
|
|
+ for(int y = 0; y < HEIGHT; y++) {
|
|
|
+ pixels[y][x][0] = 255;
|
|
|
+ pixels[y][x][1] = 255;
|
|
|
+ pixels[y][x][2] = 255;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ for(int y = 0; y < HEIGHT; y++) {
|
|
|
+ pixels[y][WIDTH / 2][0] = 128;
|
|
|
+ pixels[y][WIDTH / 2][1] = 0;
|
|
|
+ pixels[y][WIDTH / 2][2] = 255;
|
|
|
+ }
|
|
|
+ pixels[0][0][0] = 0;
|
|
|
+ pixels[0][0][1] = 0;
|
|
|
+ pixels[0][0][2] = 255;
|
|
|
+
|
|
|
+ glTexSubImage2D(
|
|
|
+ GL_TEXTURE_2D, 0, 0, 0, WIDTH, HEIGHT, GL_RGB, GL_UNSIGNED_BYTE,
|
|
|
+ pixels);
|
|
|
+
|
|
|
+ GLint size = glGetUniformLocation(program, "size");
|
|
|
+ glUniform2f(size, (float)width, (float)height);
|
|
|
+
|
|
|
+ glBindBuffer(GL_ARRAY_BUFFER, vbo);
|
|
|
+ glBindVertexArray(vba);
|
|
|
+ glDrawArrays(GL_TRIANGLES, 0, 6);
|
|
|
glfwSwapBuffers(window);
|
|
|
glfwPollEvents();
|
|
|
+ tickButtons();
|
|
|
+
|
|
|
+ auto i = glGetError();
|
|
|
+ if(i != 0) {
|
|
|
+ printf("GL-Error: %u\n", i);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
bool windowShouldClose() {
|