439 lines
12 KiB
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;
|
|
}
|
|
|