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.
 
 
 
 

354 lines
10 KiB

#include <sstream> // TODO: remove this and make something in util.h
#include <cassert>
#include <cstdlib> // calloc
#include <glm/glm.hpp>
#include <glm/geometric.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include "aixlog.hpp"
#include "render_group.h"
#include "mesh.h"
#include "util.h"
#define INFO_LOG_MAX_LENGTH 312;
// forward declarations
render_object * allocateRenderObject(uint buffer_len, uint index_len = 0);
void freeRenderObject(render_object* ro);
bool convertMeshInfo(meMeshInfo* mesh, render_object* ro, bool use_normals);
void sendIndexBufferToGL(gl_index_buffer* index_buffer, GLenum usage, GLenum target);
// interface
render_group*
rgInitSingle(rg_shader_program& sp, uint vertex_buffer_len,
bool use_normals, uint index_buffer_len,
GLenum draw_mode)
{
render_group* rg = UTIL_ALLOC(1, render_group);
rg->shader = sp;
rg->use_normals = use_normals;
rg->draw_indexed = (index_buffer_len > 0);
rg->draw_mode = draw_mode;
rg->render_objects = UTIL_ALLOC(1, render_object*);
rg->render_objects[0] = allocateRenderObject(vertex_buffer_len, index_buffer_len);
rg->num_objects = 1;
if (rg->render_objects[0] == nullptr) {
LOG(ERROR) << "Allocation Error\n";
rgFree(rg);
}
return rg;
}
bool
rgInitShaderProgram(rg_shader_program& sp, const char * vertex_code, const char * frag_code)
{
glGenVertexArrays(1, &sp.vertex_array_id);
glBindVertexArray(sp.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);
sp.program_id = glCreateProgram();
glAttachShader(sp.program_id, vertex_shader_id);
glAttachShader(sp.program_id, fragment_shader_id);
glLinkProgram(sp.program_id);
sp.model_matrix_id = glGetUniformLocation(sp.program_id, "model");
sp.view_matrix_id = glGetUniformLocation(sp.program_id, "view");
sp.projection_matrix_id = glGetUniformLocation(sp.program_id, "projection");
sp.normal_matrix_id = glGetUniformLocation(sp.program_id, "normal_matrix");
sp.num_lights_id = glGetUniformLocation(sp.program_id, "num_lights");
glDetachShader(sp.program_id, vertex_shader_id);
glDetachShader(sp.program_id, fragment_shader_id);
glDeleteShader(vertex_shader_id);
glDeleteShader(fragment_shader_id);
// TODO: quick hack to allow 'dynamic' stack allocation for msvc
// also remove INFO_LOG_MAX_LENGTH define
GLint isLinked = 0;
glGetProgramiv(sp.program_id, GL_LINK_STATUS, &isLinked);
if (isLinked == GL_FALSE) {
GLint maxLength = INFO_LOG_MAX_LENGTH;
glGetProgramiv(sp.program_id, GL_INFO_LOG_LENGTH, &maxLength);
#ifdef _WIN32
GLchar infoLog[312] = { 0 };
#else
GLchar infoLog[maxLength] = { 0 };
#endif
glGetProgramInfoLog(sp.program_id, maxLength, &maxLength, &infoLog[0]);
LOG(ERROR) << infoLog << "\n";
glDeleteProgram(sp.program_id);
return false;
}
return true;
}
// TODO: move Entity initialization to Entity.h/.cpp to fix circular dependancy
// then just call into convertMeshInfo directly
bool
rgInitEntity(Entity* e)
{
render_group* rg = UTIL_ALLOC(1, render_group);
e->ren_group = rg;
rg->draw_indexed = true;
rg->draw_mode = GL_TRIANGLES;
rg->use_normals = e->mesh_group.use_normals;
uint num_meshes = rg->num_objects = e->mesh_group.num_meshes;
// apply translation/rotation/scaling
e->world_transform = glm::mat4(1.0);
e->world_transform = glm::translate(e->world_transform, e->translation);
e->world_transform = glm::scale(e->world_transform, e->scale);
rg->render_objects = (render_object**) std::calloc(num_meshes, sizeof(render_object*));
for (uint i = 0; i < num_meshes; i++)
{
uint buffer_len = e->mesh_group.meshes[i]->num_vertices * 3;
uint index_len = e->mesh_group.meshes[i]->num_indices;
rg->render_objects[i] = allocateRenderObject(buffer_len, index_len);
if (rg->render_objects[i] == nullptr)
return false;
// TODO: safely free memory here when convertMeshInfo returns false
if (!convertMeshInfo(e->mesh_group.meshes[i], rg->render_objects[i], rg->use_normals))
return false;
}
return true;
}
// TODO: this should be called more like, rgInitGLBuffer()
// NOTE: could also make internal by adding more utility function for eg)
// initHexGrid(), initDebugDraw()...
void
rgBufferData(gl_buffer* buffer, GLenum usage, GLenum target)
{
glGenBuffers(1, &buffer->buffer_id);
glBindBuffer(target, buffer->buffer_id);
glBufferData(target, buffer->buffer_len * sizeof(GLfloat), buffer->buffer, usage);
}
void
rgDraw(render_group* rg, glm::mat4 model_matrix,
glm::mat4 view_matrix, glm::mat4 projection_matrix,
rg_point_light* lights, uint num_lights,
bool update_vertex_data, bool update_color_data)
{
for (uint i = 0; i < rg->num_objects; i++) {
render_object* ro = rg->render_objects[i];
glUseProgram(rg->shader.program_id);
glUniformMatrix4fv(rg->shader.model_matrix_id, 1, GL_FALSE, &model_matrix[0][0]);
glUniformMatrix4fv(rg->shader.view_matrix_id, 1, GL_FALSE, &view_matrix[0][0]);
glUniformMatrix4fv(rg->shader.projection_matrix_id, 1, GL_FALSE, &projection_matrix[0][0]);
// 1st attribute buffer : vertices
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, ro->vertex_buffer.buffer_id);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void*) 0);
if (update_vertex_data)
{
glBufferSubData(GL_ARRAY_BUFFER, 0, ro->vertex_buffer.buffer_len * sizeof(GLfloat),
ro->vertex_buffer.buffer);
}
// 2nd attribute buffer : colors
if (ro->color_buffer.buffer)
{
glEnableVertexAttribArray(1);
glBindBuffer(GL_ARRAY_BUFFER, ro->color_buffer.buffer_id);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, (void*) 0);
if (update_color_data) {
glBufferSubData(GL_ARRAY_BUFFER, 0, ro->color_buffer.buffer_len * sizeof(GLfloat),
ro->color_buffer.buffer);
}
}
// 3rd attribute buffer: normals
if (rg->use_normals) {
glEnableVertexAttribArray(2);
glBindBuffer(GL_ARRAY_BUFFER, ro->normal_buffer.buffer_id);
glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 0, (void*) 0);
glm::mat3 normal_matrix = glm::transpose(glm::inverse(glm::mat3(model_matrix)));
glUniformMatrix3fv(rg->shader.normal_matrix_id, 1, GL_FALSE, &normal_matrix[0][0]);
// TODO: use Uniform Buffer Objects to update light data
// https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_uniform_buffer_object.txt
// https://www.khronos.org/opengl/wiki/Uniform_Buffer_Objects
glUniform1ui(rg->shader.num_lights_id, num_lights);
for (uint i = 0; i < num_lights; i++) {
// TODO: don't do this every frame * every render_group
std::stringstream ss;
ss << "lights[" << i << "].position";
int light_pos_loc = glGetUniformLocation(rg->shader.program_id, ss.str().c_str());
glUniform3fv(light_pos_loc, 1, &lights[i].position[0]);
}
}
// draw
if (rg->draw_indexed) {
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ro->index_buffer.buffer_id);
glDrawElements(rg->draw_mode, ro->index_buffer.buffer_len, GL_UNSIGNED_INT, 0);
} else {
glDrawArrays(rg->draw_mode, 0, ro->vertex_buffer.buffer_len / 3);
}
// cleanup
glDisableVertexAttribArray(0);
if (ro->color_buffer.buffer)
glDisableVertexAttribArray(1);
if (rg->use_normals)
glDisableVertexAttribArray(2);
glUseProgram(0);
}
}
void
rgFree(render_group* rg)
{
if (rg == nullptr) {
LOG(ERROR) << "tried to free nullptr\n";
return;
}
for (uint i = 0; i < rg->num_objects; i++)
freeRenderObject(rg->render_objects[i]);
utilSafeFree(rg->render_objects);
utilSafeFree(rg);
}
// internal
render_object *
allocateRenderObject(uint buffer_len, uint index_len)
{
render_object* ro = UTIL_ALLOC(1, render_object);
if (ro == nullptr)
return nullptr;
ro->vertex_buffer.buffer = UTIL_ALLOC(buffer_len, GLfloat);
ro->vertex_buffer.buffer_len = buffer_len;
ro->normal_buffer.buffer = UTIL_ALLOC(buffer_len, GLfloat);
ro->normal_buffer.buffer_len = buffer_len;
ro->color_buffer.buffer = UTIL_ALLOC(buffer_len, GLfloat);
ro->color_buffer.buffer_len = buffer_len;
if (index_len > 0) {
ro->index_buffer.buffer = UTIL_ALLOC(index_len, uint);
ro->index_buffer.buffer_len = index_len;
}
if (ro->vertex_buffer.buffer == nullptr ||
ro->normal_buffer.buffer == nullptr ||
ro->color_buffer.buffer == nullptr ||
((index_len > 0) && (ro->vertex_buffer.buffer == nullptr)))
{
freeRenderObject(ro);
return nullptr;
}
return ro;
}
void
freeRenderObject(render_object* ro)
{
utilSafeFree(ro->vertex_buffer.buffer);
utilSafeFree(ro->normal_buffer.buffer);
utilSafeFree(ro->color_buffer.buffer);
utilSafeFree(ro->index_buffer.buffer);
utilSafeFree(ro);
}
void
sendIndexBufferToGL(gl_index_buffer* index_buffer, GLenum usage, GLenum target)
{
glGenBuffers(1, &index_buffer->buffer_id);
glBindBuffer(target, index_buffer->buffer_id);
glBufferData(target, index_buffer->buffer_len * sizeof(uint), index_buffer->buffer, usage);
}
bool
convertMeshInfo(meMeshInfo* mesh, render_object* ro, bool use_normals)
{
uint vertex_buf_len = mesh->num_vertices * 3;
GLfloat* vertex_buf = ro->vertex_buffer.buffer;
GLfloat* color_buf = ro->color_buffer.buffer;
GLfloat* normal_buf = ro->normal_buffer.buffer;
uint* index_buf = ro->index_buffer.buffer;
if (!vertex_buf || !color_buf || !normal_buf || !index_buf)
return false;
// dump vertices, colors, and normals into render_group buffers
uint vertex_index = 0;
uint vertex_prop_index = 0;
for (uint i = 0; i < vertex_buf_len; i++) {
const glm::vec3& vertex = mesh->vertices[vertex_index];
const glm::vec3& normal = 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 (use_normals) {
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] = mesh->diffuse_color[vertex_prop_index];
vertex_prop_index++;
if (vertex_prop_index == 3) {
vertex_prop_index = 0;
vertex_index++;
}
}
// dump indices
for (uint i = 0; i < mesh->num_indices; i++)
index_buf[i] = mesh->indices[i];
rgBufferData(&ro->vertex_buffer, GL_DYNAMIC_DRAW, GL_ARRAY_BUFFER);
rgBufferData(&ro->color_buffer, GL_STATIC_DRAW, GL_ARRAY_BUFFER);
if (use_normals)
rgBufferData(&ro->normal_buffer, GL_STATIC_DRAW, GL_ARRAY_BUFFER);
sendIndexBufferToGL(&ro->index_buffer, GL_STATIC_DRAW, GL_ELEMENT_ARRAY_BUFFER);
return true;
}