glerminal/source/glerminal.cpp
2024-05-09 15:42:12 -04:00

231 lines
5.1 KiB
C++

#include "glerminal-private.h"
#include <iostream>
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<GLADloadproc>(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<void*>(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();
}