You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

317 lines
9.6 KiB

#include <cassert>
#include <cstdlib> // calloc
// TODO: decide on extension library
//#include <GL/glew.h>
#include <GL/gl3w.h>
#include <glm/glm.hpp>
#include <glm/geometric.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include "aixlog.hpp"
#include "render_group.h"
#include "mesh.h"
gl_render_group*
rgCreateGroup()
{
gl_render_group* rg = (gl_render_group *) std::calloc(1, sizeof(gl_render_group));
return rg;
}
bool
rgInitShaderProgram(gl_render_group* rg, const char * vertex_code, const char * frag_code,
const char* model_name, const char* view_name, const char* projection_name)
{
glGenVertexArrays(1, &rg->vertex_array_id);
glBindVertexArray(rg->vertex_array_id);
GLuint vertex_shader_id = glCreateShader(GL_VERTEX_SHADER);
GLuint fragment_shader_id = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(vertex_shader_id, 1, &vertex_code, NULL);
glShaderSource(fragment_shader_id, 1, &frag_code, NULL);
glCompileShader(vertex_shader_id);
glCompileShader(fragment_shader_id);
rg->program_id = glCreateProgram();
glAttachShader(rg->program_id, vertex_shader_id);
glAttachShader(rg->program_id, fragment_shader_id);
glLinkProgram(rg->program_id);
rg->model_matrix_id = glGetUniformLocation(rg->program_id, model_name);
rg->view_matrix_id = glGetUniformLocation(rg->program_id, view_name);
rg->projection_matrix_id = glGetUniformLocation(rg->program_id, projection_name);
rg->normal_matrix_id = glGetUniformLocation(rg->program_id, "normal_matrix");
glDetachShader(rg->program_id, vertex_shader_id);
glDetachShader(rg->program_id, fragment_shader_id);
glDeleteShader(vertex_shader_id);
glDeleteShader(fragment_shader_id);
// check for errors
GLint isLinked = 0;
glGetProgramiv(rg->program_id, GL_LINK_STATUS, &isLinked);
if (isLinked == GL_FALSE) {
GLint maxLength = 0;
glGetProgramiv(rg->program_id, GL_INFO_LOG_LENGTH, &maxLength);
// The maxLength includes the NULL character
GLchar infoLog[maxLength];
glGetProgramInfoLog(rg->program_id, maxLength, &maxLength, &infoLog[0]);
LOG(ERROR) << infoLog << "\n";
// The program is useless now. So delete it.
glDeleteProgram(rg->program_id);
return false;
}
return true;
}
void
rgInitGLBufferObject(gl_buffer* buf_obj, uint len, GLenum usage, GLenum target, GLfloat data[])
{
// TODO: why not just assing the pointer to the render group instead of copying here?
buf_obj->buffer_len = len;
buf_obj->buffer = (GLfloat*) std::calloc(len, sizeof(GLfloat));
if (data)
{
for (uint i = 0; i < len; i++)
buf_obj->buffer[i] = data[i];
}
glGenBuffers(1, &buf_obj->buffer_id);
glBindBuffer(target, buf_obj->buffer_id);
glBufferData(target, len * sizeof(GLfloat), buf_obj->buffer, usage);
}
// NOTE: helper for fillColorBuffer() to convert uint32 color to GLfloat triplet
void
convertColor(GLfloat buf[3], uint32 color)
{
// NOTE: not using the alpha values for now
buf[0] = (GLfloat) ((color >> 24) & 0xFF) / (GLfloat) 255;
buf[1] = (GLfloat) ((color >> 16) & 0xFF) / (GLfloat) 255;
buf[2] = (GLfloat) ((color >> 8) & 0xFF) / (GLfloat) 255;
}
void
rgFillColorBuffer(GLfloat buf[], int len, std::vector<hex_info>* hexes)
{
int buf_idx;
int buf_len_per_hex = 54; // NOTE: 3 * 3 * 6
GLfloat color_buf[3];
for (int i = 0; i < (int) hexes->size(); i++)
{
buf_idx = i * buf_len_per_hex;
hex_info hxi = (*hexes)[i];
convertColor(color_buf, hxi.current_color);
for (int j = 0; j < buf_len_per_hex; j+=3)
{
buf[buf_idx + j] = color_buf[0];
buf[buf_idx + j + 1] = color_buf[1];
buf[buf_idx + j + 2] = color_buf[2];
}
}
}
bool
rgInitEntity(gl_render_group* rg, Entity* e)
{
bool retVal = true;
bool useNormals = rg->use_normals = (e->mesh->normals != nullptr);
uint vertex_buf_len = e->mesh->num_vertices * 3;
GLfloat* vertex_buf = (GLfloat*) std::calloc(vertex_buf_len, sizeof(GLfloat));
GLfloat* color_buf = (GLfloat*) std::calloc(vertex_buf_len, sizeof(GLfloat));
GLfloat* normal_buf;
if (useNormals)
normal_buf = (GLfloat*) std::calloc(vertex_buf_len, sizeof(GLfloat));
// dump vertices, colors, and normals into temporary buffers
uint vertex_index = 0;
uint vertex_prop_index = 0;
for (uint i = 0; i < vertex_buf_len; i++) {
const glm::vec3& vertex = e->mesh->vertices[vertex_index];
const glm::vec3& normal = e->mesh->normals[vertex_index];
switch (vertex_prop_index) {
case 0: vertex_buf[i] = vertex.x; break;
case 1: vertex_buf[i] = vertex.y; break;
case 2: vertex_buf[i] = vertex.z; break;
}
if (useNormals) {
switch (vertex_prop_index) {
case 0: normal_buf[i] = normal.x; break;
case 1: normal_buf[i] = normal.y; break;
case 2: normal_buf[i] = normal.z; break;
}
}
color_buf[i] = e->mesh->diffuse_color[vertex_prop_index];
vertex_prop_index++;
if (vertex_prop_index == 3) {
vertex_prop_index = 0;
vertex_index++;
}
}
// send vertex and color data to OpenGL
rgInitGLBufferObject(&rg->vertex_buffer, vertex_buf_len,
GL_DYNAMIC_DRAW, GL_ARRAY_BUFFER, vertex_buf);
rgInitGLBufferObject(&rg->color_buffer, vertex_buf_len,
GL_STATIC_DRAW, GL_ARRAY_BUFFER, color_buf);
if (useNormals) {
rgInitGLBufferObject(&rg->vertex_normals, vertex_buf_len,
GL_STATIC_DRAW, GL_ARRAY_BUFFER, normal_buf);
}
// TODO: combine this somehow with rgInitGLBufferObject()
// dump indices into temp buffer
gl_index_buffer index_buffer;
index_buffer.buffer_len = e->mesh->num_indices;
index_buffer.indices = (uint *) std::calloc(e->mesh->num_indices, sizeof(uint));
for (uint i = 0; i < e->mesh->num_indices; i++)
index_buffer.indices[i] = e->mesh->indices[i];
rg->index_buffer = index_buffer;
glGenBuffers(1, &rg->index_buffer.buffer_id);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, rg->index_buffer.buffer_id);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, e->mesh->num_indices * sizeof(uint),
rg->index_buffer.indices, GL_STATIC_DRAW);
// free temp buffers
std::free(vertex_buf);
if (useNormals) std::free(normal_buf);
std::free(color_buf);
vertex_buf = normal_buf = color_buf = nullptr;
return retVal;
}
void
rgDraw(gl_render_group* rg, GLenum draw_mode, glm::mat4 model_matrix,
glm::mat4 view_matrix, glm::mat4 projection_matrix,
glm::vec3 light_position, GLuint light_id, bool update_vertex_data)
{
glUseProgram(rg->program_id);
glUniformMatrix4fv(rg->model_matrix_id, 1, GL_FALSE, &model_matrix[0][0]);
glUniformMatrix4fv(rg->view_matrix_id, 1, GL_FALSE, &view_matrix[0][0]);
glUniformMatrix4fv(rg->projection_matrix_id, 1, GL_FALSE, &projection_matrix[0][0]);
// 1st attribute buffer : vertices
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, rg->vertex_buffer.buffer_id);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void*) 0);
if (update_vertex_data)
{
glBufferSubData(GL_ARRAY_BUFFER, 0, rg->vertex_buffer.buffer_len * sizeof(GLfloat),
rg->vertex_buffer.buffer);
}
// 2nd attribute buffer : colors
if (rg->color_buffer.buffer)
{
glEnableVertexAttribArray(1);
glBindBuffer(GL_ARRAY_BUFFER, rg->color_buffer.buffer_id);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, (void*) 0);
// TODO: maybe add an option to not send color data every frame?
glBufferSubData(GL_ARRAY_BUFFER, 0, rg->color_buffer.buffer_len * sizeof(GLfloat),
rg->color_buffer.buffer);
}
// 3rd attribute buffer: normals
if (rg->use_normals) {
glEnableVertexAttribArray(2);
glBindBuffer(GL_ARRAY_BUFFER, rg->vertex_normals.buffer_id);
glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 0, (void*) 0);
// TODO: testing lighting
glUniform3f(light_id, light_position.x, light_position.y, light_position.z);
glm::mat3 normal_matrix = glm::transpose(glm::inverse(glm::mat3(model_matrix)));
glUniformMatrix3fv(rg->normal_matrix_id, 1, GL_FALSE, &normal_matrix[0][0]);
}
// draw
glDrawArrays(draw_mode, 0, rg->vertex_buffer.buffer_len / 3);
// cleanup
glDisableVertexAttribArray(0);
if (rg->color_buffer.buffer)
glDisableVertexAttribArray(1);
}
void
rgDrawIndexed(gl_render_group* rg, GLenum draw_mode, glm::mat4 model_matrix,
glm::mat4 view_matrix, glm::mat4 projection_matrix,
glm::vec3 light_position, GLuint light_id)
{
glUseProgram(rg->program_id);
glUniformMatrix4fv(rg->model_matrix_id, 1, GL_FALSE, &model_matrix[0][0]);
glUniformMatrix4fv(rg->view_matrix_id, 1, GL_FALSE, &view_matrix[0][0]);
glUniformMatrix4fv(rg->projection_matrix_id, 1, GL_FALSE, &projection_matrix[0][0]);
// TODO: testing lighting
glUniform3f(light_id, light_position.x, light_position.y, light_position.z);
glm::mat3 normal_matrix = glm::transpose(glm::inverse(glm::mat3(model_matrix)));
glUniformMatrix3fv(rg->normal_matrix_id, 1, GL_FALSE, &normal_matrix[0][0]);
// 1st attribute buffer : vertices
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, rg->vertex_buffer.buffer_id);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void*) 0);
// 2nd attribute buffer : colors
if (rg->color_buffer.buffer)
{
glEnableVertexAttribArray(1);
glBindBuffer(GL_ARRAY_BUFFER, rg->color_buffer.buffer_id);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, (void*) 0);
}
// 3rd attribe buffer : normals
if (rg->use_normals) {
glEnableVertexAttribArray(2);
glBindBuffer(GL_ARRAY_BUFFER, rg->vertex_normals.buffer_id);
glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 0, (void*) 0);
}
// draw
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, rg->index_buffer.buffer_id);
glDrawElements(draw_mode, rg->index_buffer.buffer_len, GL_UNSIGNED_INT, 0);
// cleanup
glDisableVertexAttribArray(0);
if (rg->color_buffer.buffer)
glDisableVertexAttribArray(1);
if (rg->use_normals)
glDisableVertexAttribArray(2);
}
void
rgFree(gl_render_group* rg)
{
if (rg == nullptr)
return;
utilSafeFree(rg->vertex_buffer.buffer);
utilSafeFree(rg->vertex_normals.buffer);
utilSafeFree(rg->index_buffer.indices);
utilSafeFree(rg->color_buffer.buffer);
utilSafeFree(rg);
}