404 lines
12 KiB
C++
404 lines
12 KiB
C++
#define STB_IMAGE_IMPLEMENTATION
|
|
#include "renderer.h"
|
|
|
|
|
|
bool init_renderer(Renderer *renderer, u32 width, u32 height) {
|
|
renderer->m_width = width;
|
|
renderer->m_height = height;
|
|
|
|
|
|
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) {
|
|
log(1, "%s error: failed to initialize GLAD\n", __FUNCTION__);
|
|
return false;
|
|
}
|
|
|
|
if (!GLAD_GL_VERSION_4_6) {
|
|
log(1, "%s error: failed to get at least OpenGL 4.6\n", __FUNCTION__);
|
|
return false;
|
|
}
|
|
|
|
{
|
|
bool success = init_frame_buffer(&renderer->frame_buffer, width, height);
|
|
if (!success) {
|
|
log(1, "%s error: could not init Framebuffer\n", __FUNCTION__);
|
|
return false;
|
|
}
|
|
log(1, "%s: framebuffer succesfully initialized\n", __FUNCTION__);
|
|
}
|
|
|
|
{
|
|
bool success = load_texture(&renderer->texture, "assets/textures/crate.png");
|
|
if (!success) {
|
|
log(1, "%s: texture loading failed\n", __FUNCTION__);
|
|
return false;
|
|
}
|
|
log(1, "%s: texture successfully loaded\n", __FUNCTION__);
|
|
}
|
|
|
|
{
|
|
bool success = init_vertex_buffer(&renderer->vertex_buffer);
|
|
if (!success) {
|
|
log(1, "%s: vertex buffer successfully created\n", __FUNCTION__);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
{
|
|
bool success = init_uniform_buffer(&renderer->uniform_buffer);
|
|
if (!success) {
|
|
log(1, "%s: uniform buffer successfully created\n", __FUNCTION__);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
{
|
|
bool success = load_shaders(&renderer->shader, "shaders/basic.vert", "shaders/basic.frag");
|
|
if (!success) {
|
|
log(1, "%s: basic shader loading failed\n", __FUNCTION__);
|
|
return false;
|
|
}
|
|
log(1, "%s: basic shaders succesfully loaded\n", __FUNCTION__);
|
|
}
|
|
|
|
{
|
|
bool success = load_shaders(&renderer->alt_shader, "shaders/alt.vert", "shaders/alt.frag");
|
|
if (!success) {
|
|
log(1, "%s: alt shader loading failed\n", __FUNCTION__);
|
|
return false;
|
|
}
|
|
log(1, "%s: alt shaders succesfully loaded\n", __FUNCTION__);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void set_renderer_size(Renderer *renderer, u32 width, u32 height) {
|
|
resize_frame_buffer(&renderer->frame_buffer, width, height);
|
|
glViewport(0, 0, width, height);
|
|
}
|
|
|
|
void draw_renderer(Renderer *renderer) {
|
|
bind_frame_buffer(&renderer->frame_buffer);
|
|
|
|
glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
|
|
glClearDepth(1.0f);
|
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
|
|
|
glEnable(GL_CULL_FACE);
|
|
glEnable(GL_DEPTH_TEST);
|
|
|
|
Vector3 camera_position = Vector3(0.4f, 0.3f, 1.0f);
|
|
Vector3 camera_look_at_position = Vector3(0.0f, 0.0f, 0.0f);
|
|
Vector3 camera_up_vector = Vector3(0.0f, 1.0f, 0.0f);
|
|
|
|
renderer->projection_matrix = glm::perspective(glm::radians(90.0f), static_cast<f32>(renderer->m_width) / static_cast<f32>(renderer->m_height), 0.1f, 100.0f);
|
|
|
|
f32 dt = glfwGetTime();
|
|
Matrix4 model = Matrix4(1.0);
|
|
|
|
if (!renderer->use_alt_shader) {
|
|
use_shader(&renderer->shader);
|
|
model = glm::rotate(Matrix4(1.0f), dt, Vector3(0.0f, 0.0f, 1.0f));
|
|
} else {
|
|
use_shader(&renderer->alt_shader);
|
|
model = glm::rotate(Matrix4(1.0f), -dt, Vector3(0.0f, 0.0f, 1.0f));
|
|
}
|
|
|
|
renderer->view_matrix = glm::lookAt(camera_position, camera_look_at_position, camera_up_vector) * model;
|
|
|
|
upload_uniform_buffer_data(&renderer->uniform_buffer, renderer->view_matrix, renderer->projection_matrix);
|
|
|
|
bind_texture(&renderer->texture);
|
|
bind_vertex_buffer(&renderer->vertex_buffer);
|
|
|
|
draw_vertex_buffer(GL_TRIANGLES, 0, renderer->triangle_count * 3);
|
|
|
|
unbind_vertex_buffer();
|
|
unbind_texture();
|
|
unbind_frame_buffer(&renderer->frame_buffer);
|
|
|
|
draw_frame_buffer(&renderer->frame_buffer);
|
|
}
|
|
|
|
void upload_renderer_data(Renderer *renderer, Mesh *vertex_data) {
|
|
renderer->triangle_count = vertex_data->vertices.size() / 3;
|
|
upload_vertex_buffer_data(&renderer->vertex_buffer, vertex_data);
|
|
}
|
|
|
|
void cleanup_renderer(Renderer *renderer) {
|
|
cleanup_shader(&renderer->shader);
|
|
cleanup_shader(&renderer->alt_shader);
|
|
cleanup_texture(&renderer->texture);
|
|
cleanup_vertex_buffer(&renderer->vertex_buffer);
|
|
cleanup_frame_buffer(&renderer->frame_buffer);
|
|
}
|
|
|
|
|
|
/*
|
|
* Frame buffer
|
|
*/
|
|
bool init_frame_buffer(Frame_Buffer *frame_buffer, u32 width, u32 height) {
|
|
frame_buffer->width = width;
|
|
frame_buffer->height = height;
|
|
|
|
glGenFramebuffers(1, &frame_buffer->buffer);
|
|
glBindFramebuffer(GL_FRAMEBUFFER, frame_buffer->buffer);
|
|
glEnable(GL_FRAMEBUFFER_SRGB);
|
|
|
|
glGenTextures(1, &frame_buffer->color_tex);
|
|
glBindTexture(GL_TEXTURE_2D, frame_buffer->color_tex);
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
glBindTexture(GL_TEXTURE_2D, 0);
|
|
|
|
glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, frame_buffer->color_tex, 0);
|
|
log(1, "%s: added color buffer\n", __FUNCTION__);
|
|
|
|
glGenRenderbuffers(1, &frame_buffer->depth_buffer);
|
|
glBindRenderbuffer(GL_RENDERBUFFER, frame_buffer->depth_buffer);
|
|
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, width, height);
|
|
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, frame_buffer->depth_buffer);
|
|
glBindRenderbuffer(GL_RENDERBUFFER, 0);
|
|
log(1, "%s: added depth render buffer\n", __FUNCTION__);
|
|
|
|
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
|
|
|
return check_frame_buffer_complete(frame_buffer);
|
|
}
|
|
|
|
bool check_frame_buffer_complete(Frame_Buffer *frame_buffer) {
|
|
glBindFramebuffer(GL_FRAMEBUFFER, frame_buffer->buffer);
|
|
|
|
GLenum result = glCheckFramebufferStatus(GL_FRAMEBUFFER);
|
|
if (result != GL_FRAMEBUFFER_COMPLETE) {
|
|
log(1, "%s error: framebuffer is NOT complete\n", __FUNCTION__);
|
|
return false;
|
|
}
|
|
|
|
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
|
log(1, "%s: framebuffer is complete\n", __FUNCTION__);
|
|
return true;
|
|
}
|
|
|
|
bool resize_frame_buffer(Frame_Buffer *frame_buffer, u32 width, u32 height) {
|
|
frame_buffer->width = width;
|
|
frame_buffer->height = height;
|
|
|
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
|
glDeleteTextures(1, &frame_buffer->color_tex);
|
|
glDeleteRenderbuffers(1, &frame_buffer->depth_buffer);
|
|
glDeleteFramebuffers(1, &frame_buffer->buffer);
|
|
|
|
return init_frame_buffer(frame_buffer, width, height);
|
|
}
|
|
|
|
void draw_frame_buffer(Frame_Buffer *frame_buffer) {
|
|
glBindFramebuffer(GL_READ_FRAMEBUFFER, frame_buffer->buffer);
|
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
|
glBlitFramebuffer(0, 0, frame_buffer->width, frame_buffer->height, 0, 0, frame_buffer->width, frame_buffer->height, GL_COLOR_BUFFER_BIT, GL_NEAREST);
|
|
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
|
|
}
|
|
|
|
void bind_frame_buffer(Frame_Buffer *frame_buffer) {
|
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, frame_buffer->buffer);
|
|
}
|
|
|
|
void unbind_frame_buffer(Frame_Buffer *frame_buffer) {
|
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
|
}
|
|
|
|
void cleanup_frame_buffer(Frame_Buffer *frame_buffer) {
|
|
unbind_frame_buffer(frame_buffer);
|
|
|
|
glDeleteTextures(1, &frame_buffer->color_tex);
|
|
glDeleteRenderbuffers(1, &frame_buffer->depth_buffer);
|
|
glDeleteFramebuffers(1, &frame_buffer->buffer);
|
|
}
|
|
|
|
|
|
/*
|
|
* Vertex buffer
|
|
*/
|
|
bool init_vertex_buffer(Vertex_Buffer *vertex_buffer) {
|
|
glGenVertexArrays(1, &vertex_buffer->vao);
|
|
glGenBuffers(1, &vertex_buffer->vbo);
|
|
|
|
glBindVertexArray(vertex_buffer->vao);
|
|
glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer->vbo);
|
|
|
|
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*) offsetof(Vertex, position));
|
|
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*) offsetof(Vertex, color));
|
|
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*) offsetof(Vertex, uv));
|
|
|
|
glEnableVertexAttribArray(0);
|
|
glEnableVertexAttribArray(1);
|
|
glEnableVertexAttribArray(2);
|
|
|
|
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
|
glBindVertexArray(0);
|
|
|
|
return true;
|
|
}
|
|
|
|
void cleanup_vertex_buffer(Vertex_Buffer *vertex_buffer) {
|
|
glDeleteBuffers(1, &vertex_buffer->vbo);
|
|
glDeleteVertexArrays(1, &vertex_buffer->vao);
|
|
}
|
|
|
|
void upload_vertex_buffer_data(Vertex_Buffer *vertex_buffer, Mesh *vertex_data) {
|
|
glBindVertexArray(vertex_buffer->vao);
|
|
glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer->vbo);
|
|
glBufferData(GL_ARRAY_BUFFER, vertex_data->vertices.size() * sizeof(Vertex), &vertex_data->vertices.at(0), GL_DYNAMIC_DRAW);
|
|
|
|
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
|
glBindVertexArray(0);
|
|
}
|
|
|
|
void bind_vertex_buffer(Vertex_Buffer *vertex_buffer) {
|
|
glBindVertexArray(vertex_buffer->vao);
|
|
}
|
|
|
|
void unbind_vertex_buffer() {
|
|
glBindVertexArray(0);
|
|
}
|
|
|
|
void draw_vertex_buffer(u32 mode, u32 start, u32 num) {
|
|
glDrawArrays(mode, start, num);
|
|
}
|
|
|
|
/*
|
|
* Uniform buffer
|
|
*/
|
|
bool init_uniform_buffer(Uniform_Buffer *uniform_buffer) {
|
|
glGenBuffers(1, &uniform_buffer->ubo_buffer);
|
|
glBindBuffer(GL_UNIFORM_BUFFER, uniform_buffer->ubo_buffer);
|
|
glBufferData(GL_UNIFORM_BUFFER, 2 * sizeof(Matrix4), nullptr, GL_STATIC_DRAW);
|
|
glBindBuffer(GL_UNIFORM_BUFFER, 0);
|
|
|
|
return true;
|
|
}
|
|
|
|
void upload_uniform_buffer_data(Uniform_Buffer *uniform_buffer, Matrix4 view_matrix, Matrix4 projection_matrix) {
|
|
glBindBuffer(GL_UNIFORM_BUFFER, uniform_buffer->ubo_buffer);
|
|
glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(Matrix4), glm::value_ptr(view_matrix));
|
|
glBufferSubData(GL_UNIFORM_BUFFER, sizeof(Matrix4), sizeof(Matrix4), glm::value_ptr(projection_matrix));
|
|
glBindBufferRange(GL_UNIFORM_BUFFER, 0, uniform_buffer->ubo_buffer, 0, 2 * sizeof(Matrix4));
|
|
glBindBuffer(GL_UNIFORM_BUFFER, 0);
|
|
}
|
|
|
|
void cleanup(Uniform_Buffer *uniform_buffer) {
|
|
glDeleteBuffers(1, &uniform_buffer->ubo_buffer);
|
|
}
|
|
|
|
|
|
/*
|
|
* Texture
|
|
*/
|
|
bool load_texture(Texture *texture, string filepath) {
|
|
texture->name = filepath;
|
|
|
|
stbi_set_flip_vertically_on_load(true);
|
|
u8* texture_data = stbi_load(filepath.c_str(), &texture->width, &texture->height, &texture->number_of_channels, 0);
|
|
|
|
if (!texture_data) {
|
|
stbi_image_free(texture_data);
|
|
return false;
|
|
}
|
|
|
|
glGenTextures(1, &texture->texture);
|
|
glBindTexture(GL_TEXTURE_2D, texture->texture);
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
|
|
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texture->width, texture->height, 0, GL_RGBA, GL_UNSIGNED_BYTE, texture_data);
|
|
|
|
glGenerateMipmap(GL_TEXTURE_2D);
|
|
glBindTexture(GL_TEXTURE_2D, 0);
|
|
|
|
stbi_image_free(texture_data);
|
|
|
|
return true;
|
|
}
|
|
|
|
void bind_texture(Texture *texture) {
|
|
glBindTexture(GL_TEXTURE_2D, texture->texture);
|
|
}
|
|
|
|
void unbind_texture() {
|
|
glBindTexture(GL_TEXTURE_2D, 0);
|
|
}
|
|
|
|
void cleanup_texture(Texture *texture) {
|
|
glDeleteTextures(1, &texture->texture);
|
|
}
|
|
|
|
|
|
/*
|
|
* Shaders
|
|
*/
|
|
bool load_shaders(Shader *shader, string vertex_shader_filepath, string fragment_shader_filepath) {
|
|
u32 vertex_shader = load_shader(vertex_shader_filepath, GL_VERTEX_SHADER);
|
|
if (!vertex_shader) {
|
|
return false;
|
|
}
|
|
|
|
u32 fragment_shader = load_shader(fragment_shader_filepath, GL_FRAGMENT_SHADER);
|
|
if (!fragment_shader) {
|
|
return false;
|
|
}
|
|
|
|
shader->program = glCreateProgram();
|
|
glAttachShader(shader->program, vertex_shader);
|
|
glAttachShader(shader->program, fragment_shader);
|
|
glLinkProgram(shader->program);
|
|
|
|
s32 is_program_linked;
|
|
glGetProgramiv(shader->program, GL_LINK_STATUS, &is_program_linked);
|
|
if (!is_program_linked) {
|
|
glDeleteShader(vertex_shader);
|
|
glDeleteShader(fragment_shader);
|
|
return false;
|
|
}
|
|
|
|
s32 ubo_index = glGetUniformBlockIndex(shader->program, "Matrices");
|
|
glUniformBlockBinding(shader->program, ubo_index, 0);
|
|
|
|
glDeleteShader(vertex_shader);
|
|
glDeleteShader(fragment_shader);
|
|
|
|
return true;
|
|
}
|
|
|
|
void cleanup_shader(Shader *shader) {
|
|
glDeleteProgram(shader->program);
|
|
}
|
|
|
|
u32 load_shader(string filepath, u32 shader_type) {
|
|
string shader_as_text;
|
|
shader_as_text = load_file_to_string(filepath);
|
|
|
|
const char*shader_source = shader_as_text.c_str();
|
|
u32 shader = glCreateShader(shader_type);
|
|
glShaderSource(shader, 1, (const GLchar**) &shader_source, 0);
|
|
glCompileShader(shader);
|
|
|
|
s32 is_shader_compiled;
|
|
glGetShaderiv(shader, GL_COMPILE_STATUS, &is_shader_compiled);
|
|
if (!is_shader_compiled) {
|
|
return 0;
|
|
}
|
|
|
|
return shader;
|
|
}
|
|
|
|
void use_shader(Shader *shader) {
|
|
glUseProgram(shader->program);
|
|
}
|