1
0
Fork 0
2022-untitled-game/code/game/engine/render_mgr.c

439 lines
12 KiB
C

static GLuint
compile_shader(char* contents, size_t size, unsigned int type);
static GLuint
create_shader(char* buf, size_t size);
void
render_mgr_startup(void)
{
// Geometry rendering
{
#ifdef BUILD_DEBUG
size_t size;
char* buf = DEBUG_load_shader("shaders/geo.glsl", &size);
#else
size_t size = strlen(geo_glsl);
char* buf = (char*)geo_glsl;
#endif
GLuint shader = create_shader(buf, size);
GLuint u_model = glGetUniformLocation(shader, "u_model");
GLuint u_view = glGetUniformLocation(shader, "u_view");
GLuint u_proj = glGetUniformLocation(shader, "u_proj");
GLuint u_texture = glGetUniformLocation(shader, "u_texture");
GLuint u_light = glGetUniformLocation(shader, "u_light");
const float n = 0.1f;
const float f = 100.0f;
const float fov = 75.0f;
const float d = 1.0f / tanf(DEG_TO_RAD(fov*0.5f));
const float a = (float)RENDER_WIDTH / (float)RENDER_HEIGHT;
mat4 proj_mat = {
d/a, 0.0f, 0.0f, 0.0f,
0.0f, d, 0.0f, 0.0f,
0.0f, 0.0f, f/(f-n), (-f*n)/(f-n),
0.0f, 0.0f, 1.0f, 0.0f
};
g_engine->render_mgr.geo.shader = shader;
g_engine->render_mgr.geo.u_model = u_model;
g_engine->render_mgr.geo.u_proj = u_proj;
g_engine->render_mgr.geo.u_view= u_view;
g_engine->render_mgr.geo.u_texture = u_texture;
g_engine->render_mgr.geo.u_light = u_light;
g_engine->render_mgr.geo.proj_mat = proj_mat;
}
// Text rendering
{
#ifdef BUILD_DEBUG
size_t size;
char* buf = DEBUG_load_shader("shaders/text.glsl", &size);
#else
size_t size = strlen(text_glsl);
char* buf = (char*)text_glsl;
#endif
GLuint shader = create_shader(buf, size);
GLuint vao;
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
GLuint vbo;
glGenBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)0);
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)(2 * sizeof(float)));
GLuint texture;
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, g_font.width, g_font.height, 0, GL_RED, GL_UNSIGNED_BYTE, g_font.bitmap);
GLuint u_proj = glGetUniformLocation(shader, "u_proj");
GLuint u_model = glGetUniformLocation(shader, "u_model");
GLuint u_texture = glGetUniformLocation(shader, "u_texture");
GLuint u_color = glGetUniformLocation(shader, "u_color");
mat4 proj_mat =
{
2.0f/WINDOW_WIDTH, 0.0f, 0.0f, -1.0f,
0.0f, 2.0f/WINDOW_HEIGHT, 0.0f, -1.0f,
0.0f, 0.0f, -1.0f, -1.0f,
0.0f, 0.0f, 0.0f, 1.0f
};
g_engine->render_mgr.text.shader = shader;
g_engine->render_mgr.text.vao = vao;
g_engine->render_mgr.text.vbo = vbo;
g_engine->render_mgr.text.texture = texture;
g_engine->render_mgr.text.u_proj = u_proj;
g_engine->render_mgr.text.u_model = u_model;
g_engine->render_mgr.text.u_texture = u_texture;
g_engine->render_mgr.text.u_color = u_color;
g_engine->render_mgr.text.proj_mat = proj_mat;
}
// Screen rendering
{
#ifdef BUILD_DEBUG
size_t size;
char* buf = DEBUG_load_shader("shaders/screen.glsl", &size);
#else
size_t size = strlen(screen_glsl);
char* buf = (char*)screen_glsl;
#endif
GLuint shader = create_shader(buf, size);
// Low-resolution texture that will be rendered into
GLuint texture;
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, RENDER_WIDTH, RENDER_HEIGHT, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glBindTexture(GL_TEXTURE_2D, 0);
// Render buffer object required for the framebuffer
GLuint rbo;
glGenRenderbuffers(1, &rbo);
glBindRenderbuffer(GL_RENDERBUFFER, rbo);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, RENDER_WIDTH, RENDER_HEIGHT);
glBindRenderbuffer(GL_RENDERBUFFER, 0);
// Framebuffer attached to texture and RBO
GLuint fbo;
glGenFramebuffers(1, &fbo);
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rbo);
if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
LOG_ERROR("Unable to create render-to-texture framebuffer.");
exit(EXIT_FAILURE);
}
glBindFramebuffer(GL_FRAMEBUFFER, 0);
float vertices[] =
{
// Position Texcoord
-1.0f, 1.0f, 0.0f, 1.0f, // upper left
-1.0f, -1.0f, 0.0f, 0.0f, // lower left
1.0f, -1.0f, 1.0f, 0.0f, // lower right
1.0f, -1.0f, 1.0f, 0.0f, // lower right
1.0f, 1.0f, 1.0f, 1.0f, // upper right
-1.0f, 1.0f, 0.0f, 1.0f, // upper left
};
GLuint vao;
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
GLuint vbo;
glGenBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(vertices[0]), (void*)0);
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(vertices[0]), (void*)(2 * sizeof(vertices[0])));
glBindVertexArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
GLuint u_texture = glGetUniformLocation(shader, "u_texture");
g_engine->render_mgr.screen.shader = shader;
g_engine->render_mgr.screen.vao = vao;
g_engine->render_mgr.screen.vbo = vbo;
g_engine->render_mgr.screen.fbo = fbo;
g_engine->render_mgr.screen.rbo = rbo;
g_engine->render_mgr.screen.texture = texture;
g_engine->render_mgr.screen.u_texture = u_texture;
}
}
const char*
format_text(const char* format, ...)
{
static char buf[MAX_TEXT_LENGTH] = {};
va_list args;
va_start(args, format);
vsprintf(buf, format, args);
va_end(args);
return buf;
}
void
render_geo_begin(void)
{
glBindFramebuffer(GL_FRAMEBUFFER, g_engine->render_mgr.screen.fbo);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnable(GL_DEPTH_TEST);
glEnable(GL_BLEND);
glUseProgram(g_engine->render_mgr.geo.shader);
glViewport(0, 0, RENDER_WIDTH, RENDER_HEIGHT);
glUniformMatrix4fv(g_engine->render_mgr.geo.u_proj, 1, GL_TRUE, (float*)&g_engine->render_mgr.geo.proj_mat);
#ifdef BUILD_DEBUG
if (g_debug->camera.enabled) {
glUniformMatrix4fv(g_engine->render_mgr.geo.u_view, 1, GL_TRUE, (float*)&g_debug->camera.fly.view_mat);
}
else {
glUniformMatrix4fv(g_engine->render_mgr.geo.u_view, 1, GL_TRUE, (float*)&g_engine->render_mgr.active_camera->view_mat);
}
#else
glUniformMatrix4fv(g_engine->render_mgr.geo.u_view, 1, GL_TRUE, (float*)&g_engine->render_mgr.active_camera->view_mat);
#endif
}
void
render_geo_end(void)
{
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glViewport(VIEWPORT_X, VIEWPORT_Y, VIEWPORT_WIDTH, VIEWPORT_HEIGHT);
}
void
render_screen_begin(void)
{
glClear(GL_COLOR_BUFFER_BIT);
glEnable(GL_BLEND);
glUseProgram(g_engine->render_mgr.screen.shader);
glBindVertexArray(g_engine->render_mgr.screen.vao);
glUniform1i(g_engine->render_mgr.screen.u_texture, 0);
}
void
render_screen_end(void)
{
glDisable(GL_DEPTH_TEST);
glBindTexture(GL_TEXTURE_2D, g_engine->render_mgr.screen.texture);
glDrawArrays(GL_TRIANGLES, 0, 6);
glViewport(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT);
}
void
render_text(const char* text, vec2 position, vec3 color, float size)
{
struct vertex {
vec2 position;
vec2 texcoord;
};
struct vertex vertices[6 * MAX_TEXT_LENGTH];
int text_length = strlen(text);
LOG_ASSERT(text_length < MAX_TEXT_LENGTH,
"Text length (%d) exceeds allowed maximum (%d)", text_length, MAX_TEXT_LENGTH);
float atlas_width_recip = 1.0f / g_font.width;
float atlas_height_recip = 1.0f / g_font.height;
int cursor = 0;
for (int i = 0; i < text_length; i++)
{
// Indices start with space character (ASCII 0x20 / 32)
int idx = text[i] - ' ';
glyph_t glyph = g_font.glyphs[idx];
// Set up the four vertices required for the glyph's quad (texcoord.y is flipped to account for flipped texture)
struct vertex upper_left = {
.position.x = cursor - glyph.origin_x,
.position.y = glyph.origin_y,
.texcoord.x = glyph.x * atlas_width_recip,
.texcoord.y = 1.0f - (glyph.y * atlas_height_recip)};
struct vertex upper_right = {
.position.x = upper_left.position.x + glyph.width,
.position.y = upper_left.position.y,
.texcoord.x = (glyph.x + glyph.width) * atlas_width_recip,
.texcoord.y = upper_left.texcoord.y};
struct vertex bottom_right = {
.position.x = upper_right.position.x,
.position.y = upper_left.position.y - glyph.height,
.texcoord.x = upper_right.texcoord.x,
.texcoord.y = 1.0f - ((glyph.y + glyph.height) * atlas_height_recip)};
struct vertex bottom_left = {
.position.x = upper_left.position.x,
.position.y = bottom_right.position.y,
.texcoord.x = upper_left.texcoord.x,
.texcoord.y = bottom_right.texcoord.y};
// o-----o
// | \ |
// | \ |
// | \ |
// o-----o
vertices[6*i + 0] = upper_left;
vertices[6*i + 1] = upper_right;
vertices[6*i + 2] = bottom_right;
vertices[6*i + 3] = bottom_right;
vertices[6*i + 4] = bottom_left;
vertices[6*i + 5] = upper_left;
cursor += g_font.advance;
}
mat4 model_mat = mat4_identity();
model_mat.e00 = size;
model_mat.e11 = size;
model_mat.e03 = position.x;
model_mat.e13 = position.y;
glUseProgram(g_engine->render_mgr.text.shader);
glUniformMatrix4fv(g_engine->render_mgr.text.u_proj, 1, GL_TRUE, (float*)&g_engine->render_mgr.text.proj_mat);
glUniformMatrix4fv(g_engine->render_mgr.text.u_model, 1, GL_TRUE, (float*)&model_mat);
glUniform1i(g_engine->render_mgr.text.u_texture, 0);
glUniform3fv(g_engine->render_mgr.text.u_color, 1, (float*)&color);
glBindVertexArray(g_engine->render_mgr.text.vao);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, g_engine->render_mgr.text.texture);
glBindBuffer(GL_ARRAY_BUFFER, g_engine->render_mgr.text.vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices[0]) * 6 * text_length, vertices, GL_DYNAMIC_DRAW);
glDrawArrays(GL_TRIANGLES, 0, 6 * text_length);
}
void
set_active_camera(camera_t* camera)
{
g_engine->render_mgr.active_camera = camera;
}
camera_t*
get_active_camera(void)
{
return g_engine->render_mgr.active_camera;
}
static GLuint
compile_shader(char* contents, size_t size, unsigned int type)
{
const char* sources[3];
sources[0] = "#version 400 core\n";
sources[1] = (type == GL_VERTEX_SHADER) ? "#define VERTEX\n" : "#define FRAGMENT\n";
sources[2] = contents;
int lengths[3];
lengths[0] = strlen(sources[0]);
lengths[1] = strlen(sources[1]);
lengths[2] = size;
GLuint id = glCreateShader(type);
glShaderSource(id, 3, sources, lengths);
glCompileShader(id);
char log[512];
int success;
glGetShaderiv(id, GL_COMPILE_STATUS, &success);
if (!success)
{
glGetShaderInfoLog(id, sizeof(log), NULL, log);
LOG_ERROR("Failed to compile shader:");
LOG_ERROR(" %s", log);
#ifndef BUILD_DEBUG
exit(EXIT_FAILURE);
#endif
}
return id;
}
static GLuint
create_shader(char* buf, size_t size)
{
size_t vertex_size;
{
char* substring = strstr(buf, "#ifdef FRAGMENT");
LOG_ASSERT(substring != NULL, "Error parsing vertex shader");
vertex_size = substring - buf;
}
size_t fragment_size;
{
fragment_size = size - vertex_size;
}
GLuint vertex = compile_shader(buf, vertex_size, GL_VERTEX_SHADER);
GLuint fragment = compile_shader(buf+vertex_size, fragment_size, GL_FRAGMENT_SHADER);
GLuint shader = glCreateProgram();
{
char log[512];
int success;
glAttachShader(shader, vertex);
glAttachShader(shader, fragment);
glLinkProgram(shader);
glGetProgramiv(shader, GL_LINK_STATUS, &success);
if (!success)
{
glGetProgramInfoLog(shader, sizeof(log), NULL, log);
LOG_ERROR("Failed to link shader program:");
LOG_ERROR(" %s", log);
#ifndef BUILD_DEBUG
exit(EXIT_FAILURE);
#endif
}
glDeleteShader(vertex);
glDeleteShader(fragment);
}
return shader;
}