Bladeren bron

resource: add ability to express unit's parent-child relationship from data

Daniele Bartolini 5 jaren geleden
bovenliggende
commit
d6686483d5

+ 1 - 0
docs/changelog.rst

@@ -15,6 +15,7 @@ Changelog
 
 * Fixed child nodes in the SceneGraph not being marked as changed when their parent was changed
 * Removed support for multiple components per Unit.
+* Added ability to express unit's parent-child relationship from within .unit and .level files
 
 **Exporters**
 

+ 1 - 1
src/resource/level_resource.cpp

@@ -84,7 +84,7 @@ namespace level_resource_internal
 
 		UnitCompiler uc(opts);
 		s32 err = 0;
-		err = uc.compile_multiple_units(obj["units"]);
+		err = uc.compile_units_array(obj["units"], UINT32_MAX);
 		DATA_COMPILER_ENSURE(err == 0, opts);
 
 		Buffer unit_blob = uc.blob();

+ 1 - 1
src/resource/types.h

@@ -60,7 +60,7 @@ struct UnitResource;
 #define RESOURCE_VERSION_STATE_MACHINE    RESOURCE_VERSION(3)
 #define RESOURCE_VERSION_CONFIG           RESOURCE_VERSION(1)
 #define RESOURCE_VERSION_FONT             RESOURCE_VERSION(1)
-#define RESOURCE_VERSION_UNIT             RESOURCE_VERSION(6)
+#define RESOURCE_VERSION_UNIT             RESOURCE_VERSION(7)
 #define RESOURCE_VERSION_LEVEL            (RESOURCE_VERSION_UNIT + 4) //!< Level embeds UnitResource
 #define RESOURCE_VERSION_MATERIAL         RESOURCE_VERSION(2)
 #define RESOURCE_VERSION_MESH             RESOURCE_VERSION(4)

+ 97 - 37
src/resource/unit_compiler.cpp

@@ -306,6 +306,7 @@ UnitCompiler::UnitCompiler(CompileOptions& opts)
 	, _component_data(default_allocator())
 	, _component_info(default_allocator())
 	, _unit_names(default_allocator())
+	, _unit_parents(default_allocator())
 {
 	register_component_compiler("transform",               &compile_transform,                           0.0f);
 	register_component_compiler("camera",                  &compile_camera,                              1.0f);
@@ -332,16 +333,16 @@ Buffer UnitCompiler::read_unit(const char* path)
 
 s32 UnitCompiler::compile_unit(const char* path)
 {
-	return compile_unit_from_json(array::begin(read_unit(path)));
+	return compile_unit_from_json(array::begin(read_unit(path)), UINT32_MAX);
 }
 
-u32 component_index(const JsonArray& components, const Guid& id)
+u32 object_index_from_id(const JsonArray& objects, const Guid& id)
 {
-	for (u32 i = 0; i < array::size(components); ++i)
+	for (u32 i = 0; i < array::size(objects); ++i)
 	{
 		TempAllocator512 ta;
 		JsonObject obj(ta);
-		sjson::parse(obj, components[i]);
+		sjson::parse(obj, objects[i]);
 		if (sjson::parse_guid(obj["id"]) == id)
 			return i;
 	}
@@ -378,7 +379,7 @@ s32 UnitCompiler::collect_units(Buffer& data, Array<u32>& prefabs, const char* j
 	return 1;
 }
 
-s32 UnitCompiler::compile_unit_from_json(const char* json)
+s32 UnitCompiler::compile_unit_from_json(const char* json, const u32 parent)
 {
 	Buffer data(default_allocator());
 	Array<u32> offsets(default_allocator()); // Offsets to JSON objects into data
@@ -394,6 +395,7 @@ s32 UnitCompiler::compile_unit_from_json(const char* json)
 	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
@@ -439,7 +441,7 @@ s32 UnitCompiler::compile_unit_from_json(const char* json)
 				guid[36] = '\0';
 				Guid component_id = guid::parse(guid);
 
-				u32 comp_idx = component_index(merged_components, component_id);
+				u32 comp_idx = object_index_from_id(merged_components, component_id);
 				if (comp_idx != UINT32_MAX)
 				{
 					u32 comp_last = array::size(merged_components) - 1;
@@ -482,7 +484,7 @@ s32 UnitCompiler::compile_unit_from_json(const char* json)
 				Guid component_id = guid::parse(guid);
 
 				// Patch component "data" key
-				u32 comp_idx = component_index(merged_components, component_id);
+				u32 comp_idx = object_index_from_id(merged_components, component_id);
 				if (comp_idx != UINT32_MAX)
 				{
 					JsonObject modified_component(ta);
@@ -502,6 +504,47 @@ s32 UnitCompiler::compile_unit_from_json(const char* json)
 			}
 		}
 
+		if (json_object::has(prefab, "children"))
+		{
+			JsonArray children(ta);
+			sjson::parse_array(children, prefab["children"]);
+			for (u32 cc = 0; cc < array::size(children); ++cc)
+			{
+				array::push_back(merged_children, children[cc]);
+			}
+		}
+
+		if (json_object::has(prefab, "deleted_children"))
+		{
+			JsonArray deleted_children(ta);
+			sjson::parse_array(deleted_children, prefab["deleted_children"]);
+
+			// Delete components
+			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)
+						);
+				}
+			}
+		}
+
 		if (ii == 0)
 		{
 			// Unnamed object hash == 0
@@ -521,45 +564,68 @@ s32 UnitCompiler::compile_unit_from_json(const char* json)
 		}
 	}
 
