diff --git a/orgfile.org b/orgfile.org index aa739b5..4e1af20 100644 --- a/orgfile.org +++ b/orgfile.org @@ -36,8 +36,10 @@ while using as few libraries as possible. - State "DONE" from "TODO" [2015-08-02 Sun 19:09] ** TODO Fix readme markdown ** TODO Framebuffer and resolution independent rendering -** TODO Entity +** DONE Entity +- State "DONE" from "TODO" [2015-09-15 Tue 12:17] ** TODO Positive and negative values for input_maps and returning corresponding values when they are true +** TODO Textures ** DONE Camera - State "DONE" from "TODO" [2015-08-19 Wed 13:30] ** DONE Test render diff --git a/src/game.c b/src/game.c index 0ce43c0..6cc408c 100644 --- a/src/game.c +++ b/src/game.c @@ -18,11 +18,13 @@ #include "model.h" #include "scene.h" #include "utils.h" +#include "texture.h" void run(void); void update(float dt); void render(void); void debug(float dt); +void scene_setup(void); int player_node = -1; int player_pitch_node = -1; @@ -35,14 +37,22 @@ void game_init(void) renderer_init(window); io_file_init("/mnt/Dev/Projects/Symmetry/assets/");/* TODO: Implement proper way of getting binary directory */ shader_init(); + texture_init(); transform_init(); camera_init(); geom_init(); model_init(); entity_init(); scene_init(); - - + + /* Debug scene setup */ + scene_setup(); + + run(); +} + +void scene_setup(void) +{ int forward_keys[2] = {'W', GLFW_KEY_UP}; int backward_keys[2] = {'S', GLFW_KEY_DOWN}; int up_keys[2] = {'Q'}; @@ -63,8 +73,8 @@ void game_init(void) input_map_create("Turn_Left", turn_left_keys, 1); input_map_create("Turn_Up", turn_up_keys, 1); input_map_create("Turn_Down", turn_down_keys, 1); - - shader_create("unshaded.vert", "unshaded.frag"); + + shader_create("unshaded_textured.vert", "unshaded_textured.frag"); struct Entity* player = scene_add_new("player", "None"); player_node = player->node; vec3 viewer_pos = {0, 0, 10}; @@ -96,7 +106,7 @@ void game_init(void) transform_set_position(ground_tran, &pos); transform_scale(ground_tran, &scale_ground); - run(); + texture_create_from_file("test.tga"); } void debug(float dt) @@ -258,5 +268,6 @@ void game_cleanup(void) input_cleanup(); renderer_cleanup(); io_file_cleanup(); + texture_cleanup(); shader_cleanup(); } diff --git a/src/model.c b/src/model.c index 7b8ee6e..4f0762c 100644 --- a/src/model.c +++ b/src/model.c @@ -6,12 +6,16 @@ #include "entity.h" #include "shader.h" #include "transform.h" +#include "texture.h" +#include "renderer.h" + +#include "GL/glew.h" +#include "GLFW/glfw3.h" #include static struct Model* model_list; static int* empty_indices; - struct Model* model_get(int index) { struct Model* model = NULL; @@ -89,6 +93,7 @@ void model_cleanup(void) void model_render_all(struct Camera* camera) { + int texture = texture_find("test.tga"); mat4 mvp; for(int i = 0; i < array_len(model_list); i++) { @@ -98,10 +103,14 @@ void model_render_all(struct Camera* camera) mat4_identity(&mvp); shader_bind(model->shader); + /* shader_set_uniform_int(model->shader, "sampler", (GL_TEXTURE0 + 4) - GL_TEXTURE0); */ + texture_bind(texture, 4); + renderer_check_glerror("model:render_all"); mat4_mul(&mvp, &camera->view_proj_mat, &transform->trans_mat); shader_set_uniform_mat4(model->shader, "mvp", &mvp); - shader_set_uniform_vec4(model->shader, "color", &model->color); + shader_set_uniform_vec4(model->shader, "diffuseColor", &model->color); geom_render(model->geometry_index); + texture_unbind(4); shader_unbind(); } } diff --git a/src/renderer.c b/src/renderer.c index e28d91d..461bac9 100644 --- a/src/renderer.c +++ b/src/renderer.c @@ -11,6 +11,7 @@ void renderer_init(GLFWwindow* window) { glClearColor(0.3f, 0.6f, 0.9f, 1.0f); glEnable(GL_DEPTH_TEST); + glEnable(GL_TEXTURE_2D); glfwSetFramebufferSizeCallback(window, on_framebuffer_size_change); } diff --git a/src/shader.c b/src/shader.c index c9bdfd4..2aa002a 100644 --- a/src/shader.c +++ b/src/shader.c @@ -14,21 +14,13 @@ #include "GL/glew.h" #include "GLFW/glfw3.h" -struct Shader_Object -{ - unsigned int vertex_shader; - unsigned int fragment_shader; - unsigned int program; -}; -typedef struct Shader_Object Shader_Object; - // Constants for locations of attributes inside all shaders const int POSITION_LOC = 0; const int NORMAL_LOC = 1; const int UV_LOC = 2; const int COLOR_LOC = 3; -static struct Shader_Object* shader_list; +static uint* shader_list; static int* empty_indices; void debug_print_shader(const char* shaderText) @@ -91,7 +83,7 @@ char* run_preprocessor(char* shader_text) void shader_init(void) { - shader_list = array_new(Shader_Object); + shader_list = array_new(uint); empty_indices = array_new(int); } @@ -198,25 +190,27 @@ int shader_create(const char* vert_shader_name, const char* frag_shader_name) return -1; } + /* Safe to delete shaders now */ + glDeleteShader(vert_shader); + glDeleteShader(frag_shader); + /* add new object or overwrite existing one */ - Shader_Object* new_object = NULL; + uint* new_shader = 0; int index = -1; int empty_len = array_len(empty_indices); if(empty_len != 0) { index = empty_indices[empty_len - 1]; array_pop(empty_indices); - new_object = &shader_list[index]; + new_shader = &shader_list[index]; } else { - new_object = array_grow(shader_list, struct Shader_Object); + new_shader = array_grow(shader_list, uint); index = array_len(shader_list) - 1; } - assert(new_object); - new_object->vertex_shader = vert_shader; - new_object->fragment_shader = frag_shader; - new_object->program = program; + assert(new_shader); + *new_shader = program; log_message("%s, %s compiled into shader program", vert_shader_name, frag_shader_name); free(vs_path); @@ -227,7 +221,7 @@ int shader_create(const char* vert_shader_name, const char* frag_shader_name) void shader_bind(const int shader_index) { - glUseProgram(shader_list[shader_index].program); + glUseProgram(shader_list[shader_index]); } void shader_unbind(void) @@ -237,7 +231,7 @@ void shader_unbind(void) int shader_get_uniform_location(const int shader_index, const char* name) { - GLint handle = glGetUniformLocation(shader_list[shader_index].program, name); + GLint handle = glGetUniformLocation(shader_list[shader_index], name); if(handle == -1) log_error("shader:get_uniform_location", "Invalid uniform %s", name); @@ -288,11 +282,13 @@ void shader_set_uniform_mat4(const int shader_index, const char* name, const ma void shader_remove(const int shader_index) { - Shader_Object* shader_object = &shader_list[shader_index]; - glDeleteProgram(shader_object->program); - glDeleteShader(shader_object->vertex_shader); - glDeleteShader(shader_object->fragment_shader); - shader_object->fragment_shader = shader_object->vertex_shader = shader_object->program = -1; + uint shader = shader_list[shader_index]; + int curr_program = 0; + glGetIntegerv(GL_CURRENT_PROGRAM, &curr_program); + if((uint)curr_program == shader) + glUseProgram(0); + glDeleteProgram(shader); + shader_list[shader_index] = 0; array_push(empty_indices, shader_index, int); } diff --git a/src/texture.c b/src/texture.c new file mode 100644 index 0000000..d4e4cbe --- /dev/null +++ b/src/texture.c @@ -0,0 +1,318 @@ +#include "texture.h" +#include "array.h" +#include "file_io.h" +#include "string_utils.h" +#include "log.h" +#include "num_types.h" +#include "renderer.h" + +#include +#include +#include +#include "GL/glew.h" +#include "GLFW/glfw3.h" + +struct Texture +{ + char* name; + uint handle; + int ref_count; +}; + +#pragma pack(push, 1) +struct Tga_Header +{ + char idlength; + char colourmaptype; + char datatypecode; + short colourmaporigin; + short colourmaplength; + char colourmapdepth; + short x_origin; + short y_origin; + short width; + short height; + char bitsperpixel; + char imagedescriptor; +}; +#pragma pack(pop) + +static struct Texture* texture_list; +static int* empty_indices; + +int load_img(FILE* file, GLubyte** image_data, int* width, int* height, int* fmt, int* internal_fmt); +void texture_debug_write_tga(struct Tga_Header* header, GLubyte* image_data); + +void texture_init(void) +{ + texture_list = array_new(struct Texture); + empty_indices = array_new(int); +} + +int texture_create_from_file(const char* filename) +{ + assert(filename); + int index = -1; + uint handle = 0; + char* full_path = str_new("textures/"); + full_path = str_concat(full_path, filename); + FILE* file = io_file_open(full_path, "rb"); + int img_load_success = -1; + + if(file) + { + /* Load texture here */ + int width, height, internal_fmt, fmt; + GLubyte* img_data = NULL; + width = height = internal_fmt = fmt = -1; + img_load_success = load_img(file, &img_data, &width, &height, &fmt, &internal_fmt); + + if(img_load_success) + { + struct Texture* new_texture = NULL; + if(array_len(empty_indices) > 0) + { + index = *array_get_last(empty_indices, int); + array_pop(empty_indices); + new_texture = &texture_list[index]; + } + else + { + new_texture = array_grow(texture_list, struct Texture); + index = array_len(texture_list) - 1; + } + assert(new_texture); + + log_message("\nWidth : %d\nHeight : %d\nFormat : %s", + width, height, fmt == GL_RGB ? "RGB" : "RGBA"); + glGenTextures(1, &handle); + if(new_texture->name) free(new_texture->name); + new_texture->name = str_new(filename); + new_texture->ref_count = 1; + new_texture->handle = handle; + glBindTexture(GL_TEXTURE_2D, handle); + texture_param_set(index, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + texture_param_set(index, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + texture_param_set(index,GL_TEXTURE_WRAP_S,GL_REPEAT); + texture_param_set(index,GL_TEXTURE_WRAP_T,GL_REPEAT); + glTexImage2D(GL_TEXTURE_2D, 0, internal_fmt, width, height, 0, fmt, GL_UNSIGNED_BYTE, img_data); + renderer_check_glerror("texture:create"); + glBindTexture(GL_TEXTURE_2D, 0); + free(img_data); + } + fclose(file); + } + else + { + log_error("texture:create_from_file", "Could not open file %s", filename); + } + free(full_path); + return index; +} + +void texture_remove(int index) +{ + if(index > -1 && index < array_len(texture_list)) + { + struct Texture* texture = &texture_list[index]; + if(texture->ref_count >= 0) + { + texture->ref_count--; + if(texture->ref_count < 0) + { + glDeleteTextures(1, &texture->handle); + if(texture->name) free(texture->name); + texture->ref_count = -1; + array_push(empty_indices, index, int); + } + } + } +} + +int texture_find(const char* name) +{ + assert(name); + int index = -1; + for(int i = 0; i < array_len(texture_list); i++) + { + struct Texture* texture = &texture_list[i]; + if(texture->name && strcmp(texture->name, name) == 0) + { + index = i; + break; + } + } + return index; +} + +void texture_cleanup(void) +{ + for(int i = 0; i < array_len(texture_list); i++) + texture_remove(i); + + array_free(texture_list); + array_free(empty_indices); +} + +void texture_bind(int index, int texture_unit) +{ + assert(index > -1 && index < array_len(texture_list)); + //glActiveTexture(GL_TEXTURE0 + texture_unit); + glBindTexture(GL_TEXTURE_2D, texture_list[index].handle); +} + +void texture_unbind(int texture_unit) +{ + //glActiveTexture(GL_TEXTURE0 + texture_unit); + glBindTexture(GL_TEXTURE_2D, 0); +} + +int load_img(FILE* file, GLubyte** image_data, int* width, int* height, int* fmt, int* internal_fmt) +{ + int success = 0; + struct Tga_Header header; + size_t items_read = fread(&header, sizeof(struct Tga_Header), 1, file); + + if(items_read == 1) + { + /* log_message("sizeof struct : %d", sizeof(struct Tga_Header)); */ + /* log_message("%d\n%d\n%d\n%d\n%d\n%d\n%d\n%d\n%d\n%d\n%d\n%d\n", */ + /* header.idlength, */ + /* header.colourmaptype, */ + /* header.datatypecode, */ + /* header.colourmaporigin, */ + /* header.colourmaplength, */ + /* header.colourmapdepth, */ + /* header.x_origin, */ + /* header.y_origin, */ + /* header.width, */ + /* header.height, */ + /* header.bitsperpixel, */ + /* header.imagedescriptor); */ + if(header.datatypecode == 0) + { + log_error("texture:load_img", "No image data in file"); + success = 0; + } + else + { + /* only compressed and uncompressed true color image data supported yet */ + if(header.datatypecode != 2 && header.datatypecode != 10) + { + log_error("texture:load_img", "Unsupported image data type"); + success = 0; + return success; + } + + if(header.bitsperpixel != 24 && header.bitsperpixel != 32) + { + log_error("texture:load_img", + "Unsupported bitsperpixel size(%d), only 24 and 32 supported", header.bitsperpixel); + success = 0; + return success; + } + + if(header.width <= 0 || header.height <= 0) + { + log_error("texture:load_img", "Invalid width and height (%d:%d)", header.width, header.height); + success = 0; + return success; + } + + size_t bytes_per_pixel = header.bitsperpixel / 8; + size_t image_size = (bytes_per_pixel * header.width * header.height); + *image_data = malloc(image_size); + if(!*image_data) + { + log_error("texture:load_img", "Out of memory"); + success = 0; + return success; + } + + /* skip over unnecessary things like colormap data */ + int skipover = 0; + skipover += header.idlength; + skipover += header.colourmaptype * header.colourmaplength; + fseek(file, skipover, SEEK_CUR); + + /* Start reading pixel by pixel */ + GLubyte pixel[bytes_per_pixel]; + GLubyte* curr_pixel = *image_data; + for(int i = 0; i < header.width * header.height; i++) + { + if(header.datatypecode == 2) /* uncompressed image data */ + { + if(fread(&pixel, 1, bytes_per_pixel, file) != bytes_per_pixel) + { + success = 0; + log_error("texture:load_img", "Unexpected end of file at pixel %d", i); + free(image_data); + return success; + } + + /* Swap BGR to RGB */ + curr_pixel[0] = pixel[2]; + curr_pixel[1] = pixel[1]; + curr_pixel[2] = pixel[0]; + if(bytes_per_pixel == 4) curr_pixel[3] = pixel[3]; + + curr_pixel += bytes_per_pixel; + } + else if(header.datatypecode == 10) /* compressed image data */ + { + log_message("Not implemented yet!"); + } + } + + texture_debug_write_tga(&header, *image_data); + *height = header.height; + *width = header.width; + *fmt = bytes_per_pixel == 3 ? GL_RGB : GL_RGBA; + *internal_fmt = *fmt; + success = 1; + } + } + else + { + log_error("texture:load_img", "Could not read header"); + success = 0; + } + + return success; +} + +void texture_param_set(int index, int parameter, int value) +{ + struct Texture* texture = NULL; + if(index > -1 && index < array_len(texture_list)) + texture = &texture_list[index]; + else + return; + + GLint curr_texture = 0; + glGetIntegerv(GL_TEXTURE_2D, &curr_texture); + glBindTexture(GL_TEXTURE_2D, texture->handle); + glTexParameteri(GL_TEXTURE_2D, parameter, value); + renderer_check_glerror("texture:param_set"); + if(curr_texture != 0) + glBindTexture(GL_TEXTURE_2D, curr_texture); +} + +void texture_debug_write_tga(struct Tga_Header* header, GLubyte* image_data) +{ + /* Debug only, write the loaded image to file */ + FILE* fptr = NULL; + if ((fptr = fopen("tga_debug.tga","w")) == NULL) { + fprintf(stderr,"Failed to open outputfile\n"); + exit(-1); + } + fwrite(header, sizeof(struct Tga_Header), 1, fptr); + for (int i = 0; i < header->height * header->width; i++) + { + putc(image_data[2], fptr); + putc(image_data[1], fptr); + putc(image_data[0], fptr); + image_data += 3; + } + fclose(fptr); +} diff --git a/src/texture.h b/src/texture.h new file mode 100644 index 0000000..c70e96b --- /dev/null +++ b/src/texture.h @@ -0,0 +1,13 @@ +#ifndef texture_H +#define texture_H + +void texture_init(void); +int texture_create_from_file(const char* filename); +void texture_remove(int index); +int texture_find(const char* name); +void texture_cleanup(void); +void texture_bind(int index, int texture_unit); +void texture_unbind(int texture_unit); +void texture_param_set(int index, int parameter, int value); + +#endif