From 7413dec49439032c911c320a169a98efca72ee83 Mon Sep 17 00:00:00 2001 From: Shariq Shah Date: Thu, 5 Dec 2019 20:43:23 +1100 Subject: [PATCH] Implemented reading/writing static meshes & their material properties to file. Implemented viewing/editing static mesh properties in editor --- src/common/hashmap.c | 5 ++- src/game/editor.c | 85 ++++++++++++++++++++++++++++++++++++++++---- src/game/entity.c | 46 +++++++++++++++++++++++- src/game/game.c | 2 +- src/game/model.c | 29 +++++++++------ src/game/model.h | 3 ++ src/game/texture.c | 6 ++++ src/game/texture.h | 1 + todo.txt | 5 ++- 9 files changed, 162 insertions(+), 20 deletions(-) diff --git a/src/common/hashmap.c b/src/common/hashmap.c index ecc73e5..fb0227b 100755 --- a/src/common/hashmap.c +++ b/src/common/hashmap.c @@ -101,7 +101,10 @@ struct Variant* hashmap_value_get(const struct Hashmap* hashmap, const char* key int compare_len = key_len < HASH_MAX_KEY_LEN ? key_len : HASH_MAX_KEY_LEN; for(int i = 0; i < array_len(hashmap->buckets[index]); i++) { - if(strncmp(key, hashmap->buckets[index][i].key, compare_len) == 0) + // Check for the length of the key to avoid partial matches. We might be looking for + // "Diffuse" and we'll get matched to "Diffuse_Color" if we're relying on the length + // of "Diffuse" only + if(strnlen(hashmap->buckets[index][i].key, HASH_MAX_KEY_LEN) == compare_len && strncmp(key, hashmap->buckets[index][i].key, compare_len) == 0) { value = &hashmap->buckets[index][i].value; break; diff --git a/src/game/editor.c b/src/game/editor.c index 2be84d4..e13b05a 100755 --- a/src/game/editor.c +++ b/src/game/editor.c @@ -1783,16 +1783,16 @@ void editor_window_property_inspector(struct nk_context* context, struct Editor* sound_source_instance_volume_set(sound, sound_source->source_instance, volume); nk_layout_row_dynamic(context, 30, 2); - static char filename_buffer[MAX_FILENAME_LEN]; - strncpy(filename_buffer, sound_source->source_buffer->filename, MAX_FILENAME_LEN); + static char sound_source_filename_buffer[MAX_FILENAME_LEN]; + strncpy(sound_source_filename_buffer, sound_source->source_buffer->filename, MAX_FILENAME_LEN); nk_label(context, "File", NK_TEXT_ALIGN_LEFT | NK_TEXT_ALIGN_MIDDLE); int edit_flags = NK_EDIT_GOTO_END_ON_ACTIVATE | NK_EDIT_FIELD | NK_EDIT_SIG_ENTER; - int edit_state = nk_edit_string_zero_terminated(context, edit_flags, filename_buffer, MAX_FILENAME_LEN, NULL); + int edit_state = nk_edit_string_zero_terminated(context, edit_flags, sound_source_filename_buffer, MAX_FILENAME_LEN, NULL); if(edit_state & NK_EDIT_COMMITED) { - if(strncmp(filename_buffer, sound_source->source_buffer->filename, MAX_FILENAME_LEN) != 0) + if(strncmp(sound_source_filename_buffer, sound_source->source_buffer->filename, MAX_FILENAME_LEN) != 0) { - struct Sound_Source_Buffer* new_source_buffer = sound_source_create(sound, filename_buffer, ST_WAV_STREAM); + struct Sound_Source_Buffer* new_source_buffer = sound_source_create(sound, sound_source_filename_buffer, ST_WAV_STREAM); if(new_source_buffer) { sound_source_stop_all(sound, sound_source->source_buffer); @@ -1810,6 +1810,79 @@ void editor_window_property_inspector(struct nk_context* context, struct Editor* } } + + /* Static Mesh */ + if(entity->type == ET_STATIC_MESH) + { + struct Static_Mesh* mesh = (struct Static_Mesh*)entity; + if(nk_tree_push(context, NK_TREE_TAB, "Static Mesh", NK_MAXIMIZED)) + { + nk_layout_row_dynamic(context, 30, 2); + nk_label(context, "Geometry", NK_TEXT_ALIGN_LEFT | NK_TEXT_ALIGN_MIDDLE); + + static char geometry_filename_buffer[MAX_FILENAME_LEN]; + int edit_flags = NK_EDIT_GOTO_END_ON_ACTIVATE | NK_EDIT_FIELD | NK_EDIT_SIG_ENTER; + struct Geometry* geometry = geom_get(mesh->model.geometry_index); + strncpy(geometry_filename_buffer, geometry->filename, MAX_FILENAME_LEN); + int geometry_buffer_edit_state = nk_edit_string_zero_terminated(context, edit_flags, geometry_filename_buffer, MAX_FILENAME_LEN, NULL); + if(geometry_buffer_edit_state & NK_EDIT_COMMITED) + { + if(strncmp(geometry->filename, geometry_filename_buffer, MAX_FILENAME_LEN) != 0) + { + model_geometry_set(&mesh->model, &geometry_filename_buffer); + } + } + + nk_layout_row_dynamic(context, row_height, 2); + nk_label(context, "Material", NK_TEXT_ALIGN_LEFT | NK_TEXT_ALIGN_MIDDLE); + static const char* material_types[] = { "Blinn", "Unshaded" }; + int selected_material = nk_combo(context, material_types, 2, mesh->model.material->type, row_height, nk_vec2(180, 180)); + if(selected_material != mesh->model.material->type) + { + material_unregister_static_mesh(mesh->model.material, mesh); + struct Material* new_material = &game_state_get()->renderer->materials[selected_material]; + if(!material_register_static_mesh(new_material, mesh)) + { + log_error("editor:update", "Failed to register mesh with material"); + } + } + + nk_layout_row_dynamic(context, row_height, 2); + nk_label(context, "Diffuse Color", NK_TEXT_ALIGN_LEFT | NK_TEXT_ALIGN_MIDDLE); + editor_widget_color_combov4(context, &mesh->model.material_params[MMP_DIFFUSE_COL].val_vec4, 200, 300); + + nk_layout_row_dynamic(context, row_height, 1); + mesh->model.material_params[MMP_DIFFUSE].val_float = nk_propertyf(context, "Diffuse", 0.f, mesh->model.material_params[MMP_DIFFUSE].val_float, 10.f, 0.5f, 0.1f); + + nk_layout_row_dynamic(context, 30, 2); + nk_label(context, "Diffuse Texture", NK_TEXT_ALIGN_LEFT | NK_TEXT_ALIGN_MIDDLE); + static char diffuse_tex_filename_buffer[MAX_FILENAME_LEN]; + const char* texture_name = texture_get_name(mesh->model.material_params[MMP_DIFFUSE_TEX].val_int); + strncpy(diffuse_tex_filename_buffer, texture_name, MAX_FILENAME_LEN); + int diffuse_texture_buffer_edit_state = nk_edit_string_zero_terminated(context, edit_flags, diffuse_tex_filename_buffer, MAX_FILENAME_LEN, NULL); + if(diffuse_texture_buffer_edit_state & NK_EDIT_COMMITED) + { + if(strncmp(texture_name, diffuse_tex_filename_buffer, MAX_FILENAME_LEN) != 0) + { + int new_diffuse_texture = texture_create_from_file(&diffuse_tex_filename_buffer, TU_DIFFUSE); + if(new_diffuse_texture != -1) + { + mesh->model.material_params[MMP_DIFFUSE_TEX].val_int = new_diffuse_texture; + } + } + } + + if(mesh->model.material->type == MAT_BLINN) + { + nk_layout_row_dynamic(context, row_height, 1); + mesh->model.material_params[MMP_SPECULAR].val_float = nk_propertyf(context, "Specular", 0.f, mesh->model.material_params[MMP_SPECULAR].val_float, 10.f, 0.5f, 0.1f); + nk_layout_row_dynamic(context, row_height, 1); + mesh->model.material_params[MMP_SPECULAR_STRENGTH].val_float = nk_propertyf(context, "Specular Strength", 0.f, mesh->model.material_params[MMP_SPECULAR_STRENGTH].val_float, 100.f, 0.5f, 0.1f); + } + + nk_tree_pop(context); + } + } } else { @@ -1835,13 +1908,13 @@ void editor_window_renderer_settings(struct nk_context* context, struct Editor* struct Render_Settings* render_settings = &game_state->renderer->settings; if(nk_tree_push(context, NK_TREE_TAB, "Debug", NK_MAXIMIZED)) { - static const char* draw_modes[] = { "Triangles", "Lines", "Points" }; nk_layout_row_dynamic(context, row_height, 2); nk_label(context, "Debug Draw", NK_TEXT_ALIGN_LEFT | NK_TEXT_ALIGN_MIDDLE); render_settings->debug_draw_enabled = nk_check_label(context, "", render_settings->debug_draw_enabled); nk_layout_row_dynamic(context, row_height, 2); nk_label(context, "Debug Draw Mode", NK_TEXT_ALIGN_LEFT | NK_TEXT_ALIGN_MIDDLE); + static const char* draw_modes[] = { "Triangles", "Lines", "Points" }; render_settings->debug_draw_mode = nk_combo(context, draw_modes, 3, render_settings->debug_draw_mode, 20, nk_vec2(180, 100)); nk_layout_row_dynamic(context, row_height, 2); diff --git a/src/game/entity.c b/src/game/entity.c index fab4e4c..a74af91 100755 --- a/src/game/entity.c +++ b/src/game/entity.c @@ -17,6 +17,7 @@ #include "../system/physics.h" #include "scene.h" #include "game.h" +#include "texture.h" #include #include @@ -160,6 +161,24 @@ bool entity_write(struct Entity* entity, struct Parser_Object* object, bool writ struct Static_Mesh* mesh = (struct Static_Mesh*)entity; struct Geometry* geom = geom_get(mesh->model.geometry_index); hashmap_int_set(entity_data, "material", mesh->model.material->type); + + //Set material model params for this particular mesh + struct Model* model = &mesh->model; + switch(model->material->type) + { + case MAT_BLINN: + hashmap_vec4_set(entity_data, "diffuse_color", &model->material_params[MMP_DIFFUSE_COL].val_vec4); + hashmap_str_set(entity_data, "diffuse_texture", texture_get_name(model->material_params[MMP_DIFFUSE_TEX].val_int)); + hashmap_float_set(entity_data, "diffuse", model->material_params[MMP_DIFFUSE].val_float); + hashmap_float_set(entity_data, "specular", model->material_params[MMP_SPECULAR].val_float); + hashmap_float_set(entity_data, "specular_strength", model->material_params[MMP_SPECULAR_STRENGTH].val_float); + break; + case MAT_UNSHADED: + hashmap_vec3_set(entity_data, "diffuse_color", &mesh->model.material_params[MMP_DIFFUSE_COL].val_vec3); + hashmap_int_set(entity_data, "diffuse_texture", mesh->model.material_params[MMP_DIFFUSE_TEX].val_int); + break; + }; + hashmap_str_set(entity_data, "geometry", geom->filename); break; } @@ -389,9 +408,34 @@ struct Entity* entity_read(struct Parser_Object* object, struct Entity* parent_e const char* geometry_name = NULL; int material_type = MAT_UNSHADED; if(hashmap_value_exists(object->data, "geometry")) geometry_name = hashmap_str_get(object->data, "geometry"); - if(hashmap_value_exists(object->data, "material_type")) material_type = hashmap_int_get(object->data, "material_type"); + if(hashmap_value_exists(object->data, "material")) material_type = hashmap_int_get(object->data, "material"); struct Static_Mesh* mesh = scene_static_mesh_create(scene, name, parent_entity, geometry_name, material_type); new_entity = &mesh->base; + + //Set material model params for this particular mesh + struct Model* model = &mesh->model; + switch(model->material->type) + { + case MAT_BLINN: + if(hashmap_value_exists(object->data, "diffuse_color")) model->material_params[MMP_DIFFUSE_COL].val_vec4 = hashmap_vec4_get(object->data, "diffuse_color"); + if(hashmap_value_exists(object->data, "diffuse_texture")) + { + const char* texture_name = hashmap_str_get(object->data, "diffuse_texture"); + model->material_params[MMP_DIFFUSE_TEX].val_int = texture_create_from_file(texture_name, TU_DIFFUSE); + } + if(hashmap_value_exists(object->data, "diffuse")) model->material_params[MMP_DIFFUSE].val_float = hashmap_float_get(object->data, "diffuse"); + if(hashmap_value_exists(object->data, "specular")) model->material_params[MMP_SPECULAR].val_float = hashmap_float_get(object->data, "specular"); + if(hashmap_value_exists(object->data, "specular_strength")) model->material_params[MMP_SPECULAR_STRENGTH].val_float = hashmap_float_get(object->data, "specular_strength"); + break; + case MAT_UNSHADED: + if(hashmap_value_exists(object->data, "diffuse_color")) model->material_params[MMP_DIFFUSE_COL].val_vec4 = hashmap_vec4_get(object->data, "diffuse_color"); + if(hashmap_value_exists(object->data, "diffuse_texture")) + { + const char* texture_name = hashmap_str_get(object->data, "diffuse_texture"); + model->material_params[MMP_DIFFUSE_TEX].val_int = texture_create_from_file(texture_name, TU_DIFFUSE); + } + break; + }; } break; case ET_ROOT: diff --git a/src/game/game.c b/src/game/game.c index 153440e..41815bd 100755 --- a/src/game/game.c +++ b/src/game/game.c @@ -328,7 +328,7 @@ void game_scene_setup(void) suzanne->model.material_params[MMP_DIFFUSE].val_float = 0.5f; suzanne->model.material_params[MMP_SPECULAR].val_float = 1.f; suzanne->model.material_params[MMP_SPECULAR_STRENGTH].val_float = 1.f; - vec3_fill(&suzanne->model.material_params[MMP_DIFFUSE_COL].val_vec3, 1.f, 0.f, 1.f); + vec4_fill(&suzanne->model.material_params[MMP_DIFFUSE_COL].val_vec4, 1.f, 0.f, 1.f, 1.f); suz_pos.x = 0.f; suz_pos.y = 5.f; suz_pos.z = 5.f; diff --git a/src/game/model.c b/src/game/model.c index 3b7bce5..582a582 100755 --- a/src/game/model.c +++ b/src/game/model.c @@ -20,24 +20,33 @@ void model_init(struct Model* model, struct Static_Mesh* mesh, const char* geome /* if no name is given for geometry, use default */ int geo_index = geom_create_from_file(geometry_name ? geometry_name : "default.symbres"); + if(!model_geometry_set(model, geometry_name)) + return; + + struct Material* material = &game_state_get()->renderer->materials[material_type]; + if(!material_register_static_mesh(material, mesh)) + { + log_error("model:create", "Unable to register model with Unshaded material"); + model_reset(model, mesh); + } +} + +bool model_geometry_set(struct Model* model, const char* geometry_name) +{ + /* if no name is given for geometry, use default */ + int geo_index = geom_create_from_file(geometry_name ? geometry_name : "default.symbres"); if(geo_index == -1) { - log_error("model:init", "Failed to load model %s", geometry_name); + log_error("model:geometry_set", "Failed to load model %s", geometry_name); geo_index = geom_create_from_file("default.symbres"); if(geo_index == -1) { - log_error("model:init", "Could not load default model 'default.symbres' "); - return; + log_error("model:geometry_set", "Could not load default model 'default.symbres' "); + return false; } } - model->geometry_index = geo_index; - struct Material* material = &game_state_get()->renderer->materials[material_type]; - if(!material_register_static_mesh(material, mesh)) - { - log_error("model:create", "Unable to register model with Unshaded material"); - model_reset(model, mesh); - } + return true; } void model_reset(struct Model* model, struct Static_Mesh* mesh) diff --git a/src/game/model.h b/src/game/model.h index c2ca580..86fda53 100755 --- a/src/game/model.h +++ b/src/game/model.h @@ -1,10 +1,13 @@ #ifndef MODEL_H #define MODEL_H +#include + struct Model; struct Static_Mesh; void model_init(struct Model* model, struct Static_Mesh* mesh, const char* geometry_name, int material_type); +bool model_geometry_set(struct Model* model, const char* geometry_name); void model_reset(struct Model* model, struct Static_Mesh* mesh); #endif diff --git a/src/game/texture.c b/src/game/texture.c index 43b0aae..2046b7a 100755 --- a/src/game/texture.c +++ b/src/game/texture.c @@ -441,3 +441,9 @@ void texture_resize(int index, int width, int height, const void* data) if(curr_texture != 0) glBindTexture(GL_TEXTURE_2D, curr_texture); } + +const char* texture_get_name(int index) +{ + assert(index > -1 && index < array_len(texture_list)); + return texture_list[index].name; +} diff --git a/src/game/texture.h b/src/game/texture.h index c742992..b263f70 100755 --- a/src/game/texture.h +++ b/src/game/texture.h @@ -31,5 +31,6 @@ int texture_create(const char* name, int type, const void* data); void texture_resize(int index, int width, int height, const void* data); +const char* texture_get_name(int index); #endif diff --git a/todo.txt b/todo.txt index b592bb1..f7af410 100644 --- a/todo.txt +++ b/todo.txt @@ -1,5 +1,4 @@ Todo: - - Finish mesh and material properties serialization to and from file - Scene read/write to file with scene file only containing names of entity archetypes - Console command to read/write scene to/from file @@ -98,6 +97,7 @@ Todo: - Profit! Improvements: + - Make selected mesh wireframe alpha in editor configurable - Better naming semantics for example, init/deinit for initialization and cleanup and create/destroy when memory is allocated or deallocated - Categorized entity list in editor for example different subtree for lights and static meshes - Depth testing for editor grid @@ -320,3 +320,6 @@ Done: * Fixed all sound related bugs and completede sound to/from file * Implemented saving/loading entity children when reading/writing entity to file * Editor functionality to add entity from specific file to scene + * Finished mesh and material properties serialization to and from file + * Fixed issue of partial matches returning incorrect hashmap values + * Implemented Static Mesh property viewing/editing in editor