diff --git a/.gitignore b/.gitignore index 9eddfa6..d9f1259 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,4 @@ tags msvc/.vs/ msvc/x64/ imgui.ini - +doc/ diff --git a/data/default.fs b/data/default.fs index 7d87f03..460a4a8 100644 --- a/data/default.fs +++ b/data/default.fs @@ -22,5 +22,6 @@ void main() brightness = clamp(brightness, 0, 1); color = brightness * fragmentColor; + //color = vec3(255, 255, 255); } diff --git a/src/entity.h b/src/entity.h new file mode 100644 index 0000000..2742e19 --- /dev/null +++ b/src/entity.h @@ -0,0 +1,17 @@ + +#pragma once + +#include "mesh.h" +#include "render_group.h" + + +struct render_group; + +struct Entity +{ + meMeshGroup mesh_group; + render_group* ren_group; + + //TODO: more entity properties: sound, gameplay info +}; + diff --git a/src/hexgame.cpp b/src/hexgame.cpp index c3ba8cf..a76a37c 100644 --- a/src/hexgame.cpp +++ b/src/hexgame.cpp @@ -1,18 +1,27 @@ //----------------------------------------------------------------------------- // TODO: +// - add a color buffer to hex_line and debug render groups to re-use +// fragment shaders and simplify draw rgDraw() +// - need to add normal buffer too // - lighting // - add light struct // - pass all lights to render groups/shaders every frame -// - need to give hexagons vertex normals (can then get rid of rgDraw()) // - maybe use more than one model in a scene // - map generation // - pathfinding // - assimp animation -// - update imgui to v1.62 -- requires changes to example api // - replace aixlog with custom logging iostream? // - fix vector normalization in renderer.cpp when moving in 2 directions // - add prefix to interface function names for eg) gooey.h, renderer.h -// - test build on windows / update vs project files +// - move hex logic to new file to leave only init glue in a main.cpp +// - move SDL input callbacks somewhere too? +// - think about combining mesh data and render_group data to save some memory +// - actually don't need to save the vertex/normal buffers after passing +// to opengl +// - replace calls to malloc/free with safe(r) function in util.h +// - also need to make a macro that works with malloc void * and c++ +// - add cpu perforcmace counters in render loop +// - check for memory leaks w/ valgrind //----------------------------------------------------------------------------- // Some defaults for the game layout @@ -47,19 +56,19 @@ #endif #include "aixlog.hpp" -#include "util.h" +#include "gooey.h" #include "hexgame.h" #include "hexlib.h" -#include "renderer.h" -#include "gooey.h" #include "mesh.h" #include "platform_wait_for_vblank.h" +#include "renderer.h" +#include "util.h" using std::vector; -game_state* g_game_state; -render_state* g_render_state; -vector g_polygon_select_vertices = {Point(), Point(), Point(), Point()}; +static game_state* g_game_state; +static render_state* g_render_state; +static vector g_polygon_select_vertices = {Point(), Point(), Point(), Point()}; void createHexes(vector *hxi_array, const Layout &layout, uint32 color) @@ -454,26 +463,20 @@ cleanUp(SDL_Handles &handles) SDL_DestroyWindow(handles.window); SDL_Quit(); - if (g_game_state) - { - if (g_game_state->hex_array) - delete g_game_state->hex_array; - delete g_game_state; - } + game_state* g = g_game_state; - // TODO: C-style memory management for entities - render_state* r = g_render_state; - if (r) { - for (uint i = 0; i < r->entity_count; i++) { - meFreeMesh(r->entities[i].mesh); - } + if (g_game_state->hex_array) + delete g_game_state->hex_array; - std::free(r->entities); - r->entities = nullptr; - delete r; - r = nullptr; + for (uint i = 0; i < g->entity_count; i++) { + meFreeMeshGroup(g->entities[i].mesh_group); + rgFree(g->entities[i].ren_group); } + utilSafeFree(g->entities); + + delete g_render_state; + delete g_game_state; return true; } @@ -541,19 +544,32 @@ int main(int argc, char* argv[]) } } + // TODO: better entity memory management if (meInitAssimp()) { - // TODO: better entity memory management // TODO: hard coded limit of 1000 entities here - Entity* e = (Entity*) std::calloc(1000, sizeof(Entity)); - render_state* r = g_render_state; - r->entities = e; - //const char* test_file = "../data/animated.block.dae"; - const char* test_file = "../data/catepillar.dae"; - meMeshInfo* mesh = meLoadFromFile(test_file); - - if (mesh != nullptr) { - r->entities[r->entity_count].mesh = mesh; - r->entity_count++; + game_state* g = g_game_state; + g->entities = (Entity*) std::calloc(1000, sizeof(Entity)); + + const char* test_file1 = "../data/animated.block.dae"; + const char* test_file2 = "../data/catepillar.dae"; + meMeshGroup mg1, mg2; + + if (meLoadFromFile(test_file1, mg1)) { + g->entities[g->entity_count].mesh_group = mg1; + g->entity_count++; + } + else { + LOG(ERROR) << "Error loading file, exiting\n"; + return 1; + } + + if (meLoadFromFile(test_file2, mg2)) { + g->entities[g->entity_count].mesh_group = mg2; + g->entity_count++; + } + else { + LOG(ERROR) << "Error loading file, exiting\n"; + return 1; } } else { @@ -561,7 +577,7 @@ int main(int argc, char* argv[]) return 1; } - if (!createScene(g_game_state->hex_array, g_render_state->entities, g_render_state->entity_count)) { + if (!createScene(g_game_state->hex_array, g_game_state->entities, g_game_state->entity_count)) { LOG(ERROR) << "Error in vertex data, exiting\n"; return 1; } @@ -571,17 +587,20 @@ int main(int argc, char* argv[]) return 1; } +#if defined(_WIN32) if (!platform_init(handles.window)) { LOG(ERROR) << "Couldn't get SDL platform info, exiting\n"; return 1; } +#else + LOG(DEBUG) << "TODO: Implement better frame timer on linux\n"; // should notice this +#endif // main loop while (processSDLEvents()) { #if defined(_WIN32) // sort-of fixed in windows with platform_wait_for_vblank() #else - LOG(DEBUG) << "TODO: Implement better frame timer on linux\n"; // should notice this SDL_Delay(16); // ~60hz #endif game_state* g = g_game_state; @@ -590,7 +609,7 @@ int main(int argc, char* argv[]) moveCamera(g->is_moveup, g->is_moveleft, g->is_movedown, g->is_moveright, g->is_moveforward, g->is_movebackward); - renderFrame(g->hex_array, g_render_state->entities, g_render_state->entity_count); + renderFrame(g->hex_array, g->entities, g->entity_count); if (r->is_debug_draw && g->draw_mode == CONE_FILL) renderDebug(g_polygon_select_vertices); diff --git a/src/hexgame.h b/src/hexgame.h index ab44cc7..6f3e3bd 100644 --- a/src/hexgame.h +++ b/src/hexgame.h @@ -11,9 +11,10 @@ #include +#include "entity.h" #include "hexlib.h" -#include "util.h" #include "mesh.h" +#include "util.h" // TODO: should move this to renderer.h @@ -46,22 +47,12 @@ enum HexDrawMode PATHFINDING }; -struct Entity -{ - meMeshInfo* mesh; - //TODO: more entity properties: sound, gameplay info -}; - struct render_state { v2i viewport_dims; bool is_debug_draw; uint32 fill_color; uint32 selected_fill_color; - - // TODO: should move this to game_state - uint32 entity_count = 0; - Entity* entities = nullptr; }; struct game_state @@ -73,7 +64,11 @@ struct game_state HexDrawMode draw_mode; const Layout hex_layout; - // movement controls + // TODO: WiP + uint32 entity_count = 0; + Entity* entities = nullptr; + + // camera movement controls bool is_selecting = false; bool is_camera_rotate = false; bool is_moveforward = false; diff --git a/src/mesh.cpp b/src/mesh.cpp index 6181793..2d681a7 100644 --- a/src/mesh.cpp +++ b/src/mesh.cpp @@ -14,6 +14,10 @@ #include "mesh.h" +inline glm::vec3 copyVector(aiVector3D v_in, glm::vec3& v_out); +meMeshInfo* copyMeshInfo(const aiScene* scene, aiMesh* mesh); +void freeMesh(meMeshInfo* mesh); + bool meInitAssimp() { @@ -27,6 +31,43 @@ meInitAssimp() return true; } +bool +meLoadFromFile(const char* filename, meMeshGroup& mesh_group) +{ + LOG(INFO) << "Loading file: " << filename << "\n"; + const aiScene* scene = aiImportFile(filename, aiProcessPreset_TargetRealtime_MaxQuality); + + if (scene->mNumMeshes < 1) { + LOG(ERROR) << "Scene contains no meshes\n"; + return false; + } + + mesh_group.num_meshes = scene->mNumMeshes; + mesh_group.meshes = (meMeshInfo**) std::calloc(mesh_group.num_meshes, sizeof(meMeshInfo*)); + mesh_group.use_normals = scene->mMeshes[0]->HasNormals(); + + for (uint i = 0; i < scene->mNumMeshes; i++) { + mesh_group.meshes[i] = copyMeshInfo(scene, scene->mMeshes[i]); + } + + // free memeory from assimp + aiReleaseImport(scene); + return true; +} + +void +meFreeMeshGroup(meMeshGroup& mesh_group) +{ + for (uint i = 0; i < mesh_group.num_meshes; i++) + freeMesh(mesh_group.meshes[i]); +} + +void +meShutdownAssimp() +{ + aiDetachAllLogStreams(); +} + inline glm::vec3 copyVector(aiVector3D v_in, glm::vec3& v_out) { @@ -37,26 +78,13 @@ copyVector(aiVector3D v_in, glm::vec3& v_out) } meMeshInfo* -meLoadFromFile(const char* filename) +copyMeshInfo(const aiScene* scene, aiMesh* mesh) { - const aiScene* scene = aiImportFile(filename, aiProcessPreset_TargetRealtime_MaxQuality); - - if (scene->mNumMeshes != 1) { - LOG(ERROR) << "We Can only handle 1 mesh per entity atm\n"; -#if 0 - return nullptr; -#else - LOG(DEBUG) << "num meshes: " << scene->mNumMeshes << "\n"; - LOG(DEBUG) << "num materials: " << scene->mNumMaterials << "\n"; -#endif - } - meMeshInfo* mi = (meMeshInfo*) std::calloc(1, sizeof(meMeshInfo)); // TODO: using hard coded model transform for now mi->model_transform = glm::scale(glm::mat4(1), glm::vec3(100, 100, 100)); // allocate buffers for vertex and index data from mesh - aiMesh* mesh = scene->mMeshes[2]; mi->num_vertices = mesh->mNumVertices; mi->vertices = (glm::vec3 *) std::calloc(mi->num_vertices, sizeof(glm::vec3)); mi->normals = (glm::vec3 *) std::calloc(mi->num_vertices, sizeof(glm::vec3)); @@ -77,7 +105,7 @@ meLoadFromFile(const char* filename) mi->indices[i * 3 + j] = mesh->mFaces[i].mIndices[j]; // material - aiMaterial* mat = scene->mMaterials[1]; + aiMaterial* mat = scene->mMaterials[mesh->mMaterialIndex]; aiColor3D color(0.f, 0.f, 0.f); if (AI_SUCCESS != mat->Get(AI_MATKEY_COLOR_DIFFUSE, color)) { LOG(ERROR) << "Some Assimp-type-error\n"; @@ -87,27 +115,15 @@ meLoadFromFile(const char* filename) mi->diffuse_color.b = color.b; } - // free memeory from assimp - aiReleaseImport(scene); return mi; } void -meFreeMesh(meMeshInfo* mesh) +freeMesh(meMeshInfo* mesh) { - std::free(mesh->vertices); - mesh->vertices = nullptr; - std::free(mesh->normals); - mesh->normals = nullptr; - std::free(mesh->indices); - mesh->indices = nullptr; - std::free(mesh); - mesh = nullptr; -} - -void -meShutdownAssimp() -{ - aiDetachAllLogStreams(); + utilSafeFree(mesh->vertices); + utilSafeFree(mesh->normals); + utilSafeFree(mesh->indices); + utilSafeFree(mesh); } diff --git a/src/mesh.h b/src/mesh.h index d572412..2eecc55 100644 --- a/src/mesh.h +++ b/src/mesh.h @@ -20,12 +20,20 @@ struct meMeshInfo glm::vec3 diffuse_color; }; +struct meMeshGroup +{ + bool use_normals = false; + uint num_meshes = 0; + meMeshInfo** meshes = nullptr; + // animation/bonemapping info here... +}; + bool meInitAssimp(); -meMeshInfo* meLoadFromFile(const char* filename); +bool meLoadFromFile(const char* filename, meMeshGroup& mesh_group); -void meFreeMesh(meMeshInfo* mesh); +void meFreeMeshGroup(meMeshGroup& mesh_group); void meShutdownAssimp(); diff --git a/src/render_group.cpp b/src/render_group.cpp index 6a4d08c..3e0fb5e 100644 --- a/src/render_group.cpp +++ b/src/render_group.cpp @@ -2,9 +2,6 @@ #include #include // calloc -// TODO: decide on extension library -//#include -#include #include #include #include @@ -13,14 +10,30 @@ #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 sendBufferToGL(gl_buffer* buffer, GLenum usage, GLenum target); +void sendIndexBufferToGL(gl_index_buffer* index_buffer, GLenum usage, GLenum target); +void drawRenderObject(render_group* rg, render_object* ro, + glm::mat4 model_matrix, glm::mat4 view_matrix, glm::mat4 projection_matrix, + glm::vec3 light_position, GLuint light_id, bool update_vertex_data); + + +// interface + bool -rgInitShaderProgram(gl_render_group* rg, const char * vertex_code, const char * frag_code) +rgInitShaderProgram(rg_shader_program& sp, const char * vertex_code, const char * frag_code) { - glGenVertexArrays(1, &rg->vertex_array_id); - glBindVertexArray(rg->vertex_array_id); + 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); @@ -29,18 +42,18 @@ rgInitShaderProgram(gl_render_group* rg, const char * vertex_code, const char * 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); + sp.program_id = glCreateProgram(); + glAttachShader(sp.program_id, vertex_shader_id); + glAttachShader(sp.program_id, fragment_shader_id); + glLinkProgram(sp.program_id); - rg->model_matrix_id = glGetUniformLocation(rg->program_id, "model"); - rg->view_matrix_id = glGetUniformLocation(rg->program_id, "view"); - rg->projection_matrix_id = glGetUniformLocation(rg->program_id, "projection"); - rg->normal_matrix_id = glGetUniformLocation(rg->program_id, "normal_matrix"); + 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"); - glDetachShader(rg->program_id, vertex_shader_id); - glDetachShader(rg->program_id, fragment_shader_id); + glDetachShader(sp.program_id, vertex_shader_id); + glDetachShader(sp.program_id, fragment_shader_id); glDeleteShader(vertex_shader_id); glDeleteShader(fragment_shader_id); @@ -48,18 +61,18 @@ rgInitShaderProgram(gl_render_group* rg, const char * vertex_code, const char * // also remove INFO_LOG_MAX_LENGTH define #if 1 GLint isLinked = 0; - glGetProgramiv(rg->program_id, GL_LINK_STATUS, &isLinked); + glGetProgramiv(sp.program_id, GL_LINK_STATUS, &isLinked); if (isLinked == GL_FALSE) { GLint maxLength = INFO_LOG_MAX_LENGTH; - glGetProgramiv(rg->program_id, GL_INFO_LOG_LENGTH, &maxLength); + glGetProgramiv(sp.program_id, GL_INFO_LOG_LENGTH, &maxLength); #ifdef _WIN32 GLchar infoLog[312] = { 0 }; #else GLchar infoLog[maxLength] = { 0 }; #endif - glGetProgramInfoLog(rg->program_id, maxLength, &maxLength, &infoLog[0]); + glGetProgramInfoLog(sp.program_id, maxLength, &maxLength, &infoLog[0]); LOG(ERROR) << infoLog << "\n"; - glDeleteProgram(rg->program_id); + glDeleteProgram(sp.program_id); return false; } @@ -68,32 +81,130 @@ rgInitShaderProgram(gl_render_group* rg, const char * vertex_code, const char * return true; } -gl_buffer * -rgInitGLBuffer(gl_buffer* buffer, uint buffer_len) +// TODO: should pass in a buffer of colors here instead of hexes, move to renderer.cpp +// we don't need to know about hexes here +#if 0 +void +rgFillColorBuffer(GLfloat buf[], int len, std::vector* 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]; + utilConvertColor(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]; + } + } +} +#endif + +bool +rgInitEntity(Entity* e) +{ + render_group* rg = UTIL_ALLOC(1, render_group); + e->ren_group = rg; + rg->draw_indexed = true; + rg->use_normals = e->mesh_group.use_normals; + uint num_meshes = rg->num_objects = e->mesh_group.num_meshes; + + 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; +} + +void +rgDraw(render_group* rg, glm::mat4 model_matrix, glm::mat4 view_matrix, glm::mat4 projection_matrix, + glm::vec3 light_position, GLuint light_id, bool update_vertex_data) +{ + for (uint i = 0; i < rg->num_objects; i++) { + drawRenderObject(rg, rg->render_objects[i], model_matrix, view_matrix, projection_matrix, + light_position, light_id, false); + } +} + +void +rgFree(render_group* rg) { - buffer->buffer_len = buffer_len; - buffer->buffer = (GLfloat*) std::calloc(buffer_len, sizeof(GLfloat)); + if (rg == nullptr) { + LOG(ERROR) << "tried to free nullptr\n"; + return; + } - if (!buffer->buffer) - LOG(ERROR) << "Error allocating memory\n"; + for (uint i = 0; i < rg->num_objects; i++) + freeRenderObject(rg->render_objects[i]); - return buffer; + utilSafeFree(rg); } -gl_index_buffer * -initGLIndexBuffer(gl_index_buffer* index_buffer, uint buffer_len) + +// internal + +render_object * +allocateRenderObject(uint buffer_len, uint index_len) { - index_buffer->buffer_len = buffer_len; - index_buffer->buffer = (uint*) std::calloc(buffer_len, sizeof(uint)); + 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_buffer->buffer) - LOG(ERROR) << "Error allocating memory\n"; + 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; +} - return index_buffer; +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 -rgSendBufferToGL(gl_buffer* buffer, GLenum usage, GLenum target) +sendBufferToGL(gl_buffer* buffer, GLenum usage, GLenum target) { glGenBuffers(1, &buffer->buffer_id); glBindBuffer(target, buffer->buffer_id); @@ -108,39 +219,14 @@ sendIndexBufferToGL(gl_index_buffer* index_buffer, GLenum usage, GLenum target) glBufferData(target, index_buffer->buffer_len * sizeof(uint), index_buffer->buffer, usage); } -void -rgFillColorBuffer(GLfloat buf[], int len, std::vector* 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]; - utilConvertColor(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]; - } - } -} - -// NOTE: Copy assimp data to our structures for openGL bool -rgInitEntity(gl_render_group* rg, Entity* e) +convertMeshInfo(meMeshInfo* mesh, render_object* ro, bool use_normals) { - rg->draw_indexed = true; - rg->use_normals = (e->mesh->normals != nullptr); - uint vertex_buf_len = e->mesh->num_vertices * 3; - - GLfloat* vertex_buf = rgInitGLBuffer(&rg->vertex_buffer, vertex_buf_len)->buffer; - GLfloat* color_buf = rgInitGLBuffer(&rg->color_buffer, vertex_buf_len)->buffer; - GLfloat* normal_buf = rgInitGLBuffer(&rg->normal_buffer, vertex_buf_len)->buffer; - uint* index_buf = initGLIndexBuffer(&rg->index_buffer, e->mesh->num_indices)->buffer; + 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; @@ -150,8 +236,8 @@ rgInitEntity(gl_render_group* rg, Entity* e) 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]; + 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; @@ -159,7 +245,7 @@ rgInitEntity(gl_render_group* rg, Entity* e) case 2: vertex_buf[i] = vertex.z; break; } - if (rg->use_normals) { + if (use_normals) { switch (vertex_prop_index) { case 0: normal_buf[i] = normal.x; break; case 1: normal_buf[i] = normal.y; break; @@ -167,7 +253,7 @@ rgInitEntity(gl_render_group* rg, Entity* e) } } - color_buf[i] = e->mesh->diffuse_color[vertex_prop_index]; + color_buf[i] = mesh->diffuse_color[vertex_prop_index]; vertex_prop_index++; if (vertex_prop_index == 3) { @@ -177,89 +263,77 @@ rgInitEntity(gl_render_group* rg, Entity* e) } // dump indices - for (uint i = 0; i < e->mesh->num_indices; i++) - index_buf[i] = e->mesh->indices[i]; + for (uint i = 0; i < mesh->num_indices; i++) + index_buf[i] = mesh->indices[i]; - rgSendBufferToGL(&rg->vertex_buffer, GL_DYNAMIC_DRAW, GL_ARRAY_BUFFER); - rgSendBufferToGL(&rg->color_buffer, GL_STATIC_DRAW, GL_ARRAY_BUFFER); - if (rg->use_normals) - rgSendBufferToGL(&rg->normal_buffer, GL_STATIC_DRAW, GL_ARRAY_BUFFER); - sendIndexBufferToGL(&rg->index_buffer, GL_STATIC_DRAW, GL_ELEMENT_ARRAY_BUFFER); + sendBufferToGL(&ro->vertex_buffer, GL_DYNAMIC_DRAW, GL_ARRAY_BUFFER); + sendBufferToGL(&ro->color_buffer, GL_STATIC_DRAW, GL_ARRAY_BUFFER); + if (use_normals) + sendBufferToGL(&ro->normal_buffer, GL_STATIC_DRAW, GL_ARRAY_BUFFER); + sendIndexBufferToGL(&ro->index_buffer, GL_STATIC_DRAW, GL_ELEMENT_ARRAY_BUFFER); return true; } void -rgDraw(gl_render_group* rg, GLenum draw_mode, glm::mat4 model_matrix, - glm::mat4 view_matrix, glm::mat4 projection_matrix, +drawRenderObject(render_group* rg, render_object* ro, + 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]); + 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, rg->vertex_buffer.buffer_id); + 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, rg->vertex_buffer.buffer_len * sizeof(GLfloat), - rg->vertex_buffer.buffer); + glBufferSubData(GL_ARRAY_BUFFER, 0, ro->vertex_buffer.buffer_len * sizeof(GLfloat), + ro->vertex_buffer.buffer); } // 2nd attribute buffer : colors - if (rg->color_buffer.buffer) + if (ro->color_buffer.buffer) { glEnableVertexAttribArray(1); - glBindBuffer(GL_ARRAY_BUFFER, rg->color_buffer.buffer_id); + glBindBuffer(GL_ARRAY_BUFFER, ro->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); + glBufferSubData(GL_ARRAY_BUFFER, 0, ro->color_buffer.buffer_len * sizeof(GLfloat), + ro->color_buffer.buffer); } // 3rd attribute buffer: normals +#if 1 if (rg->use_normals) { glEnableVertexAttribArray(2); - glBindBuffer(GL_ARRAY_BUFFER, rg->normal_buffer.buffer_id); + glBindBuffer(GL_ARRAY_BUFFER, ro->normal_buffer.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]); + glUniformMatrix3fv(rg->shader.normal_matrix_id, 1, GL_FALSE, &normal_matrix[0][0]); } +#endif // draw if (rg->draw_indexed) { - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, rg->index_buffer.buffer_id); - glDrawElements(draw_mode, rg->index_buffer.buffer_len, GL_UNSIGNED_INT, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ro->index_buffer.buffer_id); + glDrawElements(GL_TRIANGLES, ro->index_buffer.buffer_len, GL_UNSIGNED_INT, 0); } else { - glDrawArrays(draw_mode, 0, rg->vertex_buffer.buffer_len / 3); + glDrawArrays(rg->draw_mode, 0, ro->vertex_buffer.buffer_len / 3); } // cleanup glDisableVertexAttribArray(0); - if (rg->color_buffer.buffer) + if (ro->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->normal_buffer.buffer); - utilSafeFree(rg->index_buffer.buffer); - utilSafeFree(rg->color_buffer.buffer); - utilSafeFree(rg); -} - diff --git a/src/render_group.h b/src/render_group.h index 9f6b5a5..8c67568 100644 --- a/src/render_group.h +++ b/src/render_group.h @@ -1,6 +1,13 @@ +#pragma once + +#include + +#include "entity.h" +//#include "hexgame.h" #include "util.h" -#include "hexgame.h" + +struct Entity; // TODO: can these structs be used as opaque pointers? @@ -18,39 +25,44 @@ struct gl_index_buffer uint* buffer = nullptr; }; -struct gl_render_group +struct render_object { gl_buffer vertex_buffer; gl_buffer normal_buffer; gl_buffer color_buffer; gl_index_buffer index_buffer; +}; + +struct rg_shader_program +{ GLuint program_id = 0; GLuint model_matrix_id = 0; GLuint view_matrix_id = 0; GLuint projection_matrix_id = 0; GLuint normal_matrix_id = 0; GLuint vertex_array_id = 0; +}; + +struct render_group +{ + uint num_objects = 0; + render_object** render_objects = nullptr; + rg_shader_program shader; bool use_normals = false; bool draw_indexed = false; + GLenum draw_mode = GL_TRIANGLES; }; -typedef struct gl_buffer gl_buffer; -typedef struct gl_index_buffer gl_index_buffer; -typedef struct gl_render_group gl_render_group; - -bool rgInitShaderProgram(gl_render_group* rg, const char * vertex_code, const char * frag_code); - -gl_buffer * rgInitGLBuffer(gl_buffer* buffer, uint buffer_len); - -void rgSendBufferToGL(gl_buffer* buffer, GLenum usage, GLenum target); +bool rgInitShaderProgram(rg_shader_program& sp, const char * vertex_code, const char * frag_code); -bool rgInitEntity(gl_render_group* rg, Entity* e); +bool rgInitEntity(Entity* e); -void rgFillColorBuffer(GLfloat buf[], int len, std::vector* hexes); +// TODO: fix this function to use just buffers instead of hexes +//void rgFillColorBuffer(GLfloat buf[], int len, std::vector* hexes); -void rgDraw(gl_render_group* rg, GLenum draw_mode, glm::mat4 model_matrix, +void rgDraw(render_group* rg, glm::mat4 model_matrix, glm::mat4 view_matrix, glm::mat4 projection_matrix, glm::vec3 light_position, GLuint light_id, bool update_vertex_data = false); -void rgFree(gl_render_group* rg); +void rgFree(render_group* rg); diff --git a/src/renderer.cpp b/src/renderer.cpp index 49f74a5..fa85773 100644 --- a/src/renderer.cpp +++ b/src/renderer.cpp @@ -83,16 +83,16 @@ struct camera }; -gl_matrix_info g_scene_matrices; -gl_render_group* g_filled_hex_render_group; -gl_render_group* g_hex_line_render_group; -gl_render_group* g_debug_render_group; -gl_render_group* g_entity_render_group; -camera g_camera; - +// globals +static gl_matrix_info g_scene_matrices; +static render_group g_filled_hex_render_group; +static render_group g_hex_line_render_group; +static render_group g_debug_render_group; +// NOTE: entity render_group references are kept in the entity struct +static camera g_camera; // TODO: testing lighting -renPointLight g_test_light; +static renPointLight g_test_light; bool @@ -158,6 +158,7 @@ initMatrices(projection_type p) g_camera.position = glm::vec3(640,0,100); g_camera.target = glm::vec3(640,500,0); + //g_camera.target = glm::vec3(0,0,0); // inital rotation should match target direction glm::vec3 &p = g_camera.position; glm::vec3 &t = g_camera.target; @@ -274,29 +275,26 @@ initRenderer(SDL_Handles &handles, v2i vpDims) // hide VRAM debug messages glDebugMessageControl(GL_DONT_CARE, 33361, GL_DONT_CARE, 0, 0, GL_FALSE); - g_filled_hex_render_group = (gl_render_group *) std::calloc(1, sizeof(gl_render_group)); - g_hex_line_render_group = (gl_render_group *) std::calloc(1, sizeof(gl_render_group)); - g_debug_render_group = (gl_render_group *) std::calloc(1, sizeof(gl_render_group)); - g_entity_render_group = (gl_render_group *) std::calloc(1, sizeof(gl_render_group)); - - if (!g_filled_hex_render_group || !g_hex_line_render_group - || !g_debug_render_group || !g_entity_render_group) - { - return false; - } - +#if 0 const char* vs_code = utilDumpTextFile(DEFAULT_VERTEX_SHADER_FILE); const char* fs_code = utilDumpTextFile(DEFAULT_FRAGMENT_SHADER_FILE); - if (!rgInitShaderProgram(g_filled_hex_render_group, vs_code, fs_code) - || !rgInitShaderProgram(g_hex_line_render_group, vs_code, LINE_FRAGMENT_SHADER_CODE) - || !rgInitShaderProgram(g_debug_render_group, vs_code, DEBUG_FRAGMENT_SHADER_CODE) - || !rgInitShaderProgram(g_entity_render_group, vs_code, fs_code)) - { + bool shader_error = (!rgInitShaderProgram(&g_filled_hex_render_group, vs_code, fs_code) + || !rgInitShaderProgram(&g_hex_line_render_group, vs_code, LINE_FRAGMENT_SHADER_CODE) + || !rgInitShaderProgram(&g_debug_render_group, vs_code, DEBUG_FRAGMENT_SHADER_CODE)); + + utilSafeFree(vs_code); + utilSafeFree(fs_code); + + if (shader_error) { + LOG(ERROR) << "Error initializing shader program\n"; return false; + } else { + return true; } - +#else return true; +#endif } void @@ -370,16 +368,17 @@ fillHexLineBuffer(GLfloat buf[], int len, std::vector* hexes) bool initHexGridBuffers(std::vector* hexes) { +#if 0 // TODO: index duplicate vertices // 6 triangles * 3 vertices per triangle * 3 floats per vertex = 54 uint vbuf_len = hexes->size() * 6 * 3 * 3; uint line_vertices_per_hex = 6 * 2; // 12 vertices since we're using line segments atm uint line_buf_len = hexes->size() * line_vertices_per_hex * 3; // 3 floats per vertex - gl_buffer& vbuf = * rgInitGLBuffer(&g_filled_hex_render_group->vertex_buffer, vbuf_len); - gl_buffer& cbuf = * rgInitGLBuffer(&g_filled_hex_render_group->color_buffer, vbuf_len); - gl_buffer& normal_buf = * rgInitGLBuffer(&g_filled_hex_render_group->normal_buffer, vbuf_len); - gl_buffer& line_buf = * rgInitGLBuffer(&g_hex_line_render_group->vertex_buffer, line_buf_len); + gl_buffer& vbuf = * rgInitGLBuffer(&g_filled_hex_render_group.vertex_buffer, vbuf_len); + gl_buffer& cbuf = * rgInitGLBuffer(&g_filled_hex_render_group.color_buffer, vbuf_len); + gl_buffer& normal_buf = * rgInitGLBuffer(&g_filled_hex_render_group.normal_buffer, vbuf_len); + gl_buffer& line_buf = * rgInitGLBuffer(&g_hex_line_render_group.vertex_buffer, line_buf_len); if (!vbuf.buffer || !cbuf.buffer || !line_buf.buffer || !normal_buf.buffer) return false; @@ -411,6 +410,7 @@ initHexGridBuffers(std::vector* hexes) rgSendBufferToGL(&line_buf, GL_STATIC_DRAW, GL_ARRAY_BUFFER); // TODO: add a color buffer to hex_line and debug render groups to re-use // fragment shaders and simplify draw rgDraw() +#endif return true; } @@ -422,30 +422,53 @@ createScene(std::vector* hexes, Entity* entities, uint32 entity_count) if (!initHexGridBuffers(hexes)) return false; - // debug draw vertexes - + // debug draw vertices +#if 0 rgInitGLBuffer(&g_debug_render_group->vertex_buffer, 4 * 3); // 4 vertices * 3 floats per vertex if (!g_debug_render_group->vertex_buffer.buffer) return false; rgSendBufferToGL(&g_debug_render_group->vertex_buffer, GL_DYNAMIC_DRAW, GL_ARRAY_BUFFER); - +#endif // entities // TODO: wtf, I only accounted for 1 entity in the entity render group :( // probably need one render_group struct for each entity - for (uint i = 0; i < entity_count; i++) - rgInitEntity(g_entity_render_group, &entities[i]); +#if 1 + const char* vs_code = utilDumpTextFile(DEFAULT_VERTEX_SHADER_FILE); + const char* fs_code = utilDumpTextFile(DEFAULT_FRAGMENT_SHADER_FILE); + rg_shader_program sp; + + bool shader_errors = !rgInitShaderProgram(sp, vs_code, fs_code); + utilSafeFree(vs_code); + utilSafeFree(fs_code); + + if (shader_errors) + return false; + + for (uint i = 0; i < entity_count; i++) { + rgInitEntity(&entities[i]); + entities[i].ren_group->shader = sp; + } +#else + if (!rgInitEntity(&entities[0])) { + LOG(ERROR) << "Error initialzing entity\n"; + return false; + } +#endif // lights // TODO: load light properties from scene/level files - g_test_light.light_ID = glGetUniformLocation(g_entity_render_group->program_id, "light_position"); - g_test_light.position = glm::vec3(640, 500, 400); // above center of hexgrid - g_test_light.direction = glm::vec3(0, 0, 0) - g_test_light.position; // back towards test entity - g_test_light.color = glm::vec3(1.f, 1.f, 1.f); - g_test_light.intensity = 1.f; + //g_test_light.light_ID = glGetUniformLocation(g_entity_render_group.program_id, "light_position"); + if (entities[0].ren_group != nullptr) { + g_test_light.light_ID = glGetUniformLocation(entities[0].ren_group->shader.program_id, "light_position"); + g_test_light.position = glm::vec3(640, 500, 400); // above center of hexgrid + g_test_light.direction = glm::vec3(0, 0, 0) - g_test_light.position; // back towards test entity + g_test_light.color = glm::vec3(1.f, 1.f, 1.f); + g_test_light.intensity = 1.f; + } return true; } @@ -541,12 +564,13 @@ renderFrame(std::vector *hexes, Entity* entities, uint32 entity_count) glClearColor(g_clear_col.R, g_clear_col.G, g_clear_col.B, g_clear_col.A); glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); - glm::mat4 m_model = g_scene_matrices.model; + //glm::mat4 m_model = g_scene_matrices.model; glm::mat4 m_view = g_scene_matrices.view; glm::mat4 m_projection = g_scene_matrices.projection; // filled hexes +#if 0 // get new colors every frame gl_render_group* rg = g_filled_hex_render_group; rgFillColorBuffer(rg->color_buffer.buffer, rg->color_buffer.buffer_len, hexes); @@ -557,14 +581,16 @@ renderFrame(std::vector *hexes, Entity* entities, uint32 entity_count) rgDraw(g_hex_line_render_group, GL_LINES, m_model, m_view, m_projection, g_test_light.position, g_test_light.light_ID); +#endif - // TODO: update and send array of lights (pos, dir, color, intesity to shaders - // every frame through rgDrawIndexed() // entities for (uint i = 0; i < entity_count; i++) { + + // TODO: find a way to store model xform on the meMeshGroup object + glm::mat4 model_transform = entities[i].mesh_group.meshes[0]->model_transform; + rgDraw( - g_entity_render_group, GL_TRIANGLES, - entities[i].mesh->model_transform, m_view, m_projection, + entities[i].ren_group, model_transform, m_view, m_projection, g_test_light.position, g_test_light.light_ID ); } @@ -573,8 +599,9 @@ renderFrame(std::vector *hexes, Entity* entities, uint32 entity_count) void renderDebug(std::vector &vertices) { +#if 0 // TODO: indexed line drawing - // copy vertexes to render group + // copy vertices to render group GLfloat* buf = g_debug_render_group->vertex_buffer.buffer; buf[0] = vertices[0].x; buf[1] = vertices[0].y; buf[2] = 0; buf[3] = vertices[1].x; buf[4] = vertices[1].y; buf[2] = 0; @@ -583,19 +610,22 @@ renderDebug(std::vector &vertices) rgDraw(g_debug_render_group, GL_LINE_LOOP, g_scene_matrices.model, g_scene_matrices.view, g_scene_matrices.projection, g_test_light.position, g_test_light.light_ID, true); +#endif } void freeBuffers() { - std::vector groups = { + LOG(DEBUG) << "redo freeBuffers function after render_group update\n"; +#if 0 + std::vector groups = { g_filled_hex_render_group, g_hex_line_render_group, g_debug_render_group, - g_entity_render_group }; - for (gl_render_group* group : groups) + for (render_group group : groups) rgFree(group); +#endif } diff --git a/src/util.cpp b/src/util.cpp index eebd4cd..27c8fb0 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -7,6 +7,7 @@ #include "util.h" const uint MAX_FILESIZE = 2 * 1024 * 1024; // 2MB +const uint MAX_STRING_LENGTH = 1024; char * utilDumpTextFile(const char* filename) @@ -30,6 +31,19 @@ utilDumpTextFile(const char* filename) return buf; } +void * +utilLogAlloc(uint item_count, uint type_size, const char* file_name, const int line) +{ + void* mem = std::calloc(item_count, type_size); + + if (mem == nullptr) { + LOG(ERROR) << "Memory allocation failed, called from " + << file_name << ":" << line; + } + + return mem; +} + // TODO: search/replace other calls to std::free with this void utilSafeFree(void* mem) @@ -40,3 +54,12 @@ utilSafeFree(void* mem) } } +// TODO: re-use non-const version, macro or cast away const +void utilSafeFree(const void* mem) +{ + if (mem != nullptr) { + std::free((void *) mem); + mem = nullptr; + } +} + diff --git a/src/util.h b/src/util.h index 96b57fd..f29797b 100644 --- a/src/util.h +++ b/src/util.h @@ -74,7 +74,11 @@ utilConvertColor(GLfloat buf[3], uint32 color) buf[2] = (GLfloat) ((color >> 8) & 0xFF) / (GLfloat) 255; } -char * utilDumpTextFile(const char* filename); +// NOTE: Wrapper for calloc that will send error message on out of memory +void * utilLogAlloc(uint item_count, uint type_size, const char* file_name, const int line); +#define UTIL_ALLOC(len, type) (type *) utilLogAlloc((len), sizeof(type), __FILE__, __LINE__) +char * utilDumpTextFile(const char* filename); void utilSafeFree(void* mem); +void utilSafeFree(const void* mem);