From cf8f9893019877a57f13a01180f5b7996abe81fe Mon Sep 17 00:00:00 2001 From: Shariq Shah Date: Mon, 9 Dec 2019 14:38:12 +1100 Subject: [PATCH] Implemented first version of reading/writing entites to/from files --- src/game/console.c | 21 +++++ src/game/entity.c | 13 +-- src/game/entity.h | 3 +- src/game/scene.c | 204 ++++++++++++++++++++++++++++++++++++++++----- src/game/scene.h | 12 +-- todo.txt | 12 ++- 6 files changed, 229 insertions(+), 36 deletions(-) diff --git a/src/game/console.c b/src/game/console.c index bdc0f4f..4689812 100755 --- a/src/game/console.c +++ b/src/game/console.c @@ -16,6 +16,7 @@ static struct nk_color console_message_color[CMT_MAX]; static int console_filter(const struct nk_text_edit *box, nk_rune unicode); static void console_command_scene_save(struct Console* console, const char* command); +static void console_command_scene_load(struct Console* console, const char* command); static void console_command_entity_save(struct Console* console, const char* command); static void console_command_entity_load(struct Console* console, const char* command); static void console_command_help(struct Console* console, const char* command); @@ -45,6 +46,7 @@ void console_init(struct Console* console) console->console_commands = hashmap_new(); hashmap_ptr_set(console->console_commands, "scene_save", &console_command_scene_save); + hashmap_ptr_set(console->console_commands, "scene_load", &console_command_scene_load); hashmap_ptr_set(console->console_commands, "entity_save", &console_command_entity_save); hashmap_ptr_set(console->console_commands, "entity_load", &console_command_entity_load); hashmap_ptr_set(console->console_commands, "help", &console_command_help); @@ -245,3 +247,22 @@ void console_command_scene_save(struct Console* console, const char* command) if(!scene_save(game_state_get()->scene, full_filename, DIRT_INSTALL)) log_error("scene_save", "Command failed"); } + +void console_command_scene_load(struct Console* console, const char* command) +{ + char filename[MAX_FILENAME_LEN]; + memset(filename, '\0', MAX_FILENAME_LEN); + + int params_read = sscanf(command, "%s", filename); + if(params_read != 1) + { + log_warning("Invalid parameters for command"); + log_warning("Usage: scene_load [file name]"); + return; + } + + char full_filename[MAX_FILENAME_LEN]; + snprintf(full_filename, MAX_FILENAME_LEN, "scenes/%s.symtres", filename); + if(!scene_load(game_state_get()->scene, full_filename, DIRT_INSTALL)) + log_error("scene_load", "Command failed"); +} diff --git a/src/game/entity.c b/src/game/entity.c index 921ea3c..b4951b7 100755 --- a/src/game/entity.c +++ b/src/game/entity.c @@ -36,6 +36,7 @@ void entity_init(struct Entity* entity, const char* name, struct Entity* parent) strncpy(entity->name, name ? name : "DEFAULT_ENTITY_NAME", MAX_ENTITY_NAME_LEN); entity->name[MAX_ENTITY_NAME_LEN - 1] = '\0'; entity->type = ET_DEFAULT; + entity->archetype_index = -1; entity->active = true; entity->marked_for_deletion = false; entity->selected_in_editor = false; @@ -46,13 +47,14 @@ void entity_reset(struct Entity * entity, int id) { assert(entity); entity->id = id; + entity->type = ET_DEFAULT; + entity->archetype_index = -1; entity->active = false; entity->marked_for_deletion = false; entity->selected_in_editor = false; memset(entity->name, '\0', MAX_ENTITY_NAME_LEN); } - bool entity_write(struct Entity* entity, struct Parser_Object* object, bool write_transform) { if(!object) @@ -168,14 +170,14 @@ bool entity_write(struct Entity* entity, struct Parser_Object* object, bool writ { 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_str_set(entity_data, "diffuse_texture", model->material_params[MMP_DIFFUSE_TEX].val_int == -1 ? "default.tga" : 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); + hashmap_str_set(entity_data, "diffuse_texture", model->material_params[MMP_DIFFUSE_TEX].val_int == -1 ? "default.tga" : texture_get_name(model->material_params[MMP_DIFFUSE_TEX].val_int)); break; }; @@ -297,6 +299,7 @@ struct Entity* entity_read(struct Parser_Object* object, struct Entity* parent_e int fbo_width = -1; int fbo_height = -1; struct Camera* camera = scene_camera_create(scene, name, parent_entity, 320, 240); + if(!camera) return new_entity; if(hashmap_value_exists(object->data, "fov")) camera->fov = hashmap_float_get(object->data, "fov"); if(hashmap_value_exists(object->data, "resizeable")) camera->resizeable = hashmap_bool_get(object->data, "resizeable"); if(hashmap_value_exists(object->data, "zoom")) camera->zoom = hashmap_float_get(object->data, "zoom"); @@ -469,7 +472,7 @@ struct Entity* entity_read(struct Parser_Object* object, struct Entity* parent_e return new_entity; } -bool entity_load(const char* filename, int directory_type) +struct Entity* entity_load(const char* filename, int directory_type) { FILE* entity_file = io_file_open(directory_type, filename, "rb"); if(!entity_file) @@ -519,7 +522,7 @@ bool entity_load(const char* filename, int directory_type) parser_free(parsed_file); fclose(entity_file); - return num_entites_loaded > 0 ? true : false; + return parent_entity; } const char* entity_type_name_get(struct Entity* entity) diff --git a/src/game/entity.h b/src/game/entity.h index 653be88..e17e0de 100755 --- a/src/game/entity.h +++ b/src/game/entity.h @@ -61,6 +61,7 @@ struct Entity { int id; int type; + int archetype_index; char name[MAX_ENTITY_NAME_LEN]; bool marked_for_deletion; bool active; @@ -154,7 +155,7 @@ struct Player void entity_init(struct Entity* entity, const char* name, struct Entity* parent); void entity_reset(struct Entity* entity, int id); bool entity_save(struct Entity* entity, const char* filename, int directory_type); -bool entity_load(const char* filename, int directory_type); +struct Entity* entity_load(const char* filename, int directory_type); bool entity_write(struct Entity* entity, struct Parser_Object* object, bool write_transform); struct Entity* entity_read(struct Parser_Object* object, struct Entity* parent_entity); const char* entity_type_name_get(struct Entity* entity); diff --git a/src/game/scene.c b/src/game/scene.c index f942978..777a378 100755 --- a/src/game/scene.c +++ b/src/game/scene.c @@ -22,6 +22,9 @@ #include #include +static void scene_write_entity_entry(struct Scene* scene, struct Entity* entity, struct Parser* parser); +static void scene_write_entity_list(struct Scene* scene, int entity_type, struct Parser* parser); + void scene_init(struct Scene* scene) { assert(scene); @@ -39,6 +42,7 @@ void scene_init(struct Scene* scene) entity_reset(&scene->lights[i], i); scene->lights[i].type = ET_LIGHT; } + for(int i = 0; i < MAX_STATIC_MESHES; i++) { entity_reset(&scene->static_meshes[i], i); @@ -49,6 +53,7 @@ void scene_init(struct Scene* scene) mesh->model.geometry_index = -1; mesh->model.material = NULL; } + for(int i = 0; i < MAX_SOUND_SOURCES; i++) entity_reset(&scene->sound_sources[i], i); int width = 1280, height = 720; window_get_drawable_size(game_state->window, &width, &height); @@ -57,9 +62,13 @@ void scene_init(struct Scene* scene) { entity_init(&scene->cameras[i], NULL, &scene->root_entity); camera_init(&scene->cameras[i], width, height); + scene->cameras[i].base.active = false; scene->cameras[i].base.id = i; } + for(int i = 0; i < MAX_ENTITY_ARCHETYPES; i++) + memset(&scene->entity_archetypes[i][0], '\0', MAX_FILENAME_LEN); + player_init(&scene->player, scene); editor_init_camera(game_state->editor, game_state->cvars); @@ -68,7 +77,98 @@ void scene_init(struct Scene* scene) bool scene_load(struct Scene* scene, const char* filename, int directory_type) { - return false; + FILE* scene_file = io_file_open(directory_type, filename, "rb"); + if(!scene_file) + { + log_error("scene:load", "Failed to open scene file %s for reading"); + return false; + } + + // Load scene config and apply renderer settings + struct Parser* parsed_file = parser_load_objects(scene_file, filename); + + if(!parsed_file) + { + log_error("scene:load", "Failed to parse file '%s' for loading scene", filename); + fclose(scene_file); + return false; + } + + if(array_len(parsed_file->objects) == 0) + { + log_error("scene:load", "No objects found in file %s", filename); + parser_free(parsed_file); + fclose(scene_file); + return false; + } + + //Clear previous scene and re-initialize + scene_destroy(scene); + scene_post_update(scene); + scene_init(scene); + + int num_objects_loaded = 0; + for(int i = 0; i < array_len(parsed_file->objects); i++) + { + struct Parser_Object* object = &parsed_file->objects[i]; + if(object->type == PO_SCENE_CONFIG) + { + struct Hashmap* scene_data = object->data; + struct Render_Settings* render_settings = &game_state_get()->renderer->settings; + + if(hashmap_value_exists(scene_data, "fog_type")) render_settings->fog.mode = hashmap_int_get(scene_data, "fog_type"); + if(hashmap_value_exists(scene_data, "fog_density")) render_settings->fog.density = hashmap_float_get(scene_data, "fog_density"); + if(hashmap_value_exists(scene_data, "fog_start_distance")) render_settings->fog.start_dist = hashmap_float_get(scene_data, "fog_start_distance"); + if(hashmap_value_exists(scene_data, "fog_max_distance")) render_settings->fog.max_dist = hashmap_float_get(scene_data, "fog_max_distance"); + if(hashmap_value_exists(scene_data, "fog_color")) render_settings->fog.color = hashmap_vec3_get(scene_data, "fog_color"); + if(hashmap_value_exists(scene_data, "ambient_light")) render_settings->ambient_light = hashmap_vec3_get(scene_data, "ambient_light"); + if(hashmap_value_exists(scene_data, "debug_draw_color")) render_settings->debug_draw_color = hashmap_vec4_get(scene_data, "debug_draw_color"); + if(hashmap_value_exists(scene_data, "debug_draw_enabled")) render_settings->debug_draw_enabled = hashmap_bool_get(scene_data, "debug_draw_enabled"); + if(hashmap_value_exists(scene_data, "debug_draw_mode")) render_settings->debug_draw_mode = hashmap_int_get(scene_data, "debug_draw_mode"); + if(hashmap_value_exists(scene_data, "debug_draw_physics")) render_settings->debug_draw_physics = hashmap_bool_get(scene_data, "debug_draw_physics"); + num_objects_loaded++; + } + else if(object->type == PO_ENTITY) + { + if(entity_read(object, &scene->root_entity)) + num_objects_loaded++; + } + else if(object->type == PO_SCENE_ENTITY_ENTRY) + { + struct Hashmap* entity_entry_data = object->data; + if(hashmap_value_exists(object->data, "filename")) + { + struct Entity* loaded_entity = entity_load(hashmap_str_get(entity_entry_data, "filename"), DIRT_INSTALL); + if(loaded_entity) + { + vec3 position = { 0.f, 0.f, 0.f }; + quat rotation = { 0.f, 0.f, 0.f, 1.f }; + vec3 scale = { 1.f, 1.f, 1.f }; + + if(hashmap_value_exists(entity_entry_data, "position")) position = hashmap_vec3_get(entity_entry_data, "position"); + if(hashmap_value_exists(entity_entry_data, "rotation")) rotation = hashmap_quat_get(entity_entry_data, "rotation"); + if(hashmap_value_exists(entity_entry_data, "scale")) scale = hashmap_vec3_get(entity_entry_data, "scale"); + + transform_set_position(loaded_entity, &position); + transform_scale(loaded_entity, &scale); + quat_assign(&loaded_entity->transform.rotation, &rotation); + transform_update_transmat(loaded_entity); + + if(hashmap_value_exists(entity_entry_data, "name")) strncpy(loaded_entity->name, hashmap_str_get(entity_entry_data, "name"), MAX_ENTITY_NAME_LEN); + num_objects_loaded++; + } + } + } + else + { + log_warning("Unknown object type '%s' in scene file %s", parser_object_type_to_str(object->type), filename); + continue; + } + } + + parser_free(parsed_file); + fclose(scene_file); + return num_objects_loaded > 0 ? true : false; } bool scene_save(struct Scene* scene, const char* filename, int directory_type) @@ -97,21 +197,11 @@ bool scene_save(struct Scene* scene, const char* filename, int directory_type) hashmap_int_set(scene_data, "debug_draw_mode", render_settings->debug_draw_mode); hashmap_bool_set(scene_data, "debug_draw_physics", render_settings->debug_draw_physics); - for(int i = 0; i < MAX_ENTITIES; i++) - { - struct Entity* entity = &scene->entities[i]; - if(!entity->active) - continue; - - struct Parser_Object* object = parser_object_new(parser, PO_ENTITY); - if(!entity_write(entity, object, false)) - { - log_error("scene:save", "Failed to save entity : %s to file : %s", entity->name, filename); - parser_free(parser); - fclose(scene_file); - return false; - } - } + scene_write_entity_list(scene, ET_DEFAULT, parser); + scene_write_entity_list(scene, ET_LIGHT, parser); + scene_write_entity_list(scene, ET_STATIC_MESH, parser); + scene_write_entity_list(scene, ET_CAMERA, parser); + scene_write_entity_list(scene, ET_SOUND_SOURCE, parser); if(parser_write_objects(parser, scene_file, filename)) log_message("Scene saved to %s", filename); @@ -121,15 +211,87 @@ bool scene_save(struct Scene* scene, const char* filename, int directory_type) return true; } +void scene_write_entity_list(struct Scene* scene, int entity_type, struct Parser* parser) +{ + int max_length = 0; + size_t stride = 0; + struct Entity* entity = NULL; + + switch(entity_type) + { + case ET_DEFAULT: + max_length = MAX_ENTITIES; + entity = &scene->entities[0]; + stride = sizeof(struct Entity); + break; + case ET_LIGHT: + max_length = MAX_LIGHTS; + entity = &scene->lights[0].base; + stride = sizeof(struct Light); + break; + case ET_STATIC_MESH: + max_length = MAX_STATIC_MESHES; + entity = &scene->static_meshes[0].base; + stride = sizeof(struct Static_Mesh); + break; + case ET_CAMERA: + max_length = MAX_CAMERAS; + entity = &scene->cameras[0].base; + stride = sizeof(struct Camera); + break; + case ET_SOUND_SOURCE: + max_length = MAX_SOUND_SOURCES; + entity = &scene->sound_sources[0].base; + stride = sizeof(struct Sound_Source); + break; + default: return; + } + + size_t count = 0; + while(count < max_length) + { + //((char*)entity) += stride * count; + ((char*)entity) += stride; + + if(entity->active) + { + if(entity->archetype_index != -1) + { + scene_write_entity_entry(scene, entity, parser); + } + else + { + struct Parser_Object* object = parser_object_new(parser, PO_ENTITY); + if(!entity_write(entity, object, true)) + log_error("scene:save", "Failed to save entity : %s", entity->name); + } + } + count++; + } +} + +void scene_write_entity_entry(struct Scene* scene, struct Entity* entity, struct Parser* parser) +{ + // For entities with archetypes, we only write the name of the archetype to load + // them from and their transformation info + struct Parser_Object* object = parser_object_new(parser, PO_SCENE_ENTITY_ENTRY); + hashmap_str_set(object->data, "filename", &scene->entity_archetypes[entity->archetype_index][0]); + hashmap_str_set(object->data, "name", entity->name); + hashmap_vec3_set(object->data, "position", &entity->transform.position); + hashmap_vec3_set(object->data, "scale", &entity->transform.scale); + hashmap_quat_set(object->data, "rotation", &entity->transform.rotation); +} + void scene_destroy(struct Scene* scene) { assert(scene); - for(int i = 0; i < MAX_ENTITIES; i++) scene_entity_base_remove(scene, &scene->entities[i]); - for(int i = 0; i < MAX_CAMERAS; i++) scene_camera_remove(scene, &scene->cameras[i]); - for(int i = 0; i < MAX_LIGHTS; i++) scene_light_remove(scene, &scene->lights[i]); - for(int i = 0; i < MAX_STATIC_MESHES; i++) scene_static_mesh_remove(scene, &scene->static_meshes[i]); - for(int i = 0; i < MAX_SOUND_SOURCES; i++) scene_sound_source_remove(scene, &scene->sound_sources[i]); + for(int i = 0; i < MAX_ENTITIES; i++) scene_entity_base_remove(scene, &scene->entities[i]); + for(int i = 0; i < MAX_CAMERAS; i++) scene_camera_remove(scene, &scene->cameras[i]); + for(int i = 0; i < MAX_LIGHTS; i++) scene_light_remove(scene, &scene->lights[i]); + for(int i = 0; i < MAX_STATIC_MESHES; i++) scene_static_mesh_remove(scene, &scene->static_meshes[i]); + for(int i = 0; i < MAX_SOUND_SOURCES; i++) scene_sound_source_remove(scene, &scene->sound_sources[i]); + for(int i = 0; i < MAX_ENTITY_ARCHETYPES; i++) memset(&scene->entity_archetypes[i][0], '\0', MAX_FILENAME_LEN); player_destroy(&scene->player); entity_reset(&scene->root_entity, 0); scene->root_entity.active = false; diff --git a/src/game/scene.h b/src/game/scene.h index 3aab0af..d8bc5ea 100755 --- a/src/game/scene.h +++ b/src/game/scene.h @@ -4,11 +4,12 @@ #include "entity.h" #include "renderer.h" -#define MAX_ENTITIES 1024 -#define MAX_LIGHTS 30 -#define MAX_CAMERAS 2 -#define MAX_STATIC_MESHES 1024 -#define MAX_SOUND_SOURCES 128 +#define MAX_ENTITIES 1024 +#define MAX_LIGHTS 30 +#define MAX_CAMERAS 2 +#define MAX_STATIC_MESHES 1024 +#define MAX_SOUND_SOURCES 128 +#define MAX_ENTITY_ARCHETYPES 32 struct Ray; struct Raycast_Result; @@ -22,6 +23,7 @@ struct Scene struct Camera cameras[MAX_CAMERAS]; struct Light lights[MAX_LIGHTS]; struct Sound_Source sound_sources[MAX_SOUND_SOURCES]; + char entity_archetypes[MAX_ENTITY_ARCHETYPES][MAX_FILENAME_LEN]; int active_camera_index; }; diff --git a/todo.txt b/todo.txt index ef24055..f166640 100644 --- a/todo.txt +++ b/todo.txt @@ -1,9 +1,9 @@ Todo: - - Implement entitie storing a reference or name of file they've been loaded from to help when they're being saved + - Implement flags that specify whether an entity should be written to file or not for example to avoid writing cameras and player to file + - Implment/Test reading/writing scene that has a mixture of default entites and entity archetypes + - Serialize player, camera properties to file + - Implement behaviour that avoids writing normal entities that do not have children or parent to file to avoid inconsistencies when loading them - Change mouse behaviour to lock cursor when looking around so as not to interfere with gui elements when in editor mode - - Scene read/write to file with scene file only containing names of - entity archetypes - - Console command to read/write scene to/from file - Editor functionality to read/write scene to/from file - Folder management api to create/delete folders when none exist. Dirent would suffice for our simple needs? - Display default mesh when selected entity type in editor does not have a mesh @@ -328,3 +328,7 @@ Done: * Implemented Static Mesh property viewing/editing in editor * Implemented contextual menu for re-entering diffuse texture name * Implemented contextual menus for entering geometry and sound source names + * Implemented entities storing a reference or name of file they've been loaded from to help when they're being saved + * Scene read/write to file with scene file only containing names of + entity archetypes + * Console command to read/write scene to/from file