Refactored sound related api and implemented writing entity to particular file

dev
Shariq Shah 6 years ago
parent 65382c7d37
commit c0dc6a2c69
  1. 8
      src/game/console.c
  2. 156
      src/game/entity.c
  3. 1
      src/game/entity.h
  4. 6
      src/game/game.c
  5. 2
      src/game/game.h
  6. 2
      src/game/material.c
  7. 8
      src/game/model.c
  8. 47
      src/game/scene.c
  9. 2
      src/system/file_io.h
  10. 7
      src/system/main.c
  11. 252
      src/system/sound.c
  12. 94
      src/system/sound.h
  13. 2
      todo.txt

@ -159,9 +159,9 @@ void console_on_log_error(struct Console* console, const char* context, const ch
void console_command_entity_save(struct Console* console, const char* command) void console_command_entity_save(struct Console* console, const char* command)
{ {
char filename[64]; char filename[MAX_FILENAME_LEN];
char entity_name[MAX_ENTITY_NAME_LEN]; char entity_name[MAX_ENTITY_NAME_LEN];
memset(filename, '\0', 64); memset(filename, '\0', MAX_FILENAME_LEN);
memset(entity_name, '\0', MAX_ENTITY_NAME_LEN); memset(entity_name, '\0', MAX_ENTITY_NAME_LEN);
int params_read = sscanf(command, "%s %s", entity_name, filename); int params_read = sscanf(command, "%s %s", entity_name, filename);
@ -179,7 +179,9 @@ void console_command_entity_save(struct Console* console, const char* command)
return; return;
} }
if(!entity_save(entity, filename, DIRT_INSTALL)) char full_filename[MAX_FILENAME_LEN];
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");
} }

@ -52,20 +52,18 @@ void entity_reset(struct Entity * entity, int id)
bool entity_write(struct Entity* entity, struct Parser_Object* object) bool entity_write(struct Entity* entity, struct Parser_Object* object)
{ {
//if(!object) if(!object)
//{ {
// log_error("entity:write", "Invalid object"); log_error("entity:write", "Invalid object");
// return false; return false;
//} }
///* First write all properties common to all entity types */ /* First write all properties common to all entity types */
//struct Hashmap* entity_data = object->data; struct Hashmap* entity_data = object->data;
//hashmap_str_set(entity_data, "name", entity->name); hashmap_str_set(entity_data, "name", entity->name);
//hashmap_int_set(entity_data, "type", entity->type); hashmap_int_set(entity_data, "type", entity->type);
//hashmap_bool_set(entity_data, "is_listener", entity->is_listener); hashmap_bool_set(entity_data, "active", entity->active);
//hashmap_bool_set(entity_data, "renderable", entity->renderable);
//hashmap_bool_set(entity_data, "has_collision", entity->has_collision);
//if(entity->has_collision) //if(entity->has_collision)
//{ //{
@ -125,72 +123,71 @@ bool entity_write(struct Entity* entity, struct Parser_Object* object)
//hashmap_vec3_set(entity_data, "position", &entity->transform.position); //hashmap_vec3_set(entity_data, "position", &entity->transform.position);
//hashmap_vec3_set(entity_data, "scale", &entity->transform.scale); //hashmap_vec3_set(entity_data, "scale", &entity->transform.scale);
//hashmap_quat_set(entity_data, "rotation", &entity->transform.rotation); //hashmap_quat_set(entity_data, "rotation", &entity->transform.rotation);
//switch(entity->type) switch(entity->type)
//{ {
//case ET_CAMERA: case ET_CAMERA:
//{ {
// struct Camera* camera = &entity->camera; struct Camera* camera = (struct Camera*)entity;
// hashmap_bool_set(entity_data, "ortho", camera->ortho); hashmap_bool_set(entity_data, "ortho", camera->ortho);
// hashmap_bool_set(entity_data, "resizeable", camera->resizeable); hashmap_bool_set(entity_data, "resizeable", camera->resizeable);
// hashmap_float_set(entity_data, "fov", camera->fov); hashmap_float_set(entity_data, "fov", camera->fov);
// hashmap_float_set(entity_data, "zoom", camera->zoom); hashmap_float_set(entity_data, "zoom", camera->zoom);
// hashmap_float_set(entity_data, "nearz", camera->nearz); hashmap_float_set(entity_data, "nearz", camera->nearz);
// hashmap_float_set(entity_data, "farz", camera->farz); hashmap_float_set(entity_data, "farz", camera->farz);
// hashmap_vec4_set(entity_data, "clear_color", &camera->clear_color); hashmap_vec4_set(entity_data, "clear_color", &camera->clear_color);
// if(entity->camera.fbo != -1) if(camera->fbo != -1)
// { {
// hashmap_bool_set(entity_data, "has_fbo", true); hashmap_bool_set(entity_data, "has_fbo", true);
// hashmap_int_set(entity_data, "fbo_height", framebuffer_height_get(camera->fbo)); hashmap_int_set(entity_data, "fbo_height", framebuffer_height_get(camera->fbo));
// hashmap_int_set(entity_data, "fbo_width", framebuffer_width_get(camera->fbo)); hashmap_int_set(entity_data, "fbo_width", framebuffer_width_get(camera->fbo));
// hashmap_bool_set(entity_data, "fbo_has_render_tex", camera->render_tex == -1 ? false : true); hashmap_bool_set(entity_data, "fbo_has_render_tex", camera->render_tex == -1 ? false : true);
// hashmap_bool_set(entity_data, "fbo_has_depth_tex", camera->depth_tex == -1 ? false : true); hashmap_bool_set(entity_data, "fbo_has_depth_tex", camera->depth_tex == -1 ? false : true);
// } }
// else else
// { {
// hashmap_bool_set(entity_data, "has_fbo", true); hashmap_bool_set(entity_data, "has_fbo", true);
// } }
// break; break;
//} }
//case ET_STATIC_MODEL: case ET_STATIC_MESH:
//{ {
// /* TODO: Change this after adding proper support for exported models from blender */ struct Static_Mesh* mesh = (struct Static_Mesh*)entity;
// struct Material* material = material_get(entity->model.material); struct Geometry* geom = geom_get(mesh->model.geometry_index);
// struct Geometry* geom = geom_get(entity->model.geometry_index); hashmap_int_set(entity_data, "material", mesh->model.material->type);
// hashmap_str_set(entity_data, "material", material->name); hashmap_str_set(entity_data, "geometry", geom->filename);
// hashmap_str_set(entity_data, "geometry", geom->filename); break;
// break; }
//} case ET_LIGHT:
//case ET_LIGHT: {
//{ struct Light* light = (struct Light*)entity;
// struct Light* light = &entity->light; hashmap_int_set(entity_data, "light_type", light->type);
// hashmap_int_set(entity_data, "light_type", light->type); hashmap_float_set(entity_data, "outer_angle", light->outer_angle);
// hashmap_float_set(entity_data, "outer_angle", light->outer_angle); hashmap_float_set(entity_data, "inner_angle", light->inner_angle);
// hashmap_float_set(entity_data, "inner_angle", light->inner_angle); hashmap_float_set(entity_data, "falloff", light->falloff);
// hashmap_float_set(entity_data, "falloff", light->falloff); hashmap_float_set(entity_data, "radius", light->radius);
// hashmap_float_set(entity_data, "radius", light->radius); hashmap_float_set(entity_data, "intensity", light->intensity);
// hashmap_float_set(entity_data, "intensity", light->intensity); hashmap_float_set(entity_data, "depth_bias", light->depth_bias);
// hashmap_float_set(entity_data, "depth_bias", light->depth_bias); hashmap_bool_set(entity_data, "valid", light->valid);
// hashmap_bool_set(entity_data, "valid", light->valid); hashmap_bool_set(entity_data, "cast_shadow", light->cast_shadow);
// hashmap_bool_set(entity_data, "cast_shadow", light->cast_shadow); hashmap_bool_set(entity_data, "pcf_enabled", light->pcf_enabled);
// hashmap_bool_set(entity_data, "pcf_enabled", light->pcf_enabled); hashmap_vec3_set(entity_data, "color", &light->color);
// hashmap_vec3_set(entity_data, "color", &light->color); break;
// break; }
//} case ET_SOUND_SOURCE:
//case ET_SOUND_SOURCE: {
//{ struct Sound_Source* sound_source = (struct Sound_Source*)entity;
// struct Sound_Source* sound_source = &entity->sound_source; hashmap_str_set(entity_data, "source_filename", sound_source->source_buffer->filename);
// hashmap_str_set(entity_data, "source_filename", sound_source->source_filename); hashmap_bool_set(entity_data, "playing", sound_source->playing);
// hashmap_bool_set(entity_data, "playing", sound_source->playing); hashmap_int_set(entity_data, "sound_type", sound_source->type);
// hashmap_int_set(entity_data, "sound_type", sound_source->type); hashmap_bool_set(entity_data, "loop", sound_source->loop);
// hashmap_bool_set(entity_data, "loop", sound_source->loop); hashmap_float_set(entity_data, "volume", sound_source->volume);
// hashmap_float_set(entity_data, "volume", sound_source->volume); hashmap_float_set(entity_data, "sound_min_distance", sound_source->min_distance);
// hashmap_float_set(entity_data, "sound_min_distance", sound_source->min_distance); hashmap_float_set(entity_data, "sound_max_distance", sound_source->max_distance);
// hashmap_float_set(entity_data, "sound_max_distance", sound_source->max_distance); hashmap_float_set(entity_data, "rolloff_factor", sound_source->rolloff_factor);
// hashmap_float_set(entity_data, "rolloff_factor", sound_source->rolloff_factor); hashmap_int_set(entity_data, "sound_attenuation_type", sound_source->attenuation_type);
// hashmap_int_set(entity_data, "sound_attenuation_type", sound_source->attenuation_type); break;
// break; }
//} };
//};
return true; return true;
} }
@ -209,6 +206,7 @@ bool entity_save(struct Entity* entity, const char* filename, int directory_type
if(!entity_write(entity, object)) if(!entity_write(entity, object))
{ {
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, filename);
parser_free(parser);
fclose(entity_file); fclose(entity_file);
return false; return false;
} }
@ -218,7 +216,7 @@ bool entity_save(struct Entity* entity, const char* filename, int directory_type
parser_free(parser); parser_free(parser);
fclose(entity_file); fclose(entity_file);
return false; return true;
} }
struct Entity* entity_read(struct Parser_Object* object) struct Entity* entity_read(struct Parser_Object* object)

