#ifndef SRENDER_H #define SRENDER_H #ifdef __cplusplus extern "C" { #endif #include #include "vectorial/simd4x4f.h" typedef struct srndr srndr; typedef struct srview srview; typedef struct srcmdbuf srcmdbuf; typedef enum { SRndrCmdTypeFrame = 0, SRndrCmdTypeGrid, SRndrCmdTypeBox, SRndrCmdTypeLight, SRndrCmdTypeSphere } SRndrCmdType; typedef struct srcmd { simd4x4f mat; simd4f color; SRndrCmdType type; } srcmd; // // Renderer // srndr* srndr_create(); void srndr_destroy(srndr* sr); // // View // srview* srview_create(); void srview_destroy(srview* sv); void srview_set_proj(srview* sv, simd4x4f proj); void srview_set_view(srview* sv, simd4x4f view); void srview_set_size(srview* sv, int width, int height); void srview_get_output_texture(srview* sv, GLuint* texture); // // Command Buffer and Commands // void srcmd_clear (srcmd *cmd); srcmdbuf* srcmdbuf_create(unsigned int size_max); void srcmdbuf_clear(srcmdbuf* cmdbuf); bool srcmdbuf_add(srcmdbuf* cmdbuf, srcmd *cmd); srcmd* srcmd_create(srcmdbuf* cmdbuf); void srndr_render(srndr* srndr, srview* sview, srcmdbuf* scmdbuf); // // srender Implementation // #ifdef SRENDER_IMPLEMENTATION #include "utils.h" #include #include typedef struct srndr { } srndr; typedef struct srview { GLuint width; GLuint height; GLuint mFrameBufferId; GLuint mColorTexture; GLuint mDepthTexture; simd4x4f proj; simd4x4f view; } srview; typedef struct srcmdbuf { int nbufsize; int ncmds; srcmd* cmds; } srcmdbuf; // // Vertex Data // typedef union srvrtxdata { struct { float x, y, z, w; float nx, ny, nz; float s, t; GLubyte r, g, b, a; }; struct { float pos[4]; float n[4]; float uv[2]; GLubyte color[4]; }; } srvrtxdata; // // Vertex Buffer Object for all debug objects (coord frame, grid, cube, ...) // const GLint DEBUG_VBO_SIZE = 1024; GLuint gDebugShapesVBOId = 0; GLuint gDebugShapesVAId = 0; // // Simple Mesh Data // typedef struct srmeshdata { int nvertices; int nindices; GLuint vao_id; GLuint vb_id; GLuint idb_id; GLuint count; GLenum mode; } srmeshdata; static srmeshdata gCoordFrameMesh = {0}; static srmeshdata gGridMesh = {0}; typedef struct srshader { const char* vert_fname; const char* frag_fname; GLuint program_id; GLuint vert_shader_id; GLuint frag_shader_id; } srshader; static srshader gDefaultShader = {"default_vert.glsl", "default_frag.glsl", 0, 0, 0}; bool srshader_compile(GLuint shader_id, const char* shader_src) { glShaderSource(shader_id, 1, &shader_src, 0); glCompileShader(shader_id); // Check Vertex Shader GLint result = GL_FALSE; int log_length = 0; glGetShaderiv(shader_id, GL_COMPILE_STATUS, &result); glGetShaderiv(shader_id, GL_INFO_LOG_LENGTH, &log_length); if (log_length > 0) { char* err_msg = calloc(log_length, sizeof(char)); assert(err_msg); glGetShaderInfoLog(shader_id, log_length, NULL, err_msg); gLog("%s", err_msg); free(err_msg); return false; } return true; } bool srshader_load( srshader* shader, const char* vert_src, const char* frag_src) { shader->vert_shader_id = glCreateShader(GL_VERTEX_SHADER); if (!srshader_compile(shader->vert_shader_id, vert_src)) { gLog("Error compiling vertex shader!"); return false; } shader->frag_shader_id = glCreateShader(GL_FRAGMENT_SHADER); if (!srshader_compile(shader->frag_shader_id, frag_src)) { gLog("Error compiling fragment shader!"); return false; } shader->program_id = glCreateProgram(); glAttachShader(shader->program_id, shader->vert_shader_id); glAttachShader(shader->program_id, shader->frag_shader_id); // Bind attribute locations glBindAttribLocation(shader->program_id, 0, "inCoord"); glBindAttribLocation(shader->program_id, 1, "inNormal"); glBindAttribLocation(shader->program_id, 2, "inUV"); glBindAttribLocation(shader->program_id, 3, "inColor"); glLinkProgram(shader->program_id); GLint result = GL_FALSE; int log_length = 0; glGetProgramiv(shader->program_id, GL_LINK_STATUS, &result); glGetProgramiv(shader->program_id, GL_INFO_LOG_LENGTH, &log_length); if (log_length > 0) { char* err_msg = calloc(log_length, sizeof(char)); assert(err_msg); glGetProgramInfoLog(shader->program_id, log_length, NULL, err_msg); gLog("%s", err_msg); free(err_msg); gLog("Error linking shader program!"); return false; } return true; } bool try_read_file(const char* dir, const char* filename, char** buf) { assert(*buf == NULL); int path_len = strlen(dir) + strlen(filename) + 1; char* path_buffer = calloc(path_len, sizeof(char)); snprintf(path_buffer, path_len, "%s%s", dir, filename); long file_length = 0; FILE* file = NULL; file = fopen(path_buffer, "rb"); if (file) { fseek(file, 0, SEEK_END); file_length = ftell(file); fseek(file, 0, SEEK_SET); *buf = calloc(file_length, sizeof(char)); assert(*buf); fread(*buf, 1, file_length, file); fclose(file); return true; } return false; } void init_shaders() { static const char* shader_search_paths[] = { "./data/shaders/", "../data/shaders/", NULL}; bool found = false; int search_index = 0; char* frag_buffer = NULL; char* vert_buffer = NULL; while (shader_search_paths[search_index] != NULL) { const char* dir = shader_search_paths[search_index]; static char path_buf[1024]; long length; if (vert_buffer == NULL) { try_read_file(dir, gDefaultShader.vert_fname, &vert_buffer); } if (frag_buffer == NULL) { try_read_file(dir, gDefaultShader.frag_fname, &frag_buffer); } if ((vert_buffer != NULL) && (frag_buffer != NULL)) { srshader_load(&gDefaultShader, vert_buffer, frag_buffer); free(vert_buffer); vert_buffer = NULL; free(frag_buffer); frag_buffer = NULL; break; } search_index++; } if (gDefaultShader.program_id == 0) { gLog("Error: could not load default shader!"); } } void init_debug_meshes() { assert(gCoordFrameMesh.nvertices == 0); glGenVertexArrays(1, &gDebugShapesVAId); glBindVertexArray(gDebugShapesVAId); glGenBuffers(1, &gDebugShapesVBOId); glBindBuffer(GL_ARRAY_BUFFER, gDebugShapesVBOId); glBufferData(GL_ARRAY_BUFFER, sizeof(union srvrtxdata) * DEBUG_VBO_SIZE, NULL, GL_STATIC_DRAW); GLintptr dbg_vbuf_offset = 0; glEnableVertexAttribArray(0); glVertexAttribPointer( 0, 4, GL_FLOAT, GL_FALSE, (sizeof(srvrtxdata)), (void*)0); glEnableVertexAttribArray(1); glVertexAttribPointer( 1, 3, GL_FLOAT, GL_FALSE, (sizeof(srvrtxdata)), (void*)(sizeof(float) * 4)); // Attribute 2: texture coordinates glEnableVertexAttribArray(2); glVertexAttribPointer( 2, 2, GL_FLOAT, GL_FALSE, (sizeof(srvrtxdata)), (void*)(sizeof(float) * 7)); // Attribute 3: color glEnableVertexAttribArray(3); glVertexAttribPointer( 3, 4, GL_UNSIGNED_BYTE, GL_TRUE, (sizeof(srvrtxdata)), (void*)(sizeof(float) * 9)); // // Coordinate Frame // gCoordFrameMesh.vao_id = gDebugShapesVAId; gCoordFrameMesh.vb_id = gDebugShapesVBOId; gCoordFrameMesh.nvertices = 6; srvrtxdata coord_frame_vertices[] = { {0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 255, 0, 0, 255}, {1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 255, 0, 0, 255}, {0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0, 255, 0, 255}, {0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0, 255, 0, 255}, {0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0, 0, 255, 255}, {0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0, 0, 255, 255}}; GLuint coord_frame_indices[] = {0, 1, 2, 3, 4, 5}; glBufferSubData( GL_ARRAY_BUFFER, dbg_vbuf_offset, sizeof(coord_frame_vertices), coord_frame_vertices); dbg_vbuf_offset = sizeof (coord_frame_vertices); glGenBuffers(1, &gCoordFrameMesh.idb_id); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, gCoordFrameMesh.idb_id); glBufferData( GL_ELEMENT_ARRAY_BUFFER, sizeof(coord_frame_indices), coord_frame_indices, GL_STATIC_DRAW); gCoordFrameMesh.mode = GL_LINES; gCoordFrameMesh.count = 6; // // Coordinate Frame // gGridMesh.vao_id = gDebugShapesVAId; gGridMesh.vb_id = gDebugShapesVBOId; gGridMesh.nvertices = 44; srvrtxdata* grid_vertices = calloc(44, sizeof(srvrtxdata)); srvrtxdata grid_def_vertex = { 0.f, 0.f, 0.f, 1.f, 0.f, 1.f, 0.f, 0.f, 0.f, 255, 255, 255, 255 }; // lines along parallel to x axis for (int i = 0; i <= 10; i++) { memcpy (&grid_vertices[2*i], &grid_def_vertex, sizeof(srvrtxdata)); memcpy (&grid_vertices[2*i + 1], &grid_def_vertex, sizeof(srvrtxdata)); grid_vertices[2 * i].pos[0] = -5.0f + i * 1.0f; grid_vertices[2 * i].pos[2] = 5.0f; grid_vertices[2 * i + 1].pos[0] = -5.0f + i * 1.0f; grid_vertices[2 * i + 1].pos[2] = -5.0f; } // lines parallel to z axis for (int i = 11; i <= 21; i++) { memcpy (&grid_vertices[2*i], &grid_def_vertex, sizeof(srvrtxdata)); memcpy (&grid_vertices[2*i + 1], &grid_def_vertex, sizeof(srvrtxdata)); grid_vertices[2 * i].pos[0] = -5.0f; grid_vertices[2 * i].pos[2] = -5.0f + (i - 11) * 1.0f; grid_vertices[2 * i + 1].pos[0] = 5.0f; grid_vertices[2 * i + 1].pos[2] = -5.0f + (i - 11) * 1.0f; } glBindBuffer(GL_ARRAY_BUFFER, gDebugShapesVBOId); glBufferSubData( GL_ARRAY_BUFFER, dbg_vbuf_offset, sizeof(srvrtxdata) * 44, grid_vertices); dbg_vbuf_offset += sizeof (srvrtxdata) * 44; free(grid_vertices); GLuint grid_indices[44]; for (int i = 0; i < 22; i++) { grid_indices[2 * i] = 2*i + 6; grid_indices[2 * i + 1] = (2*i+1) + 6; } glGenBuffers(1, &gGridMesh.idb_id); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, gGridMesh.idb_id); glBufferData( GL_ELEMENT_ARRAY_BUFFER, sizeof(grid_indices), grid_indices, GL_STATIC_DRAW); gGridMesh.mode = GL_LINES; gGridMesh.count = 44; glBindVertexArray(0); }; // // Renderer // srndr* srndr_create() { srndr* result = calloc(1, sizeof(srndr)); init_shaders(); init_debug_meshes(); return result; } void srndr_destroy(srndr* sr) { free(sr); } // // View // void srview_get_output_texture(srview* sv, GLuint* texture) { *texture = sv->mColorTexture; } void srview_set_proj(srview* sv, simd4x4f proj) { sv->proj = proj; } void srview_set_view(srview* sv, simd4x4f view) { sv->view = view; } void srview_update_framebuffer(srview* sv) { glGenFramebuffers(1, &sv->mFrameBufferId); glBindFramebuffer(GL_FRAMEBUFFER, sv->mFrameBufferId); // Color Texture glGenTextures(1, &sv->mColorTexture); glBindTexture(GL_TEXTURE_2D, sv->mColorTexture); glTexImage2D( GL_TEXTURE_2D, 0, GL_RGB, sv->width, sv->height, 0, GL_RGB, GL_UNSIGNED_BYTE, 0); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glFramebufferTexture2D( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, sv->mColorTexture, 0); // Depth Texture glGenTextures(1, &sv->mDepthTexture); glBindTexture(GL_TEXTURE_2D, sv->mDepthTexture); glTexImage2D( GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, sv->width, sv->height, 0, GL_DEPTH_COMPONENT, GL_FLOAT, 0); // Set parameters so that we can set a shadow2DSampler glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); float border_color[] = {1.0f, 1.0f, 1.0f, 1.0f}; glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, border_color); glFramebufferTexture2D( GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, sv->mDepthTexture, 0); } void srview_cleanup_framebuffer(srview* sv) { if (glIsFramebuffer(sv->mFrameBufferId)) { glDeleteFramebuffers(1, &sv->mFrameBufferId); } if (sv->mColorTexture != -1) { glDeleteTextures(1, &sv->mColorTexture); sv->mColorTexture = -1; } if (sv->mDepthTexture != -1) { glDeleteTextures(1, &sv->mDepthTexture); sv->mDepthTexture = -1; } } void srview_set_size(srview* sv, int width, int height) { if (sv->width == width && sv->height == height) { return; } sv->width = width; sv->height = height; srview_cleanup_framebuffer(sv); srview_update_framebuffer(sv); } srview* srview_create() { srview* result = calloc(1, sizeof(srview)); return result; } void srview_destroy(srview* sv) { srview_cleanup_framebuffer(sv); free(sv); } void srndr_run_frame_command(const srcmd* cmd) { glBindVertexArray(gCoordFrameMesh.vao_id); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, gCoordFrameMesh.idb_id); glDrawElements(gCoordFrameMesh.mode, gCoordFrameMesh.count, GL_UNSIGNED_INT, (void*)0); glBindBuffer(GL_ARRAY_BUFFER, 0); glBindVertexArray(0); } void srndr_run_grid_command(const srcmd* cmd) { glBindVertexArray(gGridMesh.vao_id); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, gGridMesh.idb_id); glDrawElements(gGridMesh.mode, gGridMesh.count, GL_UNSIGNED_INT, (void*)0); glBindBuffer(GL_ARRAY_BUFFER, 0); glBindVertexArray(0); } void srndr_render(srndr* srndr, srview* sview, srcmdbuf* scmdbuf) { glBindFramebuffer(GL_DRAW_FRAMEBUFFER, sview->mFrameBufferId); glViewport(0, 0, sview->width, sview->height); glClearColor(0.1f, 0.1f, 0.1f, 0.1f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glUseProgram(gDefaultShader.program_id); GLint view_mat_loc = glGetUniformLocation(gDefaultShader.program_id, "uViewMatrix"); assert(view_mat_loc != -1); glUniformMatrix4fv(view_mat_loc, 1, GL_FALSE, &sview->view); GLint proj_mat_loc = glGetUniformLocation(gDefaultShader.program_id, "uProjectionMatrix"); assert(proj_mat_loc != -1); glUniformMatrix4fv(proj_mat_loc, 1, GL_FALSE, &sview->proj); GLint model_mat_loc = glGetUniformLocation(gDefaultShader.program_id, "uModelMatrix"); assert(model_mat_loc != -1); for (int i = 0; i < scmdbuf->ncmds; i++) { const srcmd* cmd = &scmdbuf->cmds[i]; glUniformMatrix4fv(model_mat_loc, 1, GL_FALSE, &cmd->mat); switch (cmd->type) { case SRndrCmdTypeFrame: srndr_run_frame_command(cmd); break; case SRndrCmdTypeGrid: srndr_run_grid_command(cmd); break; default: gLog("Invalid command type %d at index %d", cmd->type, i); break; } } glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); } // // Render Commands // void srcmd_clear (srcmd *cmd) { cmd->color = simd4f_create(1.f, 1.f, 1.f, 1.f); cmd->type = SRndrCmdTypeFrame; simd4x4f_identity(&cmd->mat); } srcmdbuf* srcmdbuf_create(unsigned int size_max) { srcmdbuf* result = malloc(sizeof(srcmdbuf)); result->nbufsize = size_max; result->ncmds = 0; result->cmds = calloc(sizeof(srcmd), size_max); return result; } void srcmdbuf_clear(srcmdbuf* cmdbuf) { cmdbuf->ncmds = 0; } bool srcmdbuf_add(srcmdbuf* cmdbuf, srcmd *cmd) { srcmd *buf_cmd = srcmd_create(cmdbuf); if (!buf_cmd) { return false; } *buf_cmd = *cmd; } srcmd* srcmd_create(srcmdbuf* cmdbuf) { if (cmdbuf->ncmds == cmdbuf->nbufsize) { gLog("Warning: number of render commands maxed out!"); return NULL; } return &(cmdbuf->cmds[cmdbuf->ncmds++]); } #endif // SRENDER_IMPLEMENTATION #ifdef __cplusplus } #endif #endif // SRENDER_H