Implemented saving/loading entites using dialogs from editor

dev
Shariq Shah 6 years ago
parent 16beb89250
commit e81372c88e
  1. 10
      src/game/console.c
  2. 114
      src/game/editor.c
  3. 28
      src/game/entity.c
  4. 12
      src/game/scene.c
  5. 26
      todo.txt

@ -241,9 +241,7 @@ void console_command_entity_save(struct Console* console, const char* command)
return; return;
} }
char full_filename[MAX_FILENAME_LEN]; if(!entity_save(entity, filename, DIRT_INSTALL))
snprintf(full_filename, MAX_FILENAME_LEN, "entities/%s.symtres", filename);
if(!entity_save(entity, full_filename, DIRT_INSTALL))
log_error("entity_save", "Command failed"); log_error("entity_save", "Command failed");
} }
@ -262,12 +260,10 @@ void console_command_entity_load(struct Console* console, const char* command)
return; return;
} }
char full_filename[MAX_FILENAME_LEN]; struct Entity* new_entity = entity_load(filename, DIRT_INSTALL);
snprintf(full_filename, MAX_FILENAME_LEN, "entities/%s.symtres", filename);
struct Entity* new_entity = entity_load(full_filename, DIRT_INSTALL);
if(!new_entity) if(!new_entity)
{ {
log_error("entity_load", "Could not create entity from '%s'", full_filename); log_error("entity_load", "Could not create entity from '%s'", filename);
return; return;
} }