-	if (array::size(merged_components) > 0)
+	// Compile component data for each component type found in the chain of units.
+	for (u32 cc = 0; cc < array::size(merged_components); ++cc)
 	{
-		for (u32 cc = 0; cc < array::size(merged_components); ++cc)
-		{
-			const char* val = merged_components[cc];
+		const char* val = merged_components[cc];
 
-			TempAllocator512 ta;
-			JsonObject component(ta);
-			sjson::parse(component, val);
+		TempAllocator512 ta;
+		JsonObject component(ta);
+		sjson::parse(component, val);
+
+		const StringId32 type = sjson::parse_string_id(component["type"]);
 
-			const StringId32 type = sjson::parse_string_id(component["type"]);
+		Buffer component_data(default_allocator());
+		err = compile_component(component_data, type, merged_components_data[cc]);
+		DATA_COMPILER_ENSURE(err == 0, _opts);
 
-			Buffer output(default_allocator());
-			err = compile_component(output, 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<ComponentTypeData&>(hash_map::get(_component_data, type, component_types_deffault));
 
-			add_component_data(type, output, _num_units);
+		// 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))
+				);
 		}
-	}
 
+		array::push(ctd._data, array::begin(component_data), array::size(component_data));
+		array::push_back(ctd._unit_index, _num_units);
+		++ctd._num;
+	}
+	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 UnitCompiler::compile_multiple_units(const char* json)
+s32 UnitCompiler::compile_units_array(const JsonArray& units, const u32 parent)
 {
-	TempAllocator4096 ta;
-	JsonArray units(ta);
-	sjson::parse_array(units, json);
-
 	for (u32 i = 0; i < array::size(units); ++i)
 	{
-		s32 err = compile_unit_from_json(units[i]);
+		s32 err = compile_unit_from_json(units[i], parent);
 		DATA_COMPILER_ENSURE(err == 0, _opts);
 	}
-
 	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);
+}
+
 Buffer UnitCompiler::blob()
 {
 	Buffer output(default_allocator());
@@ -588,6 +654,10 @@ Buffer UnitCompiler::blob()
 	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 StringId32 type        = _component_info[ii]._type;
@@ -619,16 +689,6 @@ Buffer UnitCompiler::blob()
 	return output;
 }
 
-void UnitCompiler::add_component_data(StringId32 type, const Buffer& data, u32 unit_index)
-{
-	ComponentTypeData component_types_deffault(default_allocator());
-	ComponentTypeData& ctd = const_cast<ComponentTypeData&>(hash_map::get(_component_data, type, component_types_deffault));
-
-	array::push(ctd._data, array::begin(data), array::size(data));
-	array::push_back(ctd._unit_index, unit_index);
-	++ctd._num;
-}
-
 void UnitCompiler::register_component_compiler(const char* type, CompileFunction fn, f32 spawn_order)
 {
 	register_component_compiler(StringId32(type), fn, spawn_order);

+ 23 - 3
src/resource/unit_compiler.h

@@ -54,11 +54,16 @@ struct UnitCompiler
 	HashMap<StringId32, ComponentTypeData> _component_data;
 	Array<ComponentTypeInfo> _component_info;
 	Array<StringId32> _unit_names;
+	Array<u32> _unit_parents;
 
+	///
 	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);