@ -8,7 +8,6 @@
#include "material.h" #include "material.h"
#define MAX_ENTITY_NAME_LEN 128 #define MAX_ENTITY_NAME_LEN 128
#define MAX_SOUND_SOURCE_BUFFERS 5
struct Entity; struct Entity;

@ -77,6 +77,7 @@ bool game_init(struct Window* window, struct Hashmap* cvars)
game_state->editor = calloc(1, sizeof(*game_state->editor)); game_state->editor = calloc(1, sizeof(*game_state->editor));
game_state->gui = calloc(1, sizeof(*game_state->gui)); game_state->gui = calloc(1, sizeof(*game_state->gui));
game_state->event_manager = calloc(1, sizeof(*game_state->event_manager)); game_state->event_manager = calloc(1, sizeof(*game_state->event_manager));
game_state->sound = calloc(1, sizeof(*game_state->sound));
log_message_callback_set(game_on_log_message); log_message_callback_set(game_on_log_message);
log_warning_callback_set(game_on_log_warning); log_warning_callback_set(game_on_log_warning);
@ -105,6 +106,7 @@ bool game_init(struct Window* window, struct Hashmap* cvars)
physics_gravity_set(0.f, -9.8f, 0.f); physics_gravity_set(0.f, -9.8f, 0.f);
physics_body_set_moved_callback(entity_rigidbody_on_move); physics_body_set_moved_callback(entity_rigidbody_on_move);
physics_body_set_collision_callback(entity_rigidbody_on_collision); physics_body_set_collision_callback(entity_rigidbody_on_collision);
sound_init(game_state->sound);
scene_init(game_state->scene); scene_init(game_state->scene);
editor_init(game_state->editor); editor_init(game_state->editor);
@ -545,7 +547,7 @@ void game_post_update(float dt)
{ {
input_post_update(); input_post_update();
scene_post_update(game_state->scene); scene_post_update(game_state->scene);
sound_update_3d(); sound_update_3d(game_state->sound);
} }
void game_debug_gui(float dt) void game_debug_gui(float dt)
@ -1887,6 +1889,7 @@ void game_cleanup(void)
framebuffer_cleanup(); framebuffer_cleanup();
texture_cleanup(); texture_cleanup();
shader_cleanup(); shader_cleanup();
sound_cleanup(game_state->sound);
event_manager_cleanup(game_state->event_manager); event_manager_cleanup(game_state->event_manager);
free(game_state->editor); free(game_state->editor);
@ -1895,6 +1898,7 @@ void game_cleanup(void)
free(game_state->renderer); free(game_state->renderer);
free(game_state->event_manager); free(game_state->event_manager);
free(game_state->gui); free(game_state->gui);
free(game_state->sound);
} }
free(game_state); free(game_state);
game_state = NULL; game_state = NULL;

@ -13,6 +13,7 @@ struct Gui;
struct Event_Manager; struct Event_Manager;
struct Editor; struct Editor;
struct Hashmap; struct Hashmap;
struct Sound;
enum Game_Mode enum Game_Mode
{ {
@ -32,6 +33,7 @@ struct Game_State
struct Event_Manager* event_manager; struct Event_Manager* event_manager;
struct Editor* editor; struct Editor* editor;
struct Hashmap* cvars; struct Hashmap* cvars;
struct Sound* sound;
}; };

@ -130,12 +130,14 @@ bool material_register_static_mesh(struct Material* material, struct Static_Mesh
variant_assign_int(&mesh->model.material_params[MMP_DIFFUSE_TEX], texture_create_from_file("default.tga", TU_DIFFUSE)); variant_assign_int(&mesh->model.material_params[MMP_DIFFUSE_TEX], texture_create_from_file("default.tga", TU_DIFFUSE));
variant_assign_float(&mesh->model.material_params[MMP_SPECULAR], 1.f); variant_assign_float(&mesh->model.material_params[MMP_SPECULAR], 1.f);
variant_assign_float(&mesh->model.material_params[MMP_SPECULAR_STRENGTH], 50.f); variant_assign_float(&mesh->model.material_params[MMP_SPECULAR_STRENGTH], 50.f);
mesh->model.material = material;
} }
break; break;
case MAT_UNSHADED: case MAT_UNSHADED:
{ {
variant_assign_vec4f(&mesh->model.material_params[MMP_DIFFUSE_COL], 1.f, 0.f, 1.f, 1.f); variant_assign_vec4f(&mesh->model.material_params[MMP_DIFFUSE_COL], 1.f, 0.f, 1.f, 1.f);
variant_assign_int(&mesh->model.material_params[MMP_DIFFUSE_TEX], texture_create_from_file("default.tga", TU_DIFFUSE)); variant_assign_int(&mesh->model.material_params[MMP_DIFFUSE_TEX], texture_create_from_file("default.tga", TU_DIFFUSE));
mesh->model.material = material;
} }
break; break;
default: default:

@ -18,15 +18,15 @@ void model_init(struct Model* model, struct Static_Mesh* mesh, const char* geome
assert(model && material_type > -1 && material_type < MAT_MAX); assert(model && material_type > -1 && material_type < MAT_MAX);
/* if no name is given for geometry, use default */ /* if no name is given for geometry, use default */
int geo_index = geom_create_from_file(geometry_name ? geometry_name : "default.pamesh"); int geo_index = geom_create_from_file(geometry_name ? geometry_name : "default.symbres");
if(geo_index == -1) if(geo_index == -1)
{ {
log_error("model:init", "Failed to load model %s", geometry_name); log_error("model:init", "Failed to load model %s", geometry_name);
geo_index = geom_create_from_file("default.pamesh"); geo_index = geom_create_from_file("default.symbres");
if(geo_index == -1) if(geo_index == -1)
{ {
log_error("model:init", "Could not load default model 'default.pamesh' "); log_error("model:init", "Could not load default model 'default.symbres' ");
return; return;
} }
} }
@ -35,7 +35,7 @@ void model_init(struct Model* model, struct Static_Mesh* mesh, const char* geome
struct Material* material = &game_state_get()->renderer->materials[material_type]; struct Material* material = &game_state_get()->renderer->materials[material_type];
if(!material_register_static_mesh(material, mesh)) if(!material_register_static_mesh(material, mesh))
{ {
log_error("model:create", "Unable to register model with Unshaded material, component not added"); log_error("model:create", "Unable to register model with Unshaded material");
model_reset(model, mesh); model_reset(model, mesh);
} }
} }

@ -96,6 +96,7 @@ void scene_update(struct Scene* scene, float dt)
void scene_post_update(struct Scene* scene) void scene_post_update(struct Scene* scene)
{ {
assert(scene); assert(scene);
struct Sound* sound = game_state_get()->sound;
for(int i = 0; i < MAX_ENTITIES; i++) for(int i = 0; i < MAX_ENTITIES; i++)
{ {
@ -144,7 +145,7 @@ void scene_post_update(struct Scene* scene)
{ {
vec3 abs_pos = { 0.f, 0.f, 0.f }; vec3 abs_pos = { 0.f, 0.f, 0.f };
transform_get_absolute_position(&sound_source->base, &abs_pos); transform_get_absolute_position(&sound_source->base, &abs_pos);
sound_source_instance_update_position(sound_source->source_instance, abs_pos.x, abs_pos.y, abs_pos.z); sound_source_instance_update_position(sound, sound_source->source_instance, abs_pos);
sound_source->base.transform.is_modified = false; sound_source->base.transform.is_modified = false;
} }
} }
@ -198,16 +199,7 @@ void scene_post_update(struct Scene* scene)
if(scene->player.base.transform.is_modified) if(scene->player.base.transform.is_modified)
{ {
vec3 abs_pos = { 0.f, 0.f, 0.f }; sound_listener_update(sound);
vec3 abs_fwd = { 0.f, 0.f, -1.f };
vec3 abs_up = { 0.f, 1.f, 0.f };
transform_get_absolute_position(&scene->player, &abs_pos);
transform_get_absolute_forward(&scene->player, &abs_fwd);
transform_get_absolute_up(&scene->player, &abs_up);
sound_listener_update(abs_pos.x, abs_pos.y, abs_pos.z,
abs_fwd.x, abs_fwd.y, abs_fwd.z,
abs_up.x, abs_up.y, abs_up.z);
scene->player.base.transform.is_modified = false; scene->player.base.transform.is_modified = false;
} }
} }
@ -329,6 +321,7 @@ struct Static_Mesh* scene_static_mesh_create(struct Scene* scene, const char* na
struct Sound_Source* scene_sound_source_create(struct Scene* scene, const char* name, struct Entity* parent, const char* filename, int type, bool loop, bool play) struct Sound_Source* scene_sound_source_create(struct Scene* scene, const char* name, struct Entity* parent, const char* filename, int type, bool loop, bool play)
{ {
assert(scene && filename); assert(scene && filename);
struct Sound* sound = game_state_get()->sound;
struct Sound_Source* new_sound_source = NULL; struct Sound_Source* new_sound_source = NULL;
for(int i = 0; i < MAX_SOUND_SOURCES; i++) for(int i = 0; i < MAX_SOUND_SOURCES; i++)
{ {
@ -346,7 +339,7 @@ struct Sound_Source* scene_sound_source_create(struct Scene* scene, const char*
new_sound_source->base.type = ET_SOUND_SOURCE; new_sound_source->base.type = ET_SOUND_SOURCE;
struct Entity* entity = &new_sound_source->base; struct Entity* entity = &new_sound_source->base;
new_sound_source->source_buffer = sound_source_create(filename, type); new_sound_source->source_buffer = sound_source_create(sound, filename, type);
if(!new_sound_source->source_buffer) if(!new_sound_source->source_buffer)
{ {
log_error("entity:sync_sound_params", "Failed to load file '%s' to provide sound source for entity %s", filename, entity->name); log_error("entity:sync_sound_params", "Failed to load file '%s' to provide sound source for entity %s", filename, entity->name);
@ -354,14 +347,10 @@ struct Sound_Source* scene_sound_source_create(struct Scene* scene, const char*
return new_sound_source; return new_sound_source;
} }
new_sound_source->source_instance = sound_source_instance_create(new_sound_source->source_buffer, true); new_sound_source->source_instance = sound_source_instance_create(sound, new_sound_source->source_buffer, true);
vec3 abs_pos = { 0.f, 0.f, 0.f }; vec3 abs_pos = { 0.f, 0.f, 0.f };
vec3 abs_fwd = { 0.f, 0.f, -1.f };
vec3 abs_up = { 0.f, 1.f, 0.f };
transform_get_absolute_position(entity, &abs_pos); transform_get_absolute_position(entity, &abs_pos);
transform_get_absolute_forward(entity, &abs_fwd); sound_source_instance_update_position(sound, new_sound_source->source_instance, abs_pos);
transform_get_absolute_up(entity, &abs_up);
sound_source_instance_update_position(new_sound_source->source_instance, abs_pos.x, abs_pos.y, abs_pos.z);
new_sound_source->loop = loop; new_sound_source->loop = loop;
new_sound_source->min_distance = 0.f; new_sound_source->min_distance = 0.f;
@ -372,13 +361,13 @@ struct Sound_Source* scene_sound_source_create(struct Scene* scene, const char*
new_sound_source->volume = 1.f; new_sound_source->volume = 1.f;
new_sound_source->type = type; new_sound_source->type = type;
sound_source_instance_loop_set(new_sound_source->source_instance, new_sound_source->loop); sound_source_instance_loop_set(sound, new_sound_source->source_instance, new_sound_source->loop);
sound_source_instance_min_max_distance_set(new_sound_source->source_instance, new_sound_source->min_distance, new_sound_source->max_distance); sound_source_instance_min_max_distance_set(sound, new_sound_source->source_instance, new_sound_source->min_distance, new_sound_source->max_distance);
sound_source_instance_attenuation_set(new_sound_source->source_instance, new_sound_source->attenuation_type, new_sound_source->rolloff_factor); sound_source_instance_attenuation_set(sound, new_sound_source->source_instance, new_sound_source->attenuation_type, new_sound_source->rolloff_factor);
sound_source_instance_volume_set(new_sound_source->source_instance, new_sound_source->volume); sound_source_instance_volume_set(sound, new_sound_source->source_instance, new_sound_source->volume);
sound_update_3d(); sound_update_3d(sound);
if(new_sound_source->playing) sound_source_instance_play(new_sound_source->source_instance); if(new_sound_source->playing) sound_source_instance_play(sound, new_sound_source->source_instance);
} }
else else
{ {
@ -429,7 +418,7 @@ void scene_sound_source_remove(struct Scene* scene, struct Sound_Source* source)
{ {
assert(scene && source); assert(scene && source);
sound_source_instance_destroy(source->source_instance); sound_source_instance_destroy(game_state_get()->sound, source->source_instance);
source->source_instance = 0; source->source_instance = 0;
scene_entity_remove(scene, &source->base); scene_entity_remove(scene, &source->base);
} }
@ -456,7 +445,7 @@ struct Light* scene_light_find(struct Scene* scene, const char* name)
assert(scene && name); assert(scene && name);
struct Light* light = NULL; struct Light* light = NULL;
for(int i = 0; i < MAX_ENTITIES; i++) for(int i = 0; i < MAX_LIGHTS; i++)
{ {
if(strncmp(name, scene->lights[i].base.name, MAX_ENTITY_NAME_LEN) == 0) if(strncmp(name, scene->lights[i].base.name, MAX_ENTITY_NAME_LEN) == 0)
{ {
@ -473,7 +462,7 @@ struct Camera* scene_camera_find(struct Scene* scene, const char* name)
assert(scene && name); assert(scene && name);
struct Camera* camera = NULL; struct Camera* camera = NULL;
for(int i = 0; i < MAX_ENTITIES; i++) for(int i = 0; i < MAX_CAMERAS; i++)
{ {
if(strncmp(name, scene->cameras[i].base.name, MAX_ENTITY_NAME_LEN) == 0) if(strncmp(name, scene->cameras[i].base.name, MAX_ENTITY_NAME_LEN) == 0)
{ {
@ -490,7 +479,7 @@ struct Static_Mesh* scene_static_mesh_find(struct Scene* scene, const char* name
assert(scene && name); assert(scene && name);
struct Static_Mesh* static_mesh = NULL; struct Static_Mesh* static_mesh = NULL;
for(int i = 0; i < MAX_ENTITIES; i++) for(int i = 0; i < MAX_STATIC_MESHES; i++)
{ {
if(strncmp(name, scene->static_meshes[i].base.name, MAX_ENTITY_NAME_LEN) == 0) if(strncmp(name, scene->static_meshes[i].base.name, MAX_ENTITY_NAME_LEN) == 0)
{ {
@ -507,7 +496,7 @@ struct Sound_Source* scene_sound_source_find(struct Scene* scene, const char* na
assert(scene && name); assert(scene && name);
struct Sound_Source* sound_source = NULL; struct Sound_Source* sound_source = NULL;
for(int i = 0; i < MAX_ENTITIES; i++) for(int i = 0; i < MAX_SOUND_SOURCES; i++)
{ {
if(strncmp(name, scene->sound_sources[i].base.name, MAX_ENTITY_NAME_LEN) == 0) if(strncmp(name, scene->sound_sources[i].base.name, MAX_ENTITY_NAME_LEN) == 0)
{ {

@ -4,6 +4,8 @@
#include <stdbool.h> #include <stdbool.h>
#include <stdio.h> #include <stdio.h>
#define MAX_FILENAME_LEN 128
enum Directory_Type enum Directory_Type
{ {
DIRT_USER, /* User directory or preferences directory */ DIRT_USER, /* User directory or preferences directory */

@ -87,12 +87,6 @@ bool init(void)
return false; return false;
} }
if(!sound_init())
{
log_error("main:init", "Failed to initialize sound");
return false;
}
return true; return true;
} }
@ -101,7 +95,6 @@ void cleanup(void)
game_cleanup(); game_cleanup();
if(window) window_destroy(window); if(window) window_destroy(window);
log_reset_all_callbacks(); // Now that the game library has been unloaded, reset all callbacks to stubs so we don't crash on exit log_reset_all_callbacks(); // Now that the game library has been unloaded, reset all callbacks to stubs so we don't crash on exit
sound_cleanup();
platform_unload_gl(); platform_unload_gl();
platform_cleanup(); platform_cleanup();
config_vars_cleanup(cvars); config_vars_cleanup(cvars);

@ -4,125 +4,114 @@
#include "../common/hashmap.h" #include "../common/hashmap.h"
#include "../common/variant.h" #include "../common/variant.h"
#include "../common/string_utils.h" #include "../common/string_utils.h"
#include "file_io.h"
#include "../game/entity.h"
#include "../game/transform.h"
#include <assert.h> #include <assert.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h>
#include <soloud_c.h> #include <soloud_c.h>
struct Sound_Source_Buffer bool sound_init(struct Sound* sound)
{
int type;
union
{
Wav* wav;
WavStream* wavstream;
};
};
struct Sound_State
{
Soloud* soloud_context;
float volume;
};
static struct Sound_State sound_state =
{ {
.soloud_context = NULL, sound->soloud_context = Soloud_create();
.volume = 1.f
};
static struct Hashmap* sound_sources = NULL; if(!sound->soloud_context)
bool sound_init(void)
{
sound_sources = hashmap_new();
sound_state.soloud_context = Soloud_create();
if(!sound_state.soloud_context)
{ {
log_error("sound:init", "Failed to create sound context"); log_error("sound:init", "Failed to create sound context");
return false; return false;
} }
Soloud_initEx(sound_state.soloud_context, SOLOUD_CLIP_ROUNDOFF | SOLOUD_ENABLE_VISUALIZATION, SOLOUD_SDL2, SOLOUD_AUTO, SOLOUD_AUTO, SOLOUD_AUTO); Soloud_initEx(sound->soloud_context, SOLOUD_CLIP_ROUNDOFF | SOLOUD_ENABLE_VISUALIZATION, SOLOUD_SDL2, SOLOUD_AUTO, SOLOUD_AUTO, SOLOUD_AUTO);
Soloud_setGlobalVolume(sound_state.soloud_context, 4); Soloud_setGlobalVolume(sound->soloud_context, 4);
Soloud_set3dListenerParameters(sound_state.soloud_context, Soloud_set3dListenerParameters(sound->soloud_context,
0.f, 0.f, 0.f, // Position 0.f, 0.f, 0.f, // Position
0.f, 0.f, -1.f, // At 0.f, 0.f, -1.f, // At
0.f, 1.f, 0.f); // Up 0.f, 1.f, 0.f); // Up
sound->listener = NULL;
log_message("Sound initialized with %s", Soloud_getBackendString(sound->soloud_context));
log_message("Sound initialized with %s", Soloud_getBackendString(sound_state.soloud_context)); for(int i = 0; i < MAX_SOUND_BUFFERS; i++)
{
sound->sound_buffers[i].type = ST_NONE;
memset(sound->sound_buffers[i].filename, '\0', MAX_FILENAME_LEN);
sound->sound_buffers[i].wav = NULL;
}
return true; return true;
} }
void sound_listener_update(float apos_x, float apos_y, float apos_z, void sound_listener_update(struct Sound* sound)
float afwd_x, float afwd_y, float afwd_z,
float aup_x, float aup_y, float aup_z)
{ {
Soloud_set3dListenerParameters(sound_state.soloud_context, vec3 position = { 0.f, 0.f, 0.f };
apos_x, apos_y, apos_z, // Position vec3 at = { 0.f, 0.f, -1.f };
afwd_x, afwd_y, afwd_z, // At vec3 up = { 0.f, 1.f, 0.f };
aup_x, aup_y, aup_z); // Up if(sound->listener)
{
transform_get_absolute_position(sound->listener, &position);
transform_get_absolute_forward(sound->listener, &at);
transform_get_absolute_up(sound->listener, &up);
}
Soloud_set3dListenerParameters(sound->soloud_context,
position.x, position.y, position.z, // Position
at.x, at.y, at.z, // At
up.x, up.y, up.z); // Up
} }
void sound_volume_set(float volume) void sound_master_volume_set(struct Sound* sound, float volume)
{ {
if(volume < 0.f) volume = 0.f; if(volume < 0.f) volume = 0.f;
sound_state.volume = volume; sound->master_volume = volume;
Soloud_setGlobalVolume(sound_state.soloud_context, sound_state.volume); Soloud_setGlobalVolume(sound->soloud_context, sound->master_volume);
} }
void sound_update_3d(void) void sound_update_3d(struct Sound* sound)
{ {
Soloud_update3dAudio(sound_state.soloud_context); Soloud_update3dAudio(sound->soloud_context);
} }
void sound_cleanup(void) void sound_cleanup(struct Sound* sound)
{ {
char* key = NULL; for(int i = 0; i < MAX_SOUND_BUFFERS; i++ )
struct Variant* value = NULL;
HASHMAP_FOREACH(sound_sources, key, value)
{ {
struct Sound_Source_Buffer* source = value->val_voidptr; struct Sound_Source_Buffer* source = &sound->sound_buffers[i];
sound_source_stop_all(source); sound_source_stop_all(sound, source);
switch(source->type) switch(source->type)
{ {
case ST_WAV: Wav_destroy(source->wav); break; case ST_WAV: Wav_destroy(source->wav); break;
case ST_WAV_STREAM: WavStream_destroy(source->wavstream); break; case ST_WAV_STREAM: WavStream_destroy(source->wavstream); break;
} }
free(source);
} }
hashmap_free(sound_sources); Soloud_deinit(sound->soloud_context);
Soloud_deinit(sound_state.soloud_context); Soloud_destroy(sound->soloud_context);
Soloud_destroy(sound_state.soloud_context);
sound_state.volume = 0.f; sound->master_volume = 0.f;
sound_state.soloud_context = NULL; sound->soloud_context = NULL;
} }
void sound_source_instance_destroy(uint source_instance) void sound_source_instance_destroy(struct Sound* sound, uint source_instance)
{ {
Soloud_stop(sound_state.soloud_context, source_instance); Soloud_stop(sound->soloud_context, source_instance);
} }
void sound_source_instance_update_position(uint source_instance, float apos_x, float apos_y, float apos_z) void sound_source_instance_update_position(struct Sound* sound, uint source_instance, vec3 abs_pos)
{ {
Soloud_set3dSourceParameters(sound_state.soloud_context, source_instance, apos_x, apos_y, apos_z); Soloud_set3dSourceParameters(sound->soloud_context, source_instance, abs_pos.x, abs_pos.y, abs_pos.z);
} }
uint sound_source_instance_create(struct Sound_Source_Buffer* source, bool is3d) uint sound_source_instance_create(struct Sound* sound, struct Sound_Source_Buffer* source, bool is3d)
{ {
assert(source); assert(source);
uint source_instance = 0; uint source_instance = 0;
if(is3d) if(is3d)
{ {
source_instance = Soloud_play3dEx(sound_state.soloud_context, source_instance = Soloud_play3dEx(sound->soloud_context,
source->type == ST_WAV ? source->wav : source->wavstream, source->type == ST_WAV ? source->wav : source->wavstream,
0.f, 0.f, 0.f, 0.f, 0.f, 0.f,
0.f, 0.f, 0.f, 0.f, 0.f, 0.f,
@ -132,88 +121,104 @@ uint sound_source_instance_create(struct Sound_Source_Buffer* source, bool is3d)
} }
else else
{ {
source_instance = Soloud_playEx(sound_state.soloud_context, source->type == ST_WAV ? source->wav : source->wavstream, 1.f, 0.0f, true, 0); source_instance = Soloud_playEx(sound->soloud_context, source->type == ST_WAV ? source->wav : source->wavstream, 1.f, 0.0f, true, 0);
} }
return source_instance; return source_instance;
} }
void sound_source_instance_volume_set(uint source_instance, float volume) void sound_source_instance_volume_set(struct Sound* sound, uint source_instance, float volume)
{ {
if(volume < 0.f) volume = 0.f; if(volume < 0.f) volume = 0.f;
Soloud_setVolume(sound_state.soloud_context, source_instance, volume); Soloud_setVolume(sound->soloud_context, source_instance, volume);
} }
void sound_source_instance_loop_set(uint source_instance, bool loop) void sound_source_instance_loop_set(struct Sound* sound, uint source_instance, bool loop)
{ {
Soloud_setLooping(sound_state.soloud_context, source_instance, loop); Soloud_setLooping(sound->soloud_context, source_instance, loop);
} }
void sound_source_instance_play(uint source_instance) void sound_source_instance_play(struct Sound* sound, uint source_instance)
{ {
Soloud_setPause(sound_state.soloud_context, source_instance, false); Soloud_setPause(sound->soloud_context, source_instance, false);
} }
void sound_source_instance_pause(uint source_instance) void sound_source_instance_pause(struct Sound* sound, uint source_instance)
{ {
Soloud_setPause(sound_state.soloud_context, source_instance, true); Soloud_setPause(sound->soloud_context, source_instance, true);
} }
void sound_source_instance_rewind(uint source_instance) void sound_source_instance_rewind(struct Sound* sound, uint source_instance)
{ {
Soloud_seek(sound_state.soloud_context, source_instance, 0.0); Soloud_seek(sound->soloud_context, source_instance, 0.0);
} }
void sound_source_instance_stop(uint source_instance) void sound_source_instance_stop(struct Sound* sound, uint source_instance)
{ {
Soloud_stop(sound_state.soloud_context, source_instance); Soloud_stop(sound->soloud_context, source_instance);
} }
void sound_source_instance_min_max_distance_set(uint source_instance, float min_distance, float max_distance) void sound_source_instance_min_max_distance_set(struct Sound* sound, uint source_instance, float min_distance, float max_distance)
{ {
Soloud_set3dSourceMinMaxDistance(sound_state.soloud_context, source_instance, min_distance, max_distance); Soloud_set3dSourceMinMaxDistance(sound->soloud_context, source_instance, min_distance, max_distance);
} }
void sound_source_instance_attenuation_set(uint source_instance, int attenuation_type, float rolloff_factor) void sound_source_instance_attenuation_set(struct Sound* sound, uint source_instance, int attenuation_type, float rolloff_factor)
{ {
Soloud_set3dSourceAttenuation(sound_state.soloud_context, source_instance, attenuation_type, rolloff_factor); Soloud_set3dSourceAttenuation(sound->soloud_context, source_instance, attenuation_type, rolloff_factor);
} }
float sound_source_instance_volume_get(uint source_instance) float sound_source_instance_volume_get(struct Sound* sound, uint source_instance)
{ {
return Soloud_getVolume(sound_state.soloud_context, source_instance); return Soloud_getVolume(sound->soloud_context, source_instance);
} }
bool sound_source_instance_loop_get(uint source_instance) bool sound_source_instance_loop_get(struct Sound* sound, uint source_instance)
{ {
return Soloud_getLooping(sound_state.soloud_context, source_instance); return Soloud_getLooping(sound->soloud_context, source_instance);
} }
bool sound_source_instance_is_paused(uint source_instance) bool sound_source_instance_is_paused(struct Sound* sound, uint source_instance)
{ {
return Soloud_getPause(sound_state.soloud_context, source_instance); return Soloud_getPause(sound->soloud_context, source_instance);
} }
struct Sound_Source_Buffer* sound_source_create(const char* filename, int type) struct Sound_Source_Buffer* sound_source_create(struct Sound* sound, const char* filename, int type)
{ {
if(!filename) return NULL; if(!filename)
return NULL;
struct Sound_Source_Buffer* source = NULL; struct Sound_Source_Buffer* source = sound_source_get(sound, filename);
// See if we've already loaded this file otherwise, get the next empty slot.
// If we can't find an empty slot, print error and return NULL
if(source)
{
return source;
}
else
{
for(int i = 0; i < MAX_SOUND_BUFFERS; i++)
{
if(sound->sound_buffers[i].type == ST_NONE)
{
source = &sound->sound_buffers[i];
break;
}
}
//See if we've already loaded this file if(!source)
if(hashmap_value_exists(sound_sources, filename))
{ {
source = (struct Sound_Source_Buffer*)hashmap_ptr_get(sound_sources, filename); log_error("sound:source_create", "Could not find empty sound source slot for '%s'", filename);
return source; return source;
} }
}
long size = 0L; long size = 0L;
unsigned char* memory = io_file_read(DIRT_INSTALL, filename, "rb", &size); unsigned char* memory = io_file_read(DIRT_INSTALL, filename, "rb", &size);
if(!memory)
source = malloc(sizeof(*source));
if(!source)
{ {
log_error("sound:source_create", "Out of memory!"); log_error("sound:source_create", "Failed to read file");
return NULL; return source;
} }
switch(type) switch(type)
@ -224,8 +229,8 @@ struct Sound_Source_Buffer* sound_source_create(const char* filename, int type)
int rc = Wav_loadMem(wave, memory, (uint)size); int rc = Wav_loadMem(wave, memory, (uint)size);
if(rc != 0) if(rc != 0)
{ {
log_error("sound:source_create", "Failed to load %s, Soloud: %s", filename, Soloud_getErrorString(sound_state.soloud_context, rc)); log_error("sound:source_create", "Failed to load %s, Soloud: %s", filename, Soloud_getErrorString(sound->soloud_context, rc));
free(source); free(memory);
return 0; return 0;
} }
source->type = ST_WAV; source->type = ST_WAV;
@ -238,8 +243,8 @@ struct Sound_Source_Buffer* sound_source_create(const char* filename, int type)
int rc = WavStream_loadMem(wave_stream, memory, (uint)size); int rc = WavStream_loadMem(wave_stream, memory, (uint)size);
if(rc != 0) if(rc != 0)
{ {
log_error("sound:source_create", "Failed to load %s, Soloud: %s", filename, Soloud_getErrorString(sound_state.soloud_context, rc)); log_error("sound:source_create", "Failed to load %s, Soloud: %s", filename, Soloud_getErrorString(sound->soloud_context, rc));
free(source); free(memory);
return 0; return 0;
} }
source->type = ST_WAV_STREAM; source->type = ST_WAV_STREAM;
@ -248,38 +253,45 @@ struct Sound_Source_Buffer* sound_source_create(const char* filename, int type)
default: log_error("sound:source_create", "Invalid source type %d", type); break; default: log_error("sound:source_create", "Invalid source type %d", type); break;
} }
hashmap_ptr_set(sound_sources, filename, (void*)source); strncpy(source->filename, filename, MAX_FILENAME_LEN);
if(memory) free(memory); free(memory);
return source; return source;
} }
struct Sound_Source_Buffer* sound_source_get(const char* name) struct Sound_Source_Buffer* sound_source_get(struct Sound* sound, const char* name)
{ {
struct Sound_Source_Buffer* source = NULL; struct Sound_Source_Buffer* source = NULL;
if(hashmap_value_exists(sound_sources, name)) for(int i = 0; i < MAX_SOUND_BUFFERS; i++)
{
if(sound->sound_buffers[i].type == ST_NONE)
continue;
if(strncmp(name, sound->sound_buffers[i].filename, MAX_FILENAME_LEN) == 0)
{ {
source = (struct Sound_Source_Buffer*)hashmap_ptr_get(sound_sources, name); source = &sound->sound_buffers[i];
break;
}
} }
return source; return source;
} }
void sound_source_destroy(const char* name) void sound_source_destroy(struct Sound* sound, const char* name)
{ {
struct Sound_Source_Buffer* source = sound_source_get(name); struct Sound_Source_Buffer* source = sound_source_get(sound, name);
if(source) if(source)
{ {
sound_source_stop_all(source); sound_source_stop_all(sound, source);
switch(source->type) switch(source->type)
{ {
case ST_WAV: Wav_destroy(source->wav); break; case ST_WAV: Wav_destroy(source->wav); source->wav = NULL; break;
case ST_WAV_STREAM: WavStream_destroy(source->wavstream); break; case ST_WAV_STREAM: WavStream_destroy(source->wavstream); source->wavstream = NULL; break;
} }
free(source); source->type = ST_NONE;
memset(source->filename, '\0', MAX_FILENAME_LEN);
} }
hashmap_value_remove(sound_sources, name);
} }
void sound_source_volume_set(struct Sound_Source_Buffer* source, float volume) void sound_source_volume_set(struct Sound* sound, struct Sound_Source_Buffer* source, float volume)
{ {
assert(source); assert(source);
switch(source->type) switch(source->type)
@ -289,7 +301,7 @@ void sound_source_volume_set(struct Sound_Source_Buffer* source, float volume)
} }
} }
void sound_source_loop_set(struct Sound_Source_Buffer* source, bool loop) void sound_source_loop_set(struct Sound* sound, struct Sound_Source_Buffer* source, bool loop)
{ {
assert(source); assert(source);
switch(source->type) switch(source->type)
@ -299,17 +311,17 @@ void sound_source_loop_set(struct Sound_Source_Buffer* source, bool loop)
} }
} }
void sound_source_stop_all(struct Sound_Source_Buffer* source) void sound_source_stop_all(struct Sound* sound, struct Sound_Source_Buffer* source)
{ {
assert(source); assert(source);
switch(source->type) switch(source->type)
{ {
case ST_WAV: Soloud_stopAudioSource(sound_state.soloud_context, source->wav); break; case ST_WAV: Soloud_stopAudioSource(sound->soloud_context, source->wav); break;
case ST_WAV_STREAM: Soloud_stopAudioSource(sound_state.soloud_context, source->wavstream); break; case ST_WAV_STREAM: Soloud_stopAudioSource(sound->soloud_context, source->wavstream); break;
} }
} }
void sound_source_min_max_distance_set(struct Sound_Source_Buffer* source, float min_distance, float max_distance) void sound_source_min_max_distance_set(struct Sound* sound, struct Sound_Source_Buffer* source, float min_distance, float max_distance)
{ {
assert(source); assert(source);
switch(source->type) switch(source->type)

@ -2,10 +2,19 @@
#define SOUND_H #define SOUND_H
#include "../common/num_types.h" #include "../common/num_types.h"
#include "../common/linmath.h"
#include "file_io.h"
#define MAX_SOUND_BUFFERS 128
typedef void* Wav;
typedef void* WavStream;
typedef void* Soloud;
enum Sound_Source_Type enum Sound_Source_Type
{ {
ST_WAV = 0, ST_NONE = 0,
ST_WAV,
ST_WAV_STREAM ST_WAV_STREAM
}; };
@ -17,40 +26,53 @@ enum Sound_Attenuation_Type
SA_EXPONENTIAL // Exponential distance attenuation model SA_EXPONENTIAL // Exponential distance attenuation model
}; };
struct Sound_Source_Buffer; struct Sound_Source_Buffer
{
bool sound_init(void); int type;
void sound_cleanup(void); char filename[MAX_FILENAME_LEN];
void sound_volume_set(float volume); union
void sound_update_3d(void); {
Wav* wav;
void sound_listener_update(float apos_x, float apos_y, float apos_z, WavStream* wavstream;
float afwd_x, float afwd_y, float afwd_z, };
float aup_x, float aup_y, float aup_z); };
struct Sound
void sound_source_instance_update_position(uint source_instance, float apos_x, float apos_y, float apos_z); {
uint sound_source_instance_create(struct Sound_Source_Buffer* source, bool is3d); Soloud* soloud_context;
void sound_source_instance_destroy(uint source_instance); struct Entity* listener;
void sound_source_instance_volume_set(uint source_instance, float volume); float master_volume;
void sound_source_instance_loop_set(uint source_instance, bool loop); struct Sound_Source_Buffer sound_buffers[MAX_SOUND_BUFFERS];
void sound_source_instance_play(uint source_instance); };
void sound_source_instance_pause(uint source_instance);
void sound_source_instance_rewind(uint source_instance); bool sound_init(struct Sound* sound);
void sound_source_instance_stop(uint source_instance); void sound_cleanup(struct Sound* sound);
void sound_source_instance_min_max_distance_set(uint source_instance, float min_distance, float max_distance); void sound_master_volume_set(struct Sound* sound, float volume);
void sound_source_instance_attenuation_set(uint source_instance, int attenuation_type, float rolloff_factor); void sound_update_3d(struct Sound* sound);
void sound_listener_update(struct Sound* sound);
float sound_source_instance_volume_get(uint source_instance);
bool sound_source_instance_loop_get(uint source_instance); void sound_source_instance_update_position(struct Sound* sound, uint source_instance, vec3 abs_pos);
bool sound_source_instance_is_paused(uint source_instance); uint sound_source_instance_create(struct Sound* sound, struct Sound_Source_Buffer* source, bool is3d);
void sound_source_instance_destroy(struct Sound* sound, uint source_instance);
struct Sound_Source_Buffer* sound_source_create(const char* filename, int type); void sound_source_instance_volume_set(struct Sound* sound, uint source_instance, float volume);
struct Sound_Source_Buffer* sound_source_get(const char* name); void sound_source_instance_loop_set(struct Sound* sound, uint source_instance, bool loop);
void sound_source_destroy(const char* buffer_name); void sound_source_instance_play(struct Sound* sound, uint source_instance);
void sound_source_volume_set(struct Sound_Source_Buffer* source, float volume); void sound_source_instance_pause(struct Sound* sound, uint source_instance);
void sound_source_loop_set(struct Sound_Source_Buffer* source, bool loop); void sound_source_instance_rewind(struct Sound* sound, uint source_instance);
void sound_source_stop_all(struct Sound_Source_Buffer* source); void sound_source_instance_stop(struct Sound* sound, uint source_instance);
void sound_source_min_max_distance_set(struct Sound_Source_Buffer* source, float min_distance, float max_distance); void sound_source_instance_min_max_distance_set(struct Sound* sound, uint source_instance, float min_distance, float max_distance);
void sound_source_instance_attenuation_set(struct Sound* sound, uint source_instance, int attenuation_type, float rolloff_factor);
float sound_source_instance_volume_get(struct Sound* sound, uint source_instance);
bool sound_source_instance_loop_get(struct Sound* sound, uint source_instance);
bool sound_source_instance_is_paused(struct Sound* sound, uint source_instance);
struct Sound_Source_Buffer* sound_source_create(struct Sound* sound, const char* filename, int type);
struct Sound_Source_Buffer* sound_source_get(struct Sound* sound, const char* name);
void sound_source_destroy(struct Sound* sound, const char* buffer_name);
void sound_source_volume_set(struct Sound* sound, struct Sound_Source_Buffer* source, float volume);
void sound_source_loop_set(struct Sound* sound, struct Sound_Source_Buffer* source, bool loop);
void sound_source_stop_all(struct Sound* sound, struct Sound_Source_Buffer* source);
void sound_source_min_max_distance_set(struct Sound* sound, struct Sound_Source_Buffer* source, float min_distance, float max_distance);
#endif #endif

@ -50,6 +50,8 @@ Todo:
- 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
- Recompile Soloud on windows to use static sdl2 backend - Recompile Soloud on windows to use static sdl2 backend
- Refactor input sub-system to be the same as other sub-systems embedded
in game state.
- Figure out a way to reduce of remove snprintf calls from render code - Figure out a way to reduce of remove snprintf calls from render code
- Re-Implement saving/loading scene to/from files - Re-Implement saving/loading scene to/from files
- Implement storing console's scroll location and restore it when console is toggled - Implement storing console's scroll location and restore it when console is toggled

Loading…
Cancel
Save