A small OpenGL 3+ renderer and game engine
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.
 
 
 

338 lines
8.9 KiB

#include <glm/gtc/matrix_transform.hpp>
#include "dumbLog.h"
#include "render_object.h"
// forward declarations
struct default_render_object
{
glm::mat4 node_xform;
GLuint tex_id;
GLuint vertex_buffer_id;
GLuint normal_buffer_id;
GLuint uv_buffer_id;
GLuint index_buffer_id;
uint index_buffer_count;
};
struct simple_render_object
{
glm::mat4 model_transform;
GLuint vertex_buffer_id;
GLuint vertex_color_buffer_id;
uint vertex_count;
GLenum draw_mode;
};
struct render_objects
{
void* objects;
uint count;
mesh_t mesh_type;
};
void drawDefault(render_objects* r_objs,
glm::mat4 world_transform,
camera& cam,
shader_wrapper sw,
light_group* lights);
void drawSimple(render_objects* r_objs,
glm::mat4 world_transform,
camera& cam,
shader_wrapper sw,
light_group* lights);
inline void enableGLFloatBuffer(uint buffer_id, uint location);
bool initGLFloatBuffer(glm::vec3* buffer,
uint count,
GLuint& buffer_id,
GLenum usage = GL_STATIC_DRAW);
bool initGLIndexBuffer(uint* buffer, uint num_indices, GLuint& buffer_id);
bool initGLTexture(const util_image image, GLuint& tex_id);
bool loadMeshIntoGL(default_render_object* ro_out, mesh_info* mi_in);
inline void updateMatrices(default_shader_program* shader,
camera& cam,
glm::mat4 world_xform,
glm::mat4 node_xform);
// interface
render_objects*
roInitModel(mesh_group mg)
{
uint count = mg.num_meshes;
assert(count > 0);
render_objects* r_objs = UTIL_ALLOC(1, render_objects);
r_objs->objects = UTIL_ALLOC(count, default_render_object);
r_objs->count = count;
r_objs->mesh_type = DEFAULT_MESHES;
default_render_object* objects = (default_render_object*) r_objs->objects;
for (uint i = 0; i < count; i++) {
if (!loadMeshIntoGL(&objects[i], mg.meshes[i])) {
roFree(r_objs);
return nullptr;
}
}
return r_objs;
}
render_objects*
roInitSimpleMesh(simple_mesh& mesh_in, GLenum draw_mode)
{
render_objects* r_objs = UTIL_ALLOC(1, render_objects);
r_objs->count = 1;
r_objs->mesh_type = SIMPLE_MESH;
r_objs->objects = UTIL_ALLOC(1, simple_render_object);
simple_render_object* objects = (simple_render_object*) r_objs->objects;
if (initGLFloatBuffer(mesh_in.vertices,
mesh_in.num_vertices,
objects->vertex_buffer_id,
GL_DYNAMIC_DRAW) &&
initGLFloatBuffer(mesh_in.vert_colors,
mesh_in.num_vertices,
objects->vertex_color_buffer_id,
GL_DYNAMIC_DRAW))
{
objects->model_transform = mesh_in.model_transform;
objects->vertex_count = mesh_in.num_vertices;
objects->draw_mode = draw_mode;
return r_objs;
}
LOG(Error) << "Failed to initialize render_object\n";
roFree(r_objs);
return nullptr;
}
void
roFree(render_objects* r_objs)
{
if (r_objs->mesh_type == SIMPLE_MESH) {
//
} else if (r_objs->mesh_type == DEFAULT_MESHES) {
default_render_object* objects =
(default_render_object*) r_objs->objects;
for (uint i = 0; i < r_objs->count; i++) {
glDeleteBuffers(1, &objects[i].vertex_buffer_id);
glDeleteBuffers(1, &objects[i].normal_buffer_id);
glDeleteBuffers(1, &objects[i].uv_buffer_id);
glDeleteBuffers(1, &objects[i].index_buffer_id);
glDeleteTextures(1, &objects[i].tex_id);
}
utilSafeFree(r_objs->objects);
utilSafeFree(r_objs);
}
}
// TODO: update projection * view matrices once per frame here
void
roDraw(render_objects* r_objs,
glm::mat4 world_transform,
camera& cam,
shader_wrapper sw,
light_group* lights)
{
if (r_objs->mesh_type == SIMPLE_MESH)
drawSimple(r_objs, world_transform, cam, sw, lights);
else if (r_objs->mesh_type == DEFAULT_MESHES)
drawDefault(r_objs, world_transform, cam, sw, lights);
}
void
roUpdateSimpleMesh(render_objects* r_objs, simple_mesh* mesh, GLenum draw_mode)
{
assert(r_objs != nullptr && r_objs->objects != nullptr);
assert(mesh != nullptr && mesh->vertices != nullptr);
simple_render_object* sro = (simple_render_object*) r_objs->objects;
assert(sro->vertex_count == mesh->num_vertices);
sro->draw_mode = draw_mode;
glBindBuffer(GL_ARRAY_BUFFER, sro->vertex_buffer_id);
glBufferSubData(
GL_ARRAY_BUFFER,
0,
3 * sro->vertex_count * sizeof(GLfloat),
mesh->vertices);
glBindBuffer(GL_ARRAY_BUFFER, sro->vertex_color_buffer_id);
glBufferSubData(
GL_ARRAY_BUFFER,
0,
3 * sro->vertex_count * sizeof(GLfloat),
mesh->vert_colors);
}
// internal
void
drawDefault(render_objects* r_objs,
glm::mat4 world_transform,
camera& cam,
shader_wrapper sw,
light_group* lights)
{
default_shader_program* shader = sw.default_shader;
default_render_object* objects = (default_render_object*) r_objs->objects;
glUseProgram(shader->program_id);
updateMatrices(shader, cam, world_transform, objects->node_xform);
if (lights->needs_update) lightsUpdate(lights, shader);
for (uint i = 0; i < r_objs->count; i++) {
enableGLFloatBuffer(objects[i].vertex_buffer_id, 0);
enableGLFloatBuffer(objects[i].normal_buffer_id, 1);
// TODO: could pass in a stride parameter here to enableGLFloatBuffer()
// could then use a 2d buffer for uv coords
enableGLFloatBuffer(objects[i].uv_buffer_id, 2);
glBindTexture(GL_TEXTURE_2D, objects[i].tex_id);
glUniform1i(shader->sampler_id, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, objects[i].index_buffer_id);
glDrawElements(GL_TRIANGLES,
objects[i].index_buffer_count,
GL_UNSIGNED_INT,
0);
glDisableVertexAttribArray(0);
glDisableVertexAttribArray(1);
glDisableVertexAttribArray(2);
}
glUseProgram(0);
}
void
drawSimple(render_objects* r_objs,
glm::mat4 world_transform,
camera& cam,
shader_wrapper sw,
light_group* lights)
{
simple_render_object* ro = (simple_render_object*) r_objs->objects;
simple_shader_program* shader =
(simple_shader_program*) sw.simple_shader;
glUseProgram(shader->program_id);
cam.MVP = cam.projection * cam.view * ro->model_transform;
glUniformMatrix4fv(
shader->world_transform_id, 1, GL_FALSE, &world_transform[0][0]);
glUniformMatrix4fv(shader->MVP_id, 1, GL_FALSE, &cam.MVP[0][0]);
enableGLFloatBuffer(ro->vertex_buffer_id, 0);
enableGLFloatBuffer(ro->vertex_color_buffer_id, 1);
glDrawArrays(ro->draw_mode, 0, ro->vertex_count);
glDisableVertexAttribArray(0);
glDisableVertexAttribArray(1);
glUseProgram(0);
}
inline void
enableGLFloatBuffer(uint buffer_id, uint location)
{
glEnableVertexAttribArray(location);
glBindBuffer(GL_ARRAY_BUFFER, buffer_id);
glVertexAttribPointer(location, 3, GL_FLOAT, GL_FALSE, 0, (void*) 0);
}
bool
initGLFloatBuffer(glm::vec3* buffer,
uint count,
GLuint& buffer_id,
GLenum usage)
{
glGenBuffers(1, &buffer_id);
glBindBuffer(GL_ARRAY_BUFFER, buffer_id);
glBufferData(GL_ARRAY_BUFFER,
count * 3 * sizeof(GLfloat), // NOTE: 3 floats per vertex prop
buffer,
usage);
return (glGetError() == GL_NO_ERROR);
}
bool
initGLIndexBuffer(uint* buffer, uint num_indices, GLuint& buffer_id)
{
glGenBuffers(1, &buffer_id);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer_id);
glBufferData(GL_ELEMENT_ARRAY_BUFFER,
num_indices * sizeof(uint),
buffer,
GL_STATIC_DRAW);
return (glGetError() == GL_NO_ERROR);
}
bool
initGLTexture(const util_image image, GLuint& tex_id)
{
glGenTextures(1, &tex_id);
glBindTexture(GL_TEXTURE_2D, tex_id);
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
GLenum pixel_format = (image.num_channels == 3) ? GL_RGB : GL_RGBA;
glTexImage2D(GL_TEXTURE_2D, 0, pixel_format, image.w, image.h, 0,
pixel_format, GL_UNSIGNED_BYTE, image.pixels);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
return (glGetError() == GL_NO_ERROR);
}
bool
loadMeshIntoGL(default_render_object* ro_out, mesh_info* mi_in)
{
assert(mi_in != nullptr && ro_out != nullptr);
if (initGLFloatBuffer(mi_in->vertices,
mi_in->num_vertices,
ro_out->vertex_buffer_id) &&
initGLFloatBuffer(mi_in->normals,
mi_in->num_vertices,
ro_out->normal_buffer_id) &&
// FIXME: this is broken now with tinygltf, need to use vec2
initGLFloatBuffer(mi_in->texture_coords,
mi_in->num_vertices,
ro_out->uv_buffer_id) &&
initGLIndexBuffer(mi_in->indices,
mi_in->num_indices,
ro_out->index_buffer_id) &&
initGLTexture(mi_in->diffuse_texture, ro_out->tex_id))
{
ro_out->node_xform = mi_in->model_transform;
ro_out->index_buffer_count = mi_in->num_indices;
return true;
}
LOG(Error) << "Failed to initialize render_object\n";
return false;
}
// TODO: really only need to update the view and projection matrices once per
// frame, maybe add another interface function in render_object to call from
// renRenderFrame
inline void
updateMatrices(default_shader_program* shader,
camera& cam,
glm::mat4 world_xform,
glm::mat4 node_xform)
{
glUniformMatrix4fv(
shader->world_transform_id, 1, GL_FALSE, &world_xform[0][0]);
glUniformMatrix4fv(shader->model_matrix_id, 1, GL_FALSE, &node_xform[0][0]);
glUniformMatrix4fv(shader->view_matrix_id, 1, GL_FALSE, &cam.view[0][0]);
glUniformMatrix4fv(shader->projection_matrix_id, 1, GL_FALSE,
&cam.projection[0][0]);
glm::mat3 normal_matrix = glm::transpose(
glm::inverse(glm::mat3(cam.model)));
glUniformMatrix3fv(shader->normal_matrix_id, 1, GL_FALSE,
&normal_matrix[0][0]);
}