@ -714,6 +714,7 @@ void editor_update(struct Editor* editor, float dt)
} }
if(editor->window_scene_dialog) editor_scene_dialog(editor, context); if(editor->window_scene_dialog) editor_scene_dialog(editor, context);
if(editor->window_entity_dialog) editor_entity_dialog(editor, context);
} }
void editor_scene_dialog(struct Editor* editor, struct nk_context* context) void editor_scene_dialog(struct Editor* editor, struct nk_context* context)
@ -1687,7 +1688,10 @@ void editor_window_property_inspector(struct nk_context* context, struct Editor*
copy_parent_name = true; copy_parent_name = true;
nk_edit_unfocus(context); nk_edit_unfocus(context);
} }
//nk_label(context, parent_ent ? parent_ent->name : "NONE", NK_TEXT_ALIGN_RIGHT);
nk_layout_row_dynamic(context, row_height, 2);
nk_label(context, "Children", NK_TEXT_ALIGN_LEFT | NK_TEXT_ALIGN_MIDDLE);
nk_labelf(context, NK_TEXT_ALIGN_LEFT | NK_TEXT_ALIGN_MIDDLE, "%d", array_len(entity->transform.children));
/* Transform */ /* Transform */
{ {
@ -2170,5 +2174,113 @@ void editor_window_settings_editor(struct nk_context* context, struct Editor* ed
void editor_entity_dialog(struct Editor* editor, struct nk_context* context) void editor_entity_dialog(struct Editor* editor, struct nk_context* context)
{ {
struct Game_State* game_state = game_state_get();
struct Scene* scene = game_state->scene;
bool save = editor->entity_operation_save;
int row_height = 25;
int popup_x = 0;
int popup_y = 0;
int popup_width = 300;
int popup_height = 200;
int display_width = 0;
int display_height = 0;
int popup_flags = NK_WINDOW_TITLE | NK_WINDOW_BORDER;
window_get_drawable_size(game_state_get()->window, &display_width, &display_height);
popup_x = (display_width / 2) - (popup_width / 2);
popup_y = (display_height / 2) - (popup_height / 2);
int background_window_flags = NK_WINDOW_BACKGROUND;
int previous_opacity = context->style.window.fixed_background.data.color.a;
context->style.window.fixed_background.data.color.a = 120;
if(nk_begin(context, save ? "Entity Save" : "Entity Load", nk_recti(0, 0, display_width, display_height), background_window_flags))
{
nk_window_set_focus(context, save ? "Entity Save" : "Entity Load");
if(nk_popup_begin(context, NK_POPUP_DYNAMIC, save ? "Save Entity" : "Load Entity", popup_flags, nk_recti(popup_x, popup_y, popup_width, popup_height)))
{
nk_layout_row_dynamic(context, row_height, 1);
if(save && !editor->selected_entity)
{
nk_label_colored(context, "Please select an entity first in order to save it", NK_TEXT_ALIGN_CENTERED | NK_TEXT_ALIGN_MIDDLE, nk_rgb_f(1.f, 1.f, 0.f));
if(nk_button_label(context, "OK"))
{
editor->window_entity_dialog = 0;
nk_popup_close(context);
}
}
else
{
nk_label(context, "Enter the name of the entity:", NK_TEXT_ALIGN_CENTERED | NK_TEXT_ALIGN_MIDDLE);
static char entity_filename[MAX_FILENAME_LEN];
static bool copy_entity_filename = true;
if(copy_entity_filename)
{
memset(entity_filename, '\0', MAX_FILENAME_LEN);
}
int entity_filename_flags = NK_EDIT_SIG_ENTER | NK_EDIT_GOTO_END_ON_ACTIVATE | NK_EDIT_FIELD;
int entity_filename_state = nk_edit_string_zero_terminated(context, entity_filename_flags, entity_filename, MAX_FILENAME_LEN, NULL);
if(entity_filename_state & NK_EDIT_ACTIVATED)
{
copy_entity_filename = false;
}
else if(entity_filename_state & NK_EDIT_COMMITED)
{
if(save)
{
entity_save(editor->selected_entity, entity_filename, DIRT_INSTALL);
}
else
{
struct Entity* new_entity = entity_load(entity_filename, DIRT_INSTALL);
if(new_entity)
{
editor_entity_select(editor, new_entity);
}
}
copy_entity_filename = true;
editor->window_entity_dialog = 0;
nk_popup_close(context);
}
nk_layout_row_dynamic(context, row_height, 3);
if(nk_button_label(context, "OK"))
{
if(save)
{
entity_save(editor->selected_entity, entity_filename, DIRT_INSTALL);
}
else
{
struct Entity* new_entity = entity_load(entity_filename, DIRT_INSTALL);
if(new_entity)
{
editor_entity_select(editor, new_entity);
}
}
copy_entity_filename = true;
editor->window_entity_dialog = 0;
nk_popup_close(context);
}
nk_spacing(context, 1);
if(nk_button_label(context, "Cancel"))
{
copy_entity_filename = true;
editor->window_entity_dialog = 0;
nk_popup_close(context);
}
}
nk_popup_end(context);
}
nk_end(context);
}
else
{
editor->window_entity_dialog = 0;
}
context->style.window.fixed_background.data.color.a = previous_opacity;
} }

@ -217,7 +217,9 @@ bool entity_write(struct Entity* entity, struct Parser_Object* object, bool writ
bool entity_save(struct Entity* entity, const char* filename, int directory_type) bool entity_save(struct Entity* entity, const char* filename, int directory_type)
{ {
FILE* entity_file = io_file_open(directory_type, filename, "w"); char prefixed_filename[MAX_FILENAME_LEN + 16];
snprintf(prefixed_filename, MAX_FILENAME_LEN + 16, "entities/%s.symtres", filename);
FILE* entity_file = io_file_open(directory_type, prefixed_filename, "w");
if(!entity_file) if(!entity_file)
{ {
log_error("entity:save", "Failed to open entity file %s for writing"); log_error("entity:save", "Failed to open entity file %s for writing");
@ -228,7 +230,7 @@ bool entity_save(struct Entity* entity, const char* filename, int directory_type
struct Parser_Object* object = parser_object_new(parser, PO_ENTITY); struct Parser_Object* object = parser_object_new(parser, PO_ENTITY);
if(!entity_write(entity, object, false)) if(!entity_write(entity, object, false))
{ {
log_error("entity:save", "Failed to save entity : %s to file : %s", entity->name, filename); log_error("entity:save", "Failed to save entity : %s to file : %s", entity->name, prefixed_filename);
parser_free(parser); parser_free(parser);
fclose(entity_file); fclose(entity_file);
return false; return false;
@ -243,15 +245,15 @@ bool entity_save(struct Entity* entity, const char* filename, int directory_type
struct Entity* child_entity = entity->transform.children[i]; struct Entity* child_entity = entity->transform.children[i];
if (!entity_write(child_entity, child_object, true)) if (!entity_write(child_entity, child_object, true))
{ {
log_error("entity:save", "Failed to write child entity : %s for parent entity : %s to file : %s", entity->name, child_entity->name, filename); log_error("entity:save", "Failed to write child entity : %s for parent entity : %s to file : %s", entity->name, child_entity->name, prefixed_filename);
parser_free(parser); parser_free(parser);
fclose(entity_file); fclose(entity_file);
return false; return false;
} }
} }
if(parser_write_objects(parser, entity_file, filename)) if(parser_write_objects(parser, entity_file, prefixed_filename))
log_message("Entity %s saved to %s", entity->name, filename); log_message("Entity %s saved to %s", entity->name, prefixed_filename);
parser_free(parser); parser_free(parser);
fclose(entity_file); fclose(entity_file);
@ -470,26 +472,28 @@ struct Entity* entity_read(struct Parser_Object* object, struct Entity* parent_e
struct Entity* 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"); char prefixed_filename[MAX_FILENAME_LEN + 16];
snprintf(prefixed_filename, MAX_FILENAME_LEN + 16, "entities/%s.symtres", filename);
FILE* entity_file = io_file_open(directory_type, prefixed_filename, "rb");
if(!entity_file) if(!entity_file)
{ {
log_error("entity:load", "Failed to open entity file %s for reading", filename); log_error("entity:load", "Failed to open entity file %s for reading", prefixed_filename);
return false; return false;
} }
struct Parser* parsed_file = parser_load_objects(entity_file, filename); struct Parser* parsed_file = parser_load_objects(entity_file, prefixed_filename);
struct Entity* new_entity = false; struct Entity* new_entity = false;
if(!parsed_file) if(!parsed_file)
{ {
log_error("entity:load", "Failed to parse file '%s' for entity definition", filename); log_error("entity:load", "Failed to parse file '%s' for entity definition", prefixed_filename);
fclose(entity_file); fclose(entity_file);
return false; return false;
} }
if(array_len(parsed_file->objects) == 0) if(array_len(parsed_file->objects) == 0)
{ {
log_error("entity:load", "No objects found in file %s", filename); log_error("entity:load", "No objects found in file %s", prefixed_filename);
parser_free(parsed_file); parser_free(parsed_file);
fclose(entity_file); fclose(entity_file);
return false; return false;
@ -515,11 +519,11 @@ struct Entity* entity_load(const char* filename, int directory_type)
if(i != 0) if(i != 0)
new_entity->flags |= EF_TRANSIENT; new_entity->flags |= EF_TRANSIENT;
num_entites_loaded++; num_entites_loaded++;
log_message("Entity %s loaded from %s", new_entity->name, filename); log_message("Entity %s loaded from %s", new_entity->name, prefixed_filename);
} }
else else
{ {
log_error("entity:load", "Failed to load entity from %s", filename); log_error("entity:load", "Failed to load entity from %s", prefixed_filename);
} }
} }

@ -88,18 +88,18 @@ bool scene_load(struct Scene* scene, const char* filename, int directory_type)
} }
// Load scene config and apply renderer settings // Load scene config and apply renderer settings
struct Parser* parsed_file = parser_load_objects(scene_file, filename); struct Parser* parsed_file = parser_load_objects(scene_file, prefixed_filename);
if(!parsed_file) if(!parsed_file)
{ {
log_error("scene:load", "Failed to parse file '%s' for loading scene", filename); log_error("scene:load", "Failed to parse file '%s' for loading scene", prefixed_filename);
fclose(scene_file); fclose(scene_file);
return false; return false;
} }
if(array_len(parsed_file->objects) == 0) if(array_len(parsed_file->objects) == 0)
{ {
log_error("scene:load", "No objects found in file %s", filename); log_error("scene:load", "No objects found in file %s", prefixed_filename);
parser_free(parsed_file); parser_free(parsed_file);
fclose(scene_file); fclose(scene_file);
return false; return false;
@ -187,7 +187,7 @@ bool scene_load(struct Scene* scene, const char* filename, int directory_type)
} }
break; break;
default: default:
log_warning("Unknown object type '%s' in scene file %s", parser_object_type_to_str(object->type), filename); log_warning("Unknown object type '%s' in scene file %s", parser_object_type_to_str(object->type), prefixed_filename);
continue; continue;
} }
} }
@ -235,8 +235,8 @@ bool scene_save(struct Scene* scene, const char* filename, int directory_type)
scene_write_entity_list(scene, ET_CAMERA, parser); scene_write_entity_list(scene, ET_CAMERA, parser);
scene_write_entity_list(scene, ET_SOUND_SOURCE, parser); scene_write_entity_list(scene, ET_SOUND_SOURCE, parser);
if(parser_write_objects(parser, scene_file, filename)) if(parser_write_objects(parser, scene_file, prefixed_filename))
log_message("Scene saved to %s", filename); log_message("Scene saved to %s", prefixed_filename);
parser_free(parser); parser_free(parser);
fclose(scene_file); fclose(scene_file);

@ -1,5 +1,7 @@
Todo: Todo:
- Implement scene save/load and entity save/load operations via editor - For entities that have archetypes, check read/write them to the file they were loaded from when showing the save entity dialog
- When loading entities, show an additional optional textfield where user can enter the name they want for the entity after it is loaded
- Implelement contextual actions for entites that are shown in scene hierarchy
- Add editor undo for transformation operations - Add editor undo for transformation operations
- Decide how to handle scale when checking sphere-ray intersection - Decide how to handle scale when checking sphere-ray intersection
- Add material export for blender exporter? - Add material export for blender exporter?
@ -9,8 +11,15 @@ Todo:
- Add config file reloading and fire event that notifies potential listeners to update values from the new config file - Add config file reloading and fire event that notifies potential listeners to update values from the new config file
- Command to reload entities only - Command to reload entities only
- Serialize player, camera properties to file - Serialize player, camera properties to file
- Gameplay level features:
- Each scene should always have a directional light that serves as the main source of light when there are no other lights
- Each scene should always have a texture cube that serves as the sky
- Player/World collision and movement using ray casts only
- Triggers
- Basic Enemy
- Main Menu Scene
? Split this todo into gameplay/engine todos
- Multisampled buffers to bring back aa - Multisampled buffers to bring back aa
- 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 - Change mouse behaviour to lock cursor when looking around so as not to interfere with gui elements when in editor mode
- Folder management api to create/delete folders when none exist. Dirent would suffice for our simple needs? - 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 - Display default mesh when selected entity type in editor does not have a mesh
@ -34,14 +43,11 @@ Todo:
- Implement resetting complete transform or just rotation, translation - Implement resetting complete transform or just rotation, translation
or scale for selected entity or scale for selected entity
- Key binding and function to orient entity to camera orientation - Key binding and function to orient entity to camera orientation
- Display the name of the hovered entity either as a tooltip under the
cursor of in the top/bottom status bar
- Make lights and other entity types pickable - Make lights and other entity types pickable
- Mouse warp to opposite side of the window when it reaches bounds - Mouse warp to opposite side of the window when it reaches bounds
- Add other axis combinations like YZ and XY to transform tool - Add other axis combinations like YZ and XY to transform tool
- Transformation space selection for translation, rotation and scale. - Transformation space selection for translation, rotation and scale.
- Subscribe and Unsubscribe based on game mode changes - Subscribe and Unsubscribe based on game mode changes
- Use actual selected entity's mesh for tool mesh when the entity already has a mesh and use a placeholder like a sphere when there is not mesh
- Add warning to genie build script when running on windows and WindowsSdkVersion cannot be found. This happens when the script is not run from vcvarsall command prompt - Add warning to genie build script when running on windows and WindowsSdkVersion cannot be found. This happens when the script is not run from vcvarsall command prompt
- Improve README and add a screenshot to make the repository ready for making it public - Improve README and add a screenshot to make the repository ready for making it public
- Show Transformation deltas for example a line showing where we are to where we are going to be if we click and apply the selected transformation - Show Transformation deltas for example a line showing where we are to where we are going to be if we click and apply the selected transformation
@ -53,9 +59,6 @@ Todo:
- Quick scene filter and entity selection by popping up a menu which has list of entities and fuzzy matches them based on the typed name - Quick scene filter and entity selection by popping up a menu which has list of entities and fuzzy matches them based on the typed name
- Screen mouse coordinates to world-coordinates for aiming - Screen mouse coordinates to world-coordinates for aiming
- Player projectiles and sounds - Player projectiles and sounds
- Console command history
- Console command help
- Debug drawing/variable display that also works in game mode. Can be toggled with a console command. Can show variable values or plots thier graphs or debug textures etc
- Space partitioning and scene handling - Space partitioning and scene handling
- Move Gui_State and Editor_State into game_state and modify usage as needed - Move Gui_State and Editor_State into game_state and modify usage as needed
- Get editor camera speed and other settings from config file - Get editor camera speed and other settings from config file
@ -371,3 +374,10 @@ Done:
* Implmented renaming scene objects in editor * Implmented renaming scene objects in editor
* Implemented setting/resetting parent entity for entity * Implemented setting/resetting parent entity for entity
* Editor functionality to read/write scene to/from file * Editor functionality to read/write scene to/from file
* Display the name of the hovered entity either as a tooltip under the cursor of in the top/bottom status bar
* Debug drawing/variable display that also works in game mode. Can be toggled with a console command. Can show variable values or plots thier graphs or debug textures etc
* Console command history
* Console command help
* Use actual selected entity's mesh for tool mesh when the entity already has a mesh and use a placeholder like a sphere when there is not mesh
* Implmented dialog for loading entities into the scene via editor
* Implement behaviour that avoids writing normal entities that do not have children or parent to file to avoid inconsistencies when loading them
Loading…
Cancel
Save