-	void add_component_data(StringId32 type, const Buffer& data, u32 unit_index);
 
 	///
 	UnitCompiler(CompileOptions& opts);
@@ -66,11 +71,26 @@ struct UnitCompiler
 	///
 	~UnitCompiler();
 
+
+	///
 	Buffer read_unit(const char* name);
+
+	///
 	s32 compile_unit(const char* path);
-	s32 compile_unit_from_json(const char* json);
-	s32 compile_multiple_units(const char* json);
+
+	///
+	s32 compile_unit_from_json(const char* json, const u32 parent);
+
+	///
+	s32 compile_units_array(const JsonArray& units, const u32 parent);
+
+	///
+	s32 compile_units_array(const char* json, const u32 parent);
+
+	///
 	s32 collect_units(Buffer& data, Array<u32>& prefabs, const char* json);
+
+	///
 	Buffer blob();
 };
 

+ 6 - 1
src/resource/unit_resource.cpp

@@ -33,10 +33,15 @@ namespace unit_resource_internal
 
 namespace unit_resource
 {
+	const u32* parents(const UnitResource* ur)
+	{
+		return (u32*)&ur[1];
+	}
+
 	const ComponentData* component_type_data(const UnitResource* ur, const ComponentData* component)
 	{
 		if (component == NULL)
-			return (ComponentData*)(&ur[1]);
+			return (ComponentData*)(parents(ur) + ur->num_units);
 		else
 			return (ComponentData*)memory::align_top(component_payload(component) + component->data_size, alignof(ComponentData));
 	}

+ 4 - 0
src/resource/unit_resource.h

@@ -17,6 +17,7 @@ struct UnitResource
 	u32 version;
 	u32 num_units;
 	u32 num_component_types;
+//	u32 parents[num_units]
 //	ComponentData data[num_component_types]
 };
 
@@ -38,6 +39,9 @@ namespace unit_resource_internal
 
 namespace unit_resource
 {
+	/// Returns the array of parents in the unit resource @ur.
+	const u32* parents(const UnitResource* ur);
+
 	/// Returns the first component type data in the unit resource @a ur, or, if
 	/// @a component is != NULL, it returns the next component type data after
 	/// it.

+ 2 - 1
src/world/level.cpp

@@ -37,11 +37,12 @@ void Level::load(const Vector3& pos, const Quaternion& rot)
 	// Spawn units
 	const UnitResource* ur = level_resource::unit_resource(_resource);
 
+	// Spawn units
 	array::resize(_unit_lookup, ur->num_units);
 	for (u32 i = 0; i < ur->num_units; ++i)
 		_unit_lookup[i] = _unit_manager->create();
 
-	spawn_units(*_world, *ur, pos, rot, VECTOR3_ONE, array::begin(_unit_lookup));
+	spawn_units(*_world, ur, pos, rot, VECTOR3_ONE, array::begin(_unit_lookup));
 
 	// Play sounds
 	const u32 num_sounds = level_resource::num_sounds(_resource);

+ 40 - 17
src/world/world.cpp

@@ -98,9 +98,16 @@ World::~World()
 UnitId World::spawn_unit(StringId64 name, const Vector3& pos, const Quaternion& rot, const Vector3& scl)
 {
 	const UnitResource* ur = (const UnitResource*)_resource_manager->get(RESOURCE_TYPE_UNIT, name);
-	UnitId unit = _unit_manager->create();
-	spawn_units(*this, *ur, pos, rot, scl, &unit);
-	return unit;
+
+	UnitId* unit_lookup = (UnitId*)default_scratch_allocator().allocate(sizeof(*unit_lookup) * ur->num_units);
+	for (u32 i = 0; i < ur->num_units; ++i)
+		unit_lookup[i] = _unit_manager->create();
+
+	spawn_units(*this, ur, pos, rot, scl, unit_lookup);
+
+	UnitId root_unit = unit_lookup[0];
+	default_scratch_allocator().deallocate(unit_lookup);
+	return root_unit;
 }
 
 UnitId World::spawn_empty_unit()
@@ -570,7 +577,7 @@ void World::post_level_loaded_event()
 	event_stream::write(_events, EventType::LEVEL_LOADED, ev);
 }
 
-void spawn_units(World& w, const UnitResource& ur, const Vector3& pos, const Quaternion& rot, const Vector3& scl, const UnitId* unit_lookup)
+void spawn_units(World& w, const UnitResource* ur, const Vector3& pos, const Quaternion& rot, const Vector3& scl, const UnitId* unit_lookup)
 {
 	SceneGraph* scene_graph = w._scene_graph;
 	RenderWorld* render_world = w._render_world;
@@ -578,9 +585,11 @@ void spawn_units(World& w, const UnitResource& ur, const Vector3& pos, const Qua
 	ScriptWorld* script_world = w._script_world;
 	AnimationStateMachine* animation_state_machine = w._animation_state_machine;
 
+	const u32* unit_parents = unit_resource::parents(ur);
+
 	// Create components
-	const ComponentData* component = unit_resource::component_type_data(&ur, NULL);
-	for (u32 cc = 0; cc < ur.num_component_types; ++cc)
+	const ComponentData* component = unit_resource::component_type_data(ur, NULL);
+	for (u32 cc = 0; cc < ur->num_component_types; ++cc)
 	{
 		const u32* unit_index = unit_resource::component_unit_index(component);
 		const char* data = unit_resource::component_payload(component);
@@ -590,14 +599,28 @@ void spawn_units(World& w, const UnitResource& ur, const Vector3& pos, const Qua
 			const TransformDesc* td = (const TransformDesc*)data;
 			for (u32 i = 0, n = component->num_instances; i < n; ++i, ++td)
 			{
-				Matrix4x4 matrix = from_quaternion_translation(rot, pos);
-				Matrix4x4 matrix_res = from_quaternion_translation(td->rotation, td->position);
-				Vector3 scale;
-				scale.x = td->scale.x * scl.x;
-				scale.y = td->scale.y * scl.y;
-				scale.z = td->scale.z * scl.z;
-				set_scale(matrix_res, scale);
-				scene_graph->create(unit_lookup[unit_index[i]], matrix_res*matrix);
+				// FIXME: add SceneGraph::allocate() to reserve an instance
+				// without initializing it.
+				const TransformInstance ti = scene_graph->create(unit_lookup[unit_index[i]]
+					, td->position
+					, td->rotation
+					, td->scale
+					);
+				if (unit_parents[unit_index[i]] != UINT32_MAX)
+				{
+					TransformInstance parent_ti = scene_graph->instance(unit_lookup[unit_parents[unit_index[i]]]);
+					scene_graph->link(parent_ti, ti, td->position, td->rotation, td->scale);
+				}
+				else
+				{
+					const Vector3 scale = vector3(td->scale.x * scl.x
+						, td->scale.y * scl.y
+						, td->scale.z * scl.z
+						);
+					Matrix4x4 tr = from_quaternion_translation(rot, pos);
+					scene_graph->set_local_pose(ti, scene_graph->local_pose(ti) * tr);
+					scene_graph->set_local_scale(ti, scale);
+				}
 			}
 		}
 		else if (component->type == COMPONENT_TYPE_CAMERA)
@@ -680,14 +703,14 @@ void spawn_units(World& w, const UnitResource& ur, const Vector3& pos, const Qua
 			CE_FATAL("Unknown component type");
 		}
 
-		component = unit_resource::component_type_data(&ur, component);
+		component = unit_resource::component_type_data(ur, component);
 	}
 
-	for (u32 i = 0; i < ur.num_units; ++i)
+	for (u32 i = 0; i < ur->num_units; ++i)
 		array::push_back(w._units, unit_lookup[i]);
 
 	// Post events
-	for (u32 i = 0; i < ur.num_units; ++i)
+	for (u32 i = 0; i < ur->num_units; ++i)
 		w.post_unit_spawned_event(unit_lookup[i]);
 }
 

+ 1 - 1
src/world/world.h

@@ -211,6 +211,6 @@ struct World
 	void post_level_loaded_event();
 };
 
-void spawn_units(World& w, const UnitResource& ur, const Vector3& pos, const Quaternion& rot, const Vector3& scl, const UnitId* unit_lookup);
+void spawn_units(World& w, const UnitResource* ur, const Vector3& pos, const Quaternion& rot, const Vector3& scl, const UnitId* unit_lookup);
 
 } // namespace crown