#include "glerminal-private.h" #include namespace { glerminal::glerminal* GLERMINAL_G = nullptr; constexpr float VBO_VERTICES[] = { // first triangle 0.5f, 0.5f, // top right 0.5f, -0.5f, // bottom right -0.5f, 0.5f, // top left // second triangle 0.5f, -0.5f, // bottom right -0.5f, -0.5f, // bottom left -0.5f, 0.5f // top left }; constexpr char* VERTEX_SHADER_SOURCE = "#version 330 core\n" "layout (location = 0) in vec2 pos;\n" "void main()\n" "{\n" " gl_Position = vec4(pos.x, pos.y, 0, 1);\n" "}"; constexpr char* FRAGMENT_SHADER_SOURCE = "#version 330 core\n" "out vec4 FragColor;\n" "\n" "void main()\n" "{\n" " FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);\n" "}"; } namespace glerminal { glerminal::glerminal(glerminal_main_cb main) : m_main(main) { if (GLERMINAL_G) { throw std::runtime_error("glerminal is already running."); } // unsure if this should be an error if (!m_main) { throw std::runtime_error("No main callback provided."); } init_glfw(); init_gl(); GLERMINAL_G = this; } glerminal::~glerminal() { deinit_gl(); deinit_glfw(); GLERMINAL_G = nullptr; } void glerminal::run() { while (!glfwWindowShouldClose(m_window)) { glfwPollEvents(); m_main(); } } void glerminal::flush() { glClear(GL_COLOR_BUFFER_BIT); glUseProgram(m_program); glBindVertexArray(m_vao); glDrawArrays(GL_TRIANGLES, 0, 6); glfwSwapBuffers(m_window); } void glerminal::init_glfw() { glfwInit(); glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // not resizable for now. // // need to think about how to handle resizing to ensure // that the window stays an integer number of "tiles" large glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); // non-adjustable size for the same reason as above m_window = glfwCreateWindow(1280, 800, "glerminal", nullptr, nullptr); if (!m_window) { throw std::runtime_error("Failed to create glerminal window."); } } void glerminal::init_gl() { glfwMakeContextCurrent(m_window); glfwSwapInterval(1); if (!gladLoadGLLoader(reinterpret_cast(glfwGetProcAddress))) { throw std::runtime_error("Failed to initialize GLAD."); } glViewport(0, 0, 1280, 800); glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); // -- setup vertex data -- // create vertex buffer object glGenBuffers(1, &m_vbo); // create vertex array object glGenVertexArrays(1, &m_vao); glBindVertexArray(m_vao); // bind buffer and copy data glBindBuffer(GL_ARRAY_BUFFER, m_vbo); glBufferData(GL_ARRAY_BUFFER, sizeof(VBO_VERTICES), VBO_VERTICES, GL_STATIC_DRAW); // set up vertex attributes glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), reinterpret_cast(0)); glEnableVertexAttribArray(0); // -- setup shader program -- // compile const unsigned int vertex_shader = glCreateShader(GL_VERTEX_SHADER); glShaderSource(vertex_shader, 1, &VERTEX_SHADER_SOURCE, nullptr); glCompileShader(vertex_shader); const unsigned int fragment_shader = glCreateShader(GL_FRAGMENT_SHADER); glShaderSource(fragment_shader, 1, &FRAGMENT_SHADER_SOURCE, nullptr); glCompileShader(fragment_shader); int success; char info_log[512] = {}; // verify glGetShaderiv(vertex_shader, GL_COMPILE_STATUS, &success); if (!success) { glGetShaderInfoLog(vertex_shader, sizeof(info_log) / sizeof(*info_log), nullptr, info_log); glDeleteShader(vertex_shader); glDeleteShader(fragment_shader); using namespace std::string_literals; throw std::runtime_error("Could not compile vertex shader: "s + info_log); } glGetShaderiv(fragment_shader, GL_COMPILE_STATUS, &success); if (!success) { glGetShaderInfoLog(fragment_shader, sizeof(info_log) / sizeof(*info_log), nullptr, info_log); glDeleteShader(vertex_shader); glDeleteShader(fragment_shader); using namespace std::string_literals; throw std::runtime_error("Could not compile fragment shader: "s + info_log); } // link m_program = glCreateProgram(); glAttachShader(m_program, vertex_shader); glAttachShader(m_program, fragment_shader); glLinkProgram(m_program); glDeleteShader(vertex_shader); glDeleteShader(fragment_shader); // verify again glGetProgramiv(m_program, GL_LINK_STATUS, &success); if (!success) { glGetProgramInfoLog(m_program, sizeof(info_log) / sizeof(*info_log), nullptr, info_log); glDeleteProgram(m_program); using namespace std::string_literals; throw std::runtime_error("Could not link shader program: "s + info_log); } } void glerminal::deinit_glfw() { glDeleteProgram(m_program); glfwDestroyWindow(m_window); glfwTerminate(); } void glerminal::deinit_gl() { glDeleteProgram(m_program); } } void glerminal_run(glerminal_main_cb main) { try { glerminal::glerminal(main).run(); } catch (const std::runtime_error& e) { std::cout << "[glerminal] " << e.what() << std::endl; } } void glerminal_flush() { if (!GLERMINAL_G) { return; } GLERMINAL_G->flush(); }