From 863252960799cba351e33c32764858db3ce6b618 Mon Sep 17 00:00:00 2001 From: Daniele Bartolini Date: Mon, 30 Dec 2024 20:46:00 +0100 Subject: [PATCH] resource: proper add/remove/override children units Part-of: #215 --- docs/changelog.rst | 1 + src/resource/level_resource.cpp | 10 +- src/resource/types.h | 2 +- src/resource/unit_compiler.cpp | 741 ++++++++++++++++++++------------ src/resource/unit_compiler.h | 109 ++--- src/resource/unit_resource.cpp | 11 +- 6 files changed, 536 insertions(+), 338 deletions(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index ce14d0059..9dfd4056e 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -24,6 +24,7 @@ Changelog * Lua API: 3D Gui will now place objects on the new XY plane (on the "floor") by default. * Lua API: added ``SceneGraph.owner()``, ``SceneGraph.first_child()`` and ``SceneGraph.next_sibling()``. * Data Compiler: .mesh resource can now have shared geometries between nodes. +* Data Compiler: .unit resources have now the ability to add/remove inherited children or to override them by adding, removing or modifying their components. 0.53.0 --- 30 Nov 2024 ---------------------- diff --git a/src/resource/level_resource.cpp b/src/resource/level_resource.cpp index f7628d318..30221b8c9 100644 --- a/src/resource/level_resource.cpp +++ b/src/resource/level_resource.cpp @@ -82,12 +82,12 @@ namespace level_resource_internal } } - UnitCompiler uc(opts); - s32 err = 0; - err = uc.compile_units_array(obj["units"], UINT32_MAX); + UnitCompiler uc(default_allocator(), opts); + s32 err = unit_compiler::parse_unit_array_from_json(uc, obj["units"]); DATA_COMPILER_ENSURE(err == 0, opts); - Buffer unit_blob = uc.blob(); + Buffer units_blob = unit_compiler::blob(uc); + DATA_COMPILER_ENSURE(array::size(units_blob) > 0, opts); // Write LevelResource lr; @@ -121,7 +121,7 @@ namespace level_resource_internal // Write units opts.align(16); - opts.write(unit_blob); + opts.write(units_blob); return 0; } diff --git a/src/resource/types.h b/src/resource/types.h index e092d660b..2c0724d85 100644 --- a/src/resource/types.h +++ b/src/resource/types.h @@ -77,7 +77,7 @@ struct Platform #define RESOURCE_VERSION_STATE_MACHINE RESOURCE_VERSION(6) #define RESOURCE_VERSION_CONFIG RESOURCE_VERSION(1) #define RESOURCE_VERSION_FONT RESOURCE_VERSION(1) -#define RESOURCE_VERSION_UNIT RESOURCE_VERSION(10) +#define RESOURCE_VERSION_UNIT RESOURCE_VERSION(11) #define RESOURCE_VERSION_LEVEL (RESOURCE_VERSION_UNIT + 4) //!< Level embeds UnitResource #define RESOURCE_VERSION_MATERIAL RESOURCE_VERSION(5) #define RESOURCE_VERSION_MESH RESOURCE_VERSION(6) diff --git a/src/resource/unit_compiler.cpp b/src/resource/unit_compiler.cpp index fc5f5348e..470f5b95c 100644 --- a/src/resource/unit_compiler.cpp +++ b/src/resource/unit_compiler.cpp @@ -297,133 +297,163 @@ static s32 compile_animation_state_machine(Buffer &output, const char *json, Com return 0; } -UnitCompiler::UnitCompiler(CompileOptions &opts) - : _opts(opts) - , _num_units(0) - , _component_data(default_allocator()) - , _component_info(default_allocator()) - , _unit_names(default_allocator()) - , _unit_parents(default_allocator()) +namespace unit_compiler { - register_component_compiler("transform", &compile_transform, 0.0f); - register_component_compiler("camera", &compile_camera, 1.0f); - register_component_compiler("mesh_renderer", &compile_mesh_renderer, 1.0f); - register_component_compiler("sprite_renderer", &compile_sprite_renderer, 1.0f); - register_component_compiler("light", &compile_light, 1.0f); - register_component_compiler("script", &compile_script, 1.0f); - register_component_compiler("collider", &physics_resource_internal::compile_collider, 1.0f); - register_component_compiler("actor", &physics_resource_internal::compile_actor, 2.0f); - register_component_compiler("joint", &physics_resource_internal::compile_joint, 3.0f); - register_component_compiler("animation_state_machine", &compile_animation_state_machine, 1.0f); -} + Buffer read_unit(UnitCompiler &c, const char *path) + { + Buffer buf = c._opts.read(path); + array::push_back(buf, '\0'); + return buf; + } -UnitCompiler::~UnitCompiler() -{ -} + // Collects the prefabs data of @a unit_json and its children recursively. + // Prefab of deeper child is collected first. + // + // Consider the following hierarchy: + // + // Unit Prefab + // A P + // +-B + // +-C Q + // +-D +-R + // + // collect_prefab(A) will collect the data needed to compile + // the unit 'A' and all its children: + // + // prefab_offsets[ 0] = P = { prefab = nil } <- Prefab of A + // prefab_offsets[ 1] = Q = { prefab = nil } <- Prefab of C + // prefab_offsets[n-1] = A = { prefab = P } <- A itself + s32 collect_prefabs(UnitCompiler &c, StringId64 unit_name, const char *unit_json, bool append_data) + { + TempAllocator4096 ta; + JsonObject prefab(ta); + sjson::parse(prefab, unit_json); -Buffer UnitCompiler::read_unit(const char *path) -{ - Buffer buf = _opts.read(path); - array::push_back(buf, '\0'); - return buf; -} + if (json_object::has(prefab, "children")) { + JsonArray children(ta); + sjson::parse_array(children, prefab["children"]); -s32 UnitCompiler::compile_unit(const char *path) -{ - return compile_unit_from_json(array::begin(read_unit(path)), UINT32_MAX); -} + for (u32 i = 0; i < array::size(children); ++i) { + s32 err = collect_prefabs(c, unit_name, children[i], false); + DATA_COMPILER_ENSURE(err == 0, c._opts); + } + } -u32 object_index_from_id(const JsonArray &objects, const Guid &id) -{ - for (u32 i = 0; i < array::size(objects); ++i) { - TempAllocator512 ta; - JsonObject obj(ta); - sjson::parse(obj, objects[i]); - - if (json_object::has(obj, "id")) { - if (sjson::parse_guid(obj["id"]) == id) - return i; - } else { - if (sjson::parse_guid(obj["_guid"]) == id) - return i; + if (json_object::has(prefab, "prefab")) { + TempAllocator512 ta; + DynamicString path(ta); + sjson::parse_string(path, prefab["prefab"]); + DATA_COMPILER_ASSERT_RESOURCE_EXISTS("unit" + , path.c_str() + , c._opts + ); + StringId64 name(path.c_str()); + path += ".unit"; + + Buffer buf = read_unit(c, path.c_str()); + s32 err = collect_prefabs(c, name, array::begin(buf), true); + DATA_COMPILER_ENSURE(err == 0, c._opts); + } + + if (append_data) { + u32 prefab_offset = array::size(c._prefab_data); + array::push(c._prefab_data, unit_json, strlen32(unit_json)); + array::push_back(c._prefab_data, '\0'); + array::push_back(c._prefab_offsets, prefab_offset); + array::push_back(c._prefab_names, unit_name); } + + return 0; } - return UINT32_MAX; -} + const char *prefab_json(UnitCompiler &c, const char *prefab_name) + { + StringId64 name(prefab_name); + for (u32 i = 0; i < array::size(c._prefab_names); ++i) { + if (c._prefab_names[i] == name) + return &c._prefab_data[c._prefab_offsets[i]]; + } -s32 UnitCompiler::collect_units(Buffer &data, Array &prefabs, const char *json) -{ - u32 prefab_offt = array::size(data); - array::push(data, json, strlen32(json)); - array::push_back(data, '\0'); - array::push_back(prefabs, prefab_offt); + return NULL; + } - TempAllocator4096 ta; - JsonObject prefab(ta); - sjson::parse(prefab, json); - - if (json_object::has(prefab, "prefab")) { - TempAllocator512 ta; - DynamicString path(ta); - sjson::parse_string(path, prefab["prefab"]); - DATA_COMPILER_ASSERT_RESOURCE_EXISTS("unit" - , path.c_str() - , _opts - ); - path += ".unit"; - - Buffer buf = read_unit(path.c_str()); - return 1 + collect_units(data, prefabs, array::begin(buf)); + u32 object_index(const JsonArray &objects, const Guid &object_id) + { + for (u32 i = 0; i < array::size(objects); ++i) { + TempAllocator512 ta; + JsonObject obj(ta); + sjson::parse(obj, objects[i]); + + if (json_object::has(obj, "id")) { + if (sjson::parse_guid(obj["id"]) == object_id) + return i; + } else { + if (sjson::parse_guid(obj["_guid"]) == object_id) + return i; + } + } + + return UINT32_MAX; } - return 1; -} + Unit *find_children(Unit *unit, Guid id) + { + CE_ENSURE(unit != NULL); -s32 UnitCompiler::compile_unit_from_json(const char *json, const u32 parent) -{ - Buffer data(default_allocator()); - Array offsets(default_allocator()); // Offsets to JSON objects into data + auto cur = hash_map::begin(unit->_children); + auto end = hash_map::end(unit->_children); + for (; cur != end; ++cur) { + HASH_MAP_SKIP_HOLE(unit->_children, cur); - // offsets[ 0] = { prefab = ..., comps = .., mods = ... } <- Original unit - // offsets[ 1] = { prefab = ..., ... } <- Prefab of the original unit - // offsets[ 2] = { prefab = ..., ... } <- Prefab of the prefab of the original unit - // offsets[n-1] = { prefab = nil, ... } <- Root unit - s32 err = 0; - err = collect_units(data, offsets, json); - DATA_COMPILER_ENSURE(err >= 0, _opts); + if (cur->first == id) + return cur->second; - TempAllocator4096 ta; - JsonArray merged_components(ta); - JsonArray merged_components_data(ta); - JsonArray merged_children(ta); - u32 num_prefabs = array::size(offsets); - - // Merge components of all prefabs from the root unit up to the unit that - // started the compilation. - for (u32 ii = 0; ii < num_prefabs; ++ii) { - JsonObject prefab(ta); - sjson::parse(prefab, array::begin(data) + offsets[num_prefabs - 1 - ii]); + Unit *child = find_children(cur->second, id); + if (child != NULL) + return child; + } + + return NULL; + } - if (json_object::has(prefab, "components")) { + void delete_unit(Unit *unit) + { + auto cur = hash_map::begin(unit->_children); + auto end = hash_map::end(unit->_children); + for (; cur != end; ++cur) { + HASH_MAP_SKIP_HOLE(unit->_children, cur); + + delete_unit(cur->second); + } + + CE_DELETE(default_allocator(), unit); + } + + s32 modify_unit_components(UnitCompiler &c, Unit *unit, const char *unit_json) + { + TempAllocator4096 ta; + JsonObject obj(ta); + sjson::parse(obj, unit_json); + + if (json_object::has(obj, "components")) { JsonArray components(ta); - sjson::parse_array(components, prefab["components"]); + sjson::parse_array(components, obj["components"]); - // Add components + // Add components. for (u32 cc = 0; cc < array::size(components); ++cc) { JsonObject component(ta); sjson::parse_object(component, components[cc]); - array::push_back(merged_components, components[cc]); - array::push_back(merged_components_data, component["data"]); + array::push_back(unit->_merged_components, components[cc]); + array::push_back(unit->_merged_components_data, component["data"]); } } - if (json_object::has(prefab, "deleted_components")) { + if (json_object::has(obj, "deleted_components")) { JsonObject deleted_components(ta); - sjson::parse_object(deleted_components, prefab["deleted_components"]); + sjson::parse_object(deleted_components, obj["deleted_components"]); - // Delete components + // Delete components. auto cur = json_object::begin(deleted_components); auto end = json_object::end(deleted_components); for (; cur != end; ++cur) { @@ -431,35 +461,35 @@ s32 UnitCompiler::compile_unit_from_json(const char *json, const u32 parent) auto key = cur->first; - // Extract GUID from key #xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx + // Extract GUID from key "#xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx". char guid[37]; strncpy(guid, key.data() + 1, sizeof(guid) - 1); guid[36] = '\0'; Guid component_id = guid::parse(guid); - u32 comp_idx = object_index_from_id(merged_components, component_id); + u32 comp_idx = object_index(unit->_merged_components, component_id); if (comp_idx != UINT32_MAX) { - u32 comp_last = array::size(merged_components) - 1; - merged_components[comp_idx] = merged_components[comp_last]; - array::pop_back(merged_components); - merged_components_data[comp_idx] = merged_components_data[comp_last]; - array::pop_back(merged_components_data); + u32 comp_last = array::size(unit->_merged_components) - 1; + unit->_merged_components[comp_idx] = unit->_merged_components[comp_last]; + array::pop_back(unit->_merged_components); + unit->_merged_components_data[comp_idx] = unit->_merged_components_data[comp_last]; + array::pop_back(unit->_merged_components_data); } else { char buf[GUID_BUF_LEN]; DATA_COMPILER_ASSERT(false - , _opts - , "Deletion of unexisting component ID: %s\n" + , c._opts + , "Deletion of unexisting component ID: %s" , guid::to_string(buf, sizeof(buf), component_id) ); } } } - if (json_object::has(prefab, "modified_components")) { + if (json_object::has(obj, "modified_components")) { JsonObject modified_components(ta); - sjson::parse(modified_components, prefab["modified_components"]); + sjson::parse(modified_components, obj["modified_components"]); - // Modify components + // Modify components. auto cur = json_object::begin(modified_components); auto end = json_object::end(modified_components); for (; cur != end; ++cur) { @@ -468,233 +498,400 @@ s32 UnitCompiler::compile_unit_from_json(const char *json, const u32 parent) auto key = cur->first; auto val = cur->second; - // Extract GUID from key #xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx + // Extract GUID from key "#xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx". char guid[37]; strncpy(guid, key.data() + 1, sizeof(guid) - 1); guid[36] = '\0'; Guid component_id = guid::parse(guid); - // Patch component "data" key - u32 comp_idx = object_index_from_id(merged_components, component_id); + // Patch component "data" key. + u32 comp_idx = object_index(unit->_merged_components, component_id); if (comp_idx != UINT32_MAX) { JsonObject modified_component(ta); sjson::parse_object(modified_component, val); - merged_components_data[comp_idx] = modified_component["data"]; + unit->_merged_components_data[comp_idx] = modified_component["data"]; } else { char buf[GUID_BUF_LEN]; DATA_COMPILER_ASSERT(false - , _opts - , "Modification of unexisting component ID: %s\n" + , c._opts + , "Modification of unexisting component ID: %s" , guid::to_string(buf, sizeof(buf), component_id) ); } } } - if (json_object::has(prefab, "children")) { + return 0; + } + + s32 parse_unit_internal(UnitCompiler &c + , const char *unit_json + , Unit *instance_unit + , Unit *parent_unit + ) + { + TempAllocator4096 ta; + JsonObject obj(ta); + sjson::parse(obj, unit_json); + + Guid id = sjson::parse_guid(obj["_guid"]); + + Unit *unit = instance_unit; + if (unit == NULL) { + unit = CE_NEW(default_allocator(), Unit)(default_allocator()); + } + + if (instance_unit == NULL) { + if (parent_unit == NULL) { + hash_map::set(c._units, id, unit); + } else { + unit->_parent = parent_unit; + hash_map::set(parent_unit->_children, id, unit); + } + } + + if (json_object::has(obj, "prefab")) { + TempAllocator512 ta; + DynamicString prefab(ta); + sjson::parse_string(prefab, obj["prefab"]); + const char *prefab_json_data = prefab_json(c, prefab.c_str()); + DATA_COMPILER_ASSERT(prefab_json_data != NULL + , c._opts + , "Unknown prefab: '%s'" + , prefab.c_str() + ); + + s32 err = parse_unit_internal(c + , prefab_json_data + , unit + , NULL + ); + DATA_COMPILER_ENSURE(err == 0, c._opts); + } + + s32 err = modify_unit_components(c, unit, unit_json); + DATA_COMPILER_ENSURE(err == 0, c._opts); + + if (json_object::has(obj, "children")) { JsonArray children(ta); - sjson::parse_array(children, prefab["children"]); + sjson::parse_array(children, obj["children"]); + for (u32 cc = 0; cc < array::size(children); ++cc) { - array::push_back(merged_children, children[cc]); + s32 err = parse_unit_internal(c + , children[cc] + , NULL + , unit + ); + DATA_COMPILER_ENSURE(err == 0, c._opts); } } - if (json_object::has(prefab, "deleted_children")) { + if (json_object::has(obj, "deleted_children")) { JsonArray deleted_children(ta); - sjson::parse_array(deleted_children, prefab["deleted_children"]); + sjson::parse_array(deleted_children, obj["deleted_children"]); - // Delete components + // Delete children. for (u32 ii = 0; ii < array::size(deleted_children); ++ii) { JsonObject obj(ta); sjson::parse_object(obj, deleted_children[ii]); Guid id = sjson::parse_guid(obj["id"]); - u32 child_index = object_index_from_id(merged_children, id); - if (child_index != UINT32_MAX) { - u32 child_last = array::size(merged_children) - 1; - merged_children[child_index] = merged_children[child_last]; - array::pop_back(merged_children); - } else { - char buf[GUID_BUF_LEN]; - DATA_COMPILER_ASSERT(false - , _opts - , "Deletion of unexisting child ID: %s\n" - , guid::to_string(buf, sizeof(buf), id) - ); - } + Unit *child = find_children(unit, id); + + char buf[GUID_BUF_LEN]; + DATA_COMPILER_ASSERT(child != NULL + , c._opts + , "Deletion of unexisting child ID: %s" + , guid::to_string(buf, sizeof(buf), id) + ); + + Unit *child_parent = child->_parent; + delete_unit(child); + hash_map::remove(child_parent->_children, id); } } - if (ii == 0) { - // Unnamed object hash == 0 - StringId32 name_hash; + if (json_object::has(obj, "modified_children")) { + JsonArray modified_children(ta); + sjson::parse_array(modified_children, obj["modified_children"]); + + for (u32 ii = 0; ii < array::size(modified_children); ++ii) { + JsonObject obj(ta); + sjson::parse_object(obj, modified_children[ii]); + Guid id = sjson::parse_guid(obj["id"]); + + Unit *child = find_children(unit, id); - // Parse Level Editor data - if (json_object::has(prefab, "editor")) { - JsonObject editor(ta); - sjson::parse(editor, prefab["editor"]); + char buf[GUID_BUF_LEN]; + DATA_COMPILER_ASSERT(child != NULL + , c._opts + , "Modification of unexisting child ID: %s" + , guid::to_string(buf, sizeof(buf), id) + ); - if (json_object::has(editor, "name")) - name_hash = sjson::parse_string_id(editor["name"]); + s32 err = modify_unit_components(c, child, modified_children[ii]); + DATA_COMPILER_ENSURE(err == 0, c._opts); } + } + + // Parse unit's editor name. + if (json_object::has(obj, "editor")) { + JsonObject editor(ta); + sjson::parse(editor, obj["editor"]); - array::push_back(_unit_names, name_hash); + if (json_object::has(editor, "name")) + unit->_editor_name = sjson::parse_string_id(editor["name"]); } + + return 0; + } + + s32 parse_unit_from_json(UnitCompiler &c, const char *unit_json) + { + s32 err = collect_prefabs(c, StringId64(), unit_json, true); + DATA_COMPILER_ENSURE(err == 0, c._opts); + + u32 original_unit = c._prefab_offsets[array::size(c._prefab_offsets) - 1]; + return parse_unit_internal(c, &c._prefab_data[original_unit], NULL, NULL); + } + + s32 parse_unit(UnitCompiler &c, const char *path) + { + return parse_unit_from_json(c, array::begin(read_unit(c, path))); } - // Compile component data for each component type found in the chain of units. - for (u32 cc = 0; cc < array::size(merged_components); ++cc) { - const char *val = merged_components[cc]; + s32 parse_unit_array_from_json(UnitCompiler &c, const char *units_array_json) + { + TempAllocator4096 ta; + JsonArray units(ta); + sjson::parse_array(units, units_array_json); - TempAllocator512 ta; - JsonObject component(ta); - sjson::parse(component, val); + Array original_units(default_allocator()); - StringId32 type; - if (json_object::has(component, "type")) { - type = sjson::parse_string_id(component["type"]); - } else { - type = sjson::parse_string_id(component["_type"]); + for (u32 i = 0; i < array::size(units); ++i) { + s32 err = collect_prefabs(c, StringId64(), units[i], true); + DATA_COMPILER_ENSURE(err == 0, c._opts); + u32 original_unit = c._prefab_offsets[array::size(c._prefab_offsets) - 1]; + array::push_back(original_units, original_unit); } - Buffer component_data(default_allocator()); - err = compile_component(component_data, type, merged_components_data[cc]); - DATA_COMPILER_ENSURE(err == 0, _opts); - - // Append data to the component data for the given type. - ComponentTypeData component_types_deffault(default_allocator()); - ComponentTypeData &ctd = const_cast(hash_map::get(_component_data, type, component_types_deffault)); - - // One component per unit max. - auto cur = array::begin(ctd._unit_index); - auto end = array::end(ctd._unit_index); - if (std::find(cur, end, _num_units) != end) { - char buf[STRING_ID32_BUF_LEN]; - DATA_COMPILER_ASSERT(false - , _opts - , "Unit already has a component of type: %s" - , type.to_string(buf, sizeof(buf)) - ); + for (u32 i = 0; i < array::size(units); ++i) { + s32 err = parse_unit_internal(c, &c._prefab_data[original_units[i]], NULL, NULL); + DATA_COMPILER_ENSURE(err == 0, c._opts); } - array::push(ctd._data, array::begin(component_data), array::size(component_data)); - array::push_back(ctd._unit_index, _num_units); - ++ctd._num; + return 0; } - array::push_back(_unit_parents, parent); - ++_num_units; - err = compile_units_array(merged_children, _num_units - 1); - DATA_COMPILER_ENSURE(err == 0, _opts); - return 0; -} + s32 flatten_unit(UnitCompiler &c, Unit *unit, u32 parent_unit_index) + { + const u32 unit_index = c._num_units; -s32 UnitCompiler::compile_units_array(const JsonArray &units, const u32 parent) -{ - for (u32 i = 0; i < array::size(units); ++i) { - s32 err = compile_unit_from_json(units[i], parent); - DATA_COMPILER_ENSURE(err == 0, _opts); + // Compile component data for each component type found + // in the tree of units. + for (u32 cc = 0; cc < array::size(unit->_merged_components); ++cc) { + const char *component_json = unit->_merged_components[cc]; + + TempAllocator512 ta; + JsonObject component(ta); + sjson::parse(component, component_json); + + StringId32 comp_type; + if (json_object::has(component, "type")) { + comp_type = sjson::parse_string_id(component["type"]); + } else { + comp_type = sjson::parse_string_id(component["_type"]); + } + + // Append data to the component data for the given type. + ComponentTypeData ctd_deffault(default_allocator()); + ComponentTypeData &ctd = const_cast(hash_map::get(c._component_data, comp_type, ctd_deffault)); + DATA_COMPILER_ASSERT(&ctd != &ctd_deffault, c._opts, "Unknown component type"); + + // Compile component. + Buffer comp_data(default_allocator()); + s32 err = ctd._compiler(comp_data, unit->_merged_components_data[cc], c._opts); + DATA_COMPILER_ENSURE(err == 0, c._opts); + + // One component per unit max. + auto cur = array::begin(ctd._unit_index); + auto end = array::end(ctd._unit_index); + if (std::find(cur, end, unit_index) != end) { + char buf[STRING_ID32_BUF_LEN]; + DATA_COMPILER_ASSERT(false + , c._opts + , "Unit already has a component of type: %s" + , comp_type.to_string(buf, sizeof(buf)) + ); + } + + array::push(ctd._data, array::begin(comp_data), array::size(comp_data)); + array::push_back(ctd._unit_index, unit_index); + ++ctd._num; + } + + array::push_back(c._unit_parents, parent_unit_index); + array::push_back(c._unit_names, unit->_editor_name); + ++c._num_units; + + // Flatten children tree. + auto cur = hash_map::begin(unit->_children); + auto end = hash_map::end(unit->_children); + for (; cur != end; ++cur) { + HASH_MAP_SKIP_HOLE(unit->_children, cur); + + s32 err = flatten_unit(c, cur->second, unit_index); + DATA_COMPILER_ENSURE(err == 0, c._opts); + } + + return 0; } - return 0; -} -s32 UnitCompiler::compile_units_array(const char *json, const u32 parent) -{ - TempAllocator4096 ta; - JsonArray units(ta); - sjson::parse_array(units, json); - return compile_units_array(units, parent); -} + void register_component_compiler(UnitCompiler &c, StringId32 type, CompileFunction fn, f32 spawn_order) + { + ComponentTypeData ctd(default_allocator()); + ctd._compiler = fn; -Buffer UnitCompiler::blob() -{ - Buffer output(default_allocator()); - FileBuffer fb(output); - BinaryWriter bw(fb); + ComponentTypeInfo cti; + cti._type = type; + cti._spawn_order = spawn_order; - // Count component types - u32 num_component_types = 0; - auto cur = hash_map::begin(_component_data); - auto end = hash_map::end(_component_data); - for (; cur != end; ++cur) { - HASH_MAP_SKIP_HOLE(_component_data, cur); + hash_map::set(c._component_data, type, ctd); - if (cur->second._num > 0) - ++num_component_types; + array::push_back(c._component_info, cti); + std::sort(array::begin(c._component_info), array::end(c._component_info)); } - // Write header - UnitResource ur; - ur.version = RESOURCE_HEADER(RESOURCE_VERSION_UNIT); - ur.num_units = _num_units; - ur.num_component_types = num_component_types; - - bw.write(ur.version); - bw.write(ur.num_units); - bw.write(ur.num_component_types); - - // Write parents - for (u32 ii = 0; ii < _num_units; ++ii) - bw.write(_unit_parents[ii]); - - for (u32 ii = 0; ii < array::size(_component_info); ++ii) { - const ComponentTypeData deffault_ctd(default_allocator()); - - const StringId32 type = _component_info[ii]._type; - const ComponentTypeData &ctd = hash_map::get(_component_data, type, deffault_ctd); - - const Buffer &data = ctd._data; - const Array &unit_index = ctd._unit_index; - const u32 num = ctd._num; - - if (num == 0) - continue; - - // Write component data - ComponentData cd; - cd.type = type; - cd.num_instances = num; - cd.data_size = array::size(data); - - bw.align(alignof(cd)); - bw.write(cd.type); - bw.write(cd.num_instances); - bw.write(cd.data_size); - for (u32 jj = 0; jj < array::size(unit_index); ++jj) - bw.write(unit_index[jj]); - bw.align(16); - bw.write(array::begin(data), array::size(data)); + void register_component_compiler(UnitCompiler &c, const char *type, CompileFunction fn, f32 spawn_order) + { + register_component_compiler(c, StringId32(type), fn, spawn_order); } - return output; -} + s32 flatten(UnitCompiler &c) + { + auto cur = hash_map::begin(c._units); + auto end = hash_map::end(c._units); + for (; cur != end; ++cur) { + HASH_MAP_SKIP_HOLE(c._units, cur); -void UnitCompiler::register_component_compiler(const char *type, CompileFunction fn, f32 spawn_order) -{ - register_component_compiler(StringId32(type), fn, spawn_order); -} + s32 err = flatten_unit(c, cur->second, UINT32_MAX); + DATA_COMPILER_ENSURE(err == 0, c._opts); + } -void UnitCompiler::register_component_compiler(StringId32 type, CompileFunction fn, f32 spawn_order) -{ - ComponentTypeData ctd(default_allocator()); - ctd._compiler = fn; + return 0; + } + + Buffer blob(UnitCompiler &c) + { + Buffer output(default_allocator()); + FileBuffer fb(output); + BinaryWriter bw(fb); - ComponentTypeInfo cti; - cti._type = type; - cti._spawn_order = spawn_order; + flatten(c); - hash_map::set(_component_data, type, ctd); + // Count component types. + u32 num_component_types = 0; + auto cur = hash_map::begin(c._component_data); + auto end = hash_map::end(c._component_data); + for (; cur != end; ++cur) { + HASH_MAP_SKIP_HOLE(c._component_data, cur); - array::push_back(_component_info, cti); - std::sort(array::begin(_component_info), array::end(_component_info)); + if (cur->second._num > 0) + ++num_component_types; + } + + // Write header. + UnitResource ur; + ur.version = RESOURCE_HEADER(RESOURCE_VERSION_UNIT); + ur.num_units = c._num_units; + ur.num_component_types = num_component_types; + + bw.write(ur.version); + bw.write(ur.num_units); + bw.write(ur.num_component_types); + + CE_ENSURE(c._num_units > 0); + CE_ENSURE(array::size(c._unit_parents) > 0); + CE_ENSURE(array::size(c._unit_parents) == c._num_units); + + // Write parents. + for (u32 ii = 0; ii < c._num_units; ++ii) + bw.write(c._unit_parents[ii]); + + for (u32 ii = 0; ii < array::size(c._component_info); ++ii) { + const StringId32 comp_type = c._component_info[ii]._type; + + const ComponentTypeData ctd_deffault(default_allocator()); + const ComponentTypeData &ctd = hash_map::get(c._component_data, comp_type, ctd_deffault); + + if (ctd._num == 0) + continue; + + // Write component data. + ComponentData cd; + cd.type = comp_type; + cd.num_instances = ctd._num; + cd.data_size = array::size(ctd._data); + + bw.align(alignof(cd)); + bw.write(cd.type); + bw.write(cd.num_instances); + bw.write(cd.data_size); + for (u32 jj = 0; jj < array::size(ctd._unit_index); ++jj) + bw.write(ctd._unit_index[jj]); + bw.align(16); + bw.write(array::begin(ctd._data), array::size(ctd._data)); + } + + return output; + } + +} // namespace unit_compiler + +Unit::Unit(Allocator &a) + : _merged_components(a) + , _merged_components_data(a) + , _children(a) + , _parent(NULL) +{ } -s32 UnitCompiler::compile_component(Buffer &output, StringId32 type, const char *json) +UnitCompiler::UnitCompiler(Allocator &a, CompileOptions &opts) + : _units(a) + , _opts(opts) + , _prefab_data(a) + , _prefab_offsets(a) + , _prefab_names(a) + , _component_data(a) + , _component_info(a) + , _unit_names(a) + , _unit_parents(a) + , _num_units(0) +{ + unit_compiler::register_component_compiler(*this, "transform", &compile_transform, 0.0f); + unit_compiler::register_component_compiler(*this, "camera", &compile_camera, 1.0f); + unit_compiler::register_component_compiler(*this, "mesh_renderer", &compile_mesh_renderer, 1.0f); + unit_compiler::register_component_compiler(*this, "sprite_renderer", &compile_sprite_renderer, 1.0f); + unit_compiler::register_component_compiler(*this, "light", &compile_light, 1.0f); + unit_compiler::register_component_compiler(*this, "script", &compile_script, 1.0f); + unit_compiler::register_component_compiler(*this, "collider", &physics_resource_internal::compile_collider, 1.0f); + unit_compiler::register_component_compiler(*this, "actor", &physics_resource_internal::compile_actor, 2.0f); + unit_compiler::register_component_compiler(*this, "joint", &physics_resource_internal::compile_joint, 3.0f); + unit_compiler::register_component_compiler(*this, "animation_state_machine", &compile_animation_state_machine, 1.0f); +} + +UnitCompiler::~UnitCompiler() { - DATA_COMPILER_ASSERT(hash_map::has(_component_data, type), _opts, "Unknown component"); + auto cur = hash_map::begin(_units); + auto end = hash_map::end(_units); + for (; cur != end; ++cur) { + HASH_MAP_SKIP_HOLE(_units, cur); + + unit_compiler::delete_unit(cur->second); + } - return hash_map::get(_component_data, type, ComponentTypeData(default_allocator()))._compiler(output, json, _opts); + hash_map::clear(_units); } } // namespace crown diff --git a/src/resource/unit_compiler.h b/src/resource/unit_compiler.h index 5567f10d8..18c5c482c 100644 --- a/src/resource/unit_compiler.h +++ b/src/resource/unit_compiler.h @@ -15,82 +15,83 @@ namespace crown { -struct UnitCompiler -{ - typedef s32 (*CompileFunction)(Buffer &output, const char *json, CompileOptions &opts); +typedef s32 (*CompileFunction)(Buffer &output, const char *json, CompileOptions &opts); - struct ComponentTypeData +struct ComponentTypeData +{ + ALLOCATOR_AWARE; + + u32 _num; + Array _unit_index; + Buffer _data; + CompileFunction _compiler; + + explicit ComponentTypeData(Allocator &a) + : _num(0) + , _unit_index(a) + , _data(a) + , _compiler(NULL) { - ALLOCATOR_AWARE; - - u32 _num; - Array _unit_index; - Buffer _data; - CompileFunction _compiler; - - explicit ComponentTypeData(Allocator &a) - : _num(0) - , _unit_index(a) - , _data(a) - , _compiler(NULL) - { - } - }; - - struct ComponentTypeInfo + } +}; + +struct ComponentTypeInfo +{ + StringId32 _type; + float _spawn_order; + + bool operator<(const ComponentTypeInfo &a) const { - StringId32 _type; - float _spawn_order; + return _spawn_order < a._spawn_order; + } +}; - bool operator<(const ComponentTypeInfo &a) const - { - return _spawn_order < a._spawn_order; - } - }; +struct Unit +{ + ALLOCATOR_AWARE; + StringId32 _editor_name; + JsonArray _merged_components; + JsonArray _merged_components_data; + HashMap _children; + Unit *_parent; + + /// + explicit Unit(Allocator &a); +}; + +struct UnitCompiler +{ + HashMap _units; CompileOptions &_opts; - u32 _num_units; + Buffer _prefab_data; + Array _prefab_offsets; + Array _prefab_names; HashMap _component_data; Array _component_info; Array _unit_names; Array _unit_parents; + u32 _num_units; /// - void register_component_compiler(const char *type, CompileFunction fn, f32 spawn_order); - - /// - void register_component_compiler(StringId32 type, CompileFunction fn, f32 spawn_order); - - /// - s32 compile_component(Buffer &output, StringId32 type, const char *json); - - /// - explicit UnitCompiler(CompileOptions &opts); + UnitCompiler(Allocator &a, CompileOptions &opts); /// ~UnitCompiler(); +}; +namespace unit_compiler +{ /// - Buffer read_unit(const char *name); - - /// - s32 compile_unit(const char *path); - - /// - s32 compile_unit_from_json(const char *json, const u32 parent); - - /// - s32 compile_units_array(const JsonArray &units, const u32 parent); + s32 parse_unit(UnitCompiler &c, const char *path); /// - s32 compile_units_array(const char *json, const u32 parent); + s32 parse_unit_array_from_json(UnitCompiler &c, const char *units_array_json); /// - s32 collect_units(Buffer &data, Array &prefabs, const char *json); + Buffer blob(UnitCompiler &c); - /// - Buffer blob(); -}; +} // namespace unit_compiler } // namespace crown diff --git a/src/resource/unit_resource.cpp b/src/resource/unit_resource.cpp index 499da3b8c..f70f2c68c 100644 --- a/src/resource/unit_resource.cpp +++ b/src/resource/unit_resource.cpp @@ -18,13 +18,12 @@ namespace unit_resource_internal { s32 compile(CompileOptions &opts) { - Buffer unit_data(default_allocator()); - - UnitCompiler uc(opts); - s32 err = uc.compile_unit(opts.source_path()); + UnitCompiler uc(default_allocator(), opts); + s32 err = unit_compiler::parse_unit(uc, opts.source_path()); DATA_COMPILER_ENSURE(err == 0, opts); - - opts.write(uc.blob()); + Buffer blob = unit_compiler::blob(uc); + DATA_COMPILER_ENSURE(array::size(blob) > 0, opts); + opts.write(blob); return 0; }