Add ComponentLibrary and Component Interfaces (#511)

* Add ComponentLibrary and Component Interfaces

* Component Interface removal

* ComponentLibrary Fat Trimming

* ComponentLibrary Fixes

* ComponentLibrary Fixes

* Add versioning in ComponentLibrary

* ComponentLibrary fixes

* Tidying up ComponentLibrary

* removed version arrays because they were unused;
changed componentVersion check to libraryVersion >= componentVersion;
and some refactors

* we also tell how many component managers were serialized

* Make Serializing Dynamic

I've added a function in wiArchive for retrieving the DATA array
directly. Used to serialize dynamically the archive by choosing to read
datablock or not. Integer hash from string will be used to determine
the existence of the component on the engine's scene or not.

* Make Serialization Dynamic

* archive can jump over unregistered component data; component versioning; refactors;

* component version will be jumped over too

Co-authored-by: Turánszki János <turanszkij@users.noreply.github.com>
This commit is contained in:
Megumumpkin
2022-08-17 16:13:13 +07:00
committed by GitHub
parent 99b704d8c7
commit 8aeed3e17f
8 changed files with 1035 additions and 845 deletions
+1
View File
@@ -1,5 +1,6 @@
This file contains changelog of wi::Archive versions
84: component library serialization
83: physical light units
82: serialized LightComponent::fov_inner
81: serialized LightComponent::forced_shadow_resolution
+1 -1
View File
@@ -5,7 +5,7 @@ namespace wi
{
// this should always be only INCREMENTED and only if a new serialization is implemeted somewhere!
static constexpr uint64_t __archiveVersion = 83;
static constexpr uint64_t __archiveVersion = 84;
// this is the version number of which below the archive is not compatible with the current version
static constexpr uint64_t __archiveVersionBarrier = 22;
+25
View File
@@ -64,6 +64,31 @@ namespace wi
// The file's name will include the directory as well
const std::string& GetSourceFileName() const;
// Appends the current archive write offset as uint64_t to the archive
// Returns the previous write offset of the archive, which can be used by PatchUnknownJumpPosition()
// to write the current archive position to that previous position
size_t WriteUnknownJumpPosition()
{
size_t pos_prev = pos;
_write(uint64_t(pos));
return pos_prev;
}
// Writes the current archive write offset to the specified archive write offset.
// It can be used with Jump() to skip parts of the archive when reading
void PatchUnknownJumpPosition(size_t offset)
{
assert(!readMode);
assert(!DATA.empty());
assert(offset + sizeof(uint64_t) < DATA.size());
*(uint64_t*)(DATA.data() + offset) = uint64_t(pos);
}
// Modifies the current archive offset
// It can be used in conjunction with WriteUnknownJumpPosition() and PatchUnknownJumpPosition()
void Jump(uint64_t jump_pos)
{
pos = jump_pos;
}
// It could be templated but we have to be extremely careful of different datasizes on different platforms
// because serialized data should be interchangeable!
// So providing exact copy operations for exact types enforces platform agnosticism
+184 -1
View File
@@ -2,6 +2,7 @@
#define WI_ENTITY_COMPONENT_SYSTEM_H
#include "wiArchive.h"
#include "wiHelper.h"
#include "wiJobSystem.h"
#include "wiUnorderedMap.h"
#include "wiVector.h"
@@ -9,6 +10,8 @@
#include <cstdint>
#include <cassert>
#include <atomic>
#include <memory>
#include <string>
// Entity-Component System
namespace wi::ecs
@@ -31,11 +34,19 @@ namespace wi::ecs
wi::jobsystem::context ctx; // allow components to spawn serialization subtasks
wi::unordered_map<uint64_t, Entity> remap;
bool allow_remap = true;
uint64_t version = 0; // The ComponentLibrary serialization will modify this by the registered component's version number
~EntitySerializer()
{
wi::jobsystem::Wait(ctx); // automatically wait for all subtasks after serialization
}
// Returns the library version of the currently serializing Component
// If not using ComponentLibrary, it returns version set by the user.
uint64_t GetVersion() const
{
return version;
}
};
// This is the safe way to serialize an entity
inline void SerializeEntity(wi::Archive& archive, Entity& entity, EntitySerializer& seri)
@@ -70,9 +81,29 @@ namespace wi::ecs
}
}
// This is an interface class to implement a ComponentManager,
// inherit this class if you want to work with ComponentLibrary
class ComponentManager_Interface
{
public:
virtual void Copy(const ComponentManager_Interface& other) = 0;
virtual void Merge(ComponentManager_Interface& other) = 0;
virtual void Clear() = 0;
virtual void Serialize(wi::Archive& archive, EntitySerializer& seri) = 0;
virtual void Component_Serialize(Entity entity, wi::Archive& archive, EntitySerializer& seri) = 0;
virtual void Remove(Entity entity) = 0;
virtual void Remove_KeepSorted(Entity entity) = 0;
virtual void MoveItem(size_t index_from, size_t index_to) = 0;
virtual bool Contains(Entity entity) const = 0;
virtual size_t GetIndex(Entity entity) const = 0;
virtual size_t GetCount() const = 0;
virtual Entity GetEntity(size_t index) const = 0;
virtual const wi::vector<Entity>& GetEntityArray() const = 0;
};
// The ComponentManager is a container that stores components and matches them with entities
template<typename Component>
class ComponentManager
class ComponentManager : public ComponentManager_Interface
{
public:
@@ -122,6 +153,16 @@ namespace wi::ecs
other.Clear();
}
inline void Copy(const ComponentManager_Interface& other)
{
Copy((ComponentManager<Component>&)other);
}
inline void Merge(ComponentManager_Interface& other)
{
Merge((ComponentManager<Component>&)other);
}
// Read/Write everything to an archive depending on the archive state
inline void Serialize(wi::Archive& archive, EntitySerializer& seri)
{
@@ -161,6 +202,34 @@ namespace wi::ecs
}
}
//Read one single component onto an archive, make sure entity are serialized first
inline void Component_Serialize(Entity entity, wi::Archive& archive, EntitySerializer& seri)
{
if(archive.IsReadMode())
{
bool component_exists;
archive >> component_exists;
if (component_exists)
{
auto& component = this->Create(entity);
component.Serialize(archive, seri);
}
}
else
{
auto component = this->GetComponent(entity);
if (component != nullptr)
{
archive << true;
component->Serialize(archive, seri);
}
else
{
archive << false;
}
}
}
// Create a new component and retrieve a reference to it
inline Component& Create(Entity entity)
{
@@ -346,6 +415,120 @@ namespace wi::ecs
// Disallow this to be copied by mistake
ComponentManager(const ComponentManager&) = delete;
};
// This is the class to store all component managers,
// this is useful for bulk operation of all attached components within an entity
class ComponentLibrary
{
public:
struct LibraryEntry
{
std::unique_ptr<ComponentManager_Interface> component_manager;
uint64_t version = 0;
};
wi::unordered_map<std::string, LibraryEntry> entries;
// Create an instance of ComponentManager of a certain data type
// The name must be unique, it will be used in serialization
// version is optional, it will be propagated to ComponentManager::Serialize() inside the EntitySerializer parameter
template<typename T>
inline ComponentManager<T>& Register(std::string name, uint64_t version = 0)
{
entries[name].component_manager = std::make_unique<ComponentManager<T>>();
entries[name].version = version;
return static_cast<ComponentManager<T>&>(*entries[name].component_manager);
}
// Serialize all registered component managers
inline void Serialize(wi::Archive& archive, EntitySerializer& seri)
{
if(archive.IsReadMode())
{
bool has_next = false;
do
{
archive >> has_next;
if(has_next)
{
std::string name;
archive >> name;
uint64_t jump_size = 0;
archive >> jump_size;
auto it = entries.find(name);
if(it != entries.end())
{
archive >> seri.version;
it->second.component_manager->Serialize(archive, seri);
}
else
{
// component manager of this name was not registered, skip serialization by jumping over the data
archive.Jump(jump_size);
}
}
}
while(has_next);
}
else
{
for(auto& it : entries)
{
archive << true;
archive << it.first; // name
size_t offset = archive.WriteUnknownJumpPosition(); // we will be able to jump from here...
archive << it.second.version;
it.second.component_manager->Serialize(archive, seri);
archive.PatchUnknownJumpPosition(offset); // ...to here, if this component manager was not registered
}
archive << false;
}
}
// Serialize all components for one entity
inline void Entity_Serialize(Entity entity, wi::Archive& archive, EntitySerializer& seri)
{
if(archive.IsReadMode())
{
bool has_next = false;
do
{
archive >> has_next;
if(has_next)
{
std::string name;
archive >> name;
uint64_t jump_size = 0;
archive >> jump_size;
auto it = entries.find(name);
if (it != entries.end())
{
archive >> seri.version;
it->second.component_manager->Component_Serialize(entity, archive, seri);
}
else
{
// component manager of this name was not registered, skip serialization by jumping over the data
archive.Jump(jump_size);
}
}
}
while(has_next);
}
else
{
for(auto& it : entries)
{
archive << true;
archive << it.first; // name
size_t offset = archive.WriteUnknownJumpPosition(); // we will be able to jump from here...
archive << it.second.version;
it.second.component_manager->Component_Serialize(entity, archive, seri);
archive.PatchUnknownJumpPosition(offset); // ...to here, if this component manager was not registered
}
archive << false;
}
}
};
}
#endif // WI_ENTITY_COMPONENT_SYSTEM_H
+16 -112
View File
@@ -2025,34 +2025,10 @@ namespace wi::scene
}
void Scene::Clear()
{
names.Clear();
layers.Clear();
transforms.Clear();
hierarchy.Clear();
materials.Clear();
meshes.Clear();
impostors.Clear();
objects.Clear();
aabb_objects.Clear();
rigidbodies.Clear();
softbodies.Clear();
armatures.Clear();
lights.Clear();
aabb_lights.Clear();
cameras.Clear();
probes.Clear();
aabb_probes.Clear();
forces.Clear();
decals.Clear();
aabb_decals.Clear();
animations.Clear();
animation_datas.Clear();
emitters.Clear();
hairs.Clear();
weathers.Clear();
sounds.Clear();
inverse_kinematics.Clear();
springs.Clear();
for(auto& entry : componentLibrary.entries)
{
entry.second.component_manager->Clear();
}
TLAS = RaytracingAccelerationStructure();
BVH.Clear();
@@ -2069,67 +2045,19 @@ namespace wi::scene
}
void Scene::Merge(Scene& other)
{
names.Merge(other.names);
layers.Merge(other.layers);
transforms.Merge(other.transforms);
hierarchy.Merge(other.hierarchy);
materials.Merge(other.materials);
meshes.Merge(other.meshes);
impostors.Merge(other.impostors);
objects.Merge(other.objects);
aabb_objects.Merge(other.aabb_objects);
rigidbodies.Merge(other.rigidbodies);
softbodies.Merge(other.softbodies);
armatures.Merge(other.armatures);
lights.Merge(other.lights);
aabb_lights.Merge(other.aabb_lights);
cameras.Merge(other.cameras);
probes.Merge(other.probes);
aabb_probes.Merge(other.aabb_probes);
forces.Merge(other.forces);
decals.Merge(other.decals);
aabb_decals.Merge(other.aabb_decals);
animations.Merge(other.animations);
animation_datas.Merge(other.animation_datas);
emitters.Merge(other.emitters);
hairs.Merge(other.hairs);
weathers.Merge(other.weathers);
sounds.Merge(other.sounds);
inverse_kinematics.Merge(other.inverse_kinematics);
springs.Merge(other.springs);
for (auto& entry : componentLibrary.entries)
{
entry.second.component_manager->Merge(*other.componentLibrary.entries[entry.first].component_manager);
}
bounds = AABB::Merge(bounds, other.bounds);
}
void Scene::FindAllEntities(wi::unordered_set<wi::ecs::Entity>& entities) const
{
entities.insert(names.GetEntityArray().begin(), names.GetEntityArray().end());
entities.insert(layers.GetEntityArray().begin(), layers.GetEntityArray().end());
entities.insert(transforms.GetEntityArray().begin(), transforms.GetEntityArray().end());
entities.insert(hierarchy.GetEntityArray().begin(), hierarchy.GetEntityArray().end());
entities.insert(materials.GetEntityArray().begin(), materials.GetEntityArray().end());
entities.insert(meshes.GetEntityArray().begin(), meshes.GetEntityArray().end());
entities.insert(impostors.GetEntityArray().begin(), impostors.GetEntityArray().end());
entities.insert(objects.GetEntityArray().begin(), objects.GetEntityArray().end());
entities.insert(aabb_objects.GetEntityArray().begin(), aabb_objects.GetEntityArray().end());
entities.insert(rigidbodies.GetEntityArray().begin(), rigidbodies.GetEntityArray().end());
entities.insert(softbodies.GetEntityArray().begin(), softbodies.GetEntityArray().end());
entities.insert(armatures.GetEntityArray().begin(), armatures.GetEntityArray().end());
entities.insert(lights.GetEntityArray().begin(), lights.GetEntityArray().end());
entities.insert(aabb_lights.GetEntityArray().begin(), aabb_lights.GetEntityArray().end());
entities.insert(cameras.GetEntityArray().begin(), cameras.GetEntityArray().end());
entities.insert(probes.GetEntityArray().begin(), probes.GetEntityArray().end());
entities.insert(aabb_probes.GetEntityArray().begin(), aabb_probes.GetEntityArray().end());
entities.insert(forces.GetEntityArray().begin(), forces.GetEntityArray().end());
entities.insert(decals.GetEntityArray().begin(), decals.GetEntityArray().end());
entities.insert(aabb_decals.GetEntityArray().begin(), aabb_decals.GetEntityArray().end());
entities.insert(animations.GetEntityArray().begin(), animations.GetEntityArray().end());
entities.insert(animation_datas.GetEntityArray().begin(), animation_datas.GetEntityArray().end());
entities.insert(emitters.GetEntityArray().begin(), emitters.GetEntityArray().end());
entities.insert(hairs.GetEntityArray().begin(), hairs.GetEntityArray().end());
entities.insert(weathers.GetEntityArray().begin(), weathers.GetEntityArray().end());
entities.insert(sounds.GetEntityArray().begin(), sounds.GetEntityArray().end());
entities.insert(inverse_kinematics.GetEntityArray().begin(), inverse_kinematics.GetEntityArray().end());
entities.insert(springs.GetEntityArray().begin(), springs.GetEntityArray().end());
for (auto& entry : componentLibrary.entries)
{
entities.insert(entry.second.component_manager->GetEntityArray().begin(), entry.second.component_manager->GetEntityArray().end());
}
}
void Scene::Entity_Remove(Entity entity, bool recursive)
@@ -2152,34 +2080,10 @@ namespace wi::scene
}
}
names.Remove(entity);
layers.Remove(entity);
transforms.Remove(entity);
hierarchy.Remove(entity);
materials.Remove(entity);
meshes.Remove(entity);
impostors.Remove(entity);
objects.Remove(entity);
aabb_objects.Remove(entity);
rigidbodies.Remove(entity);
softbodies.Remove(entity);
armatures.Remove(entity);
lights.Remove(entity);
aabb_lights.Remove(entity);
cameras.Remove(entity);
probes.Remove(entity);
aabb_probes.Remove(entity);
forces.Remove(entity);
decals.Remove(entity);
aabb_decals.Remove(entity);
animations.Remove(entity);
animation_datas.Remove(entity);
emitters.Remove(entity);
hairs.Remove(entity);
weathers.Remove(entity);
sounds.Remove(entity);
inverse_kinematics.Remove(entity);
springs.Remove(entity);
for (auto& entry : componentLibrary.entries)
{
entry.second.component_manager->Remove(entity);
}
}
Entity Scene::Entity_FindByName(const std::string& name)
{
+30 -28
View File
@@ -1308,34 +1308,36 @@ namespace wi::scene
struct Scene
{
wi::ecs::ComponentManager<NameComponent> names;
wi::ecs::ComponentManager<LayerComponent> layers;
wi::ecs::ComponentManager<TransformComponent> transforms;
wi::ecs::ComponentManager<HierarchyComponent> hierarchy;
wi::ecs::ComponentManager<MaterialComponent> materials;
wi::ecs::ComponentManager<MeshComponent> meshes;
wi::ecs::ComponentManager<ImpostorComponent> impostors;
wi::ecs::ComponentManager<ObjectComponent> objects;
wi::ecs::ComponentManager<wi::primitive::AABB> aabb_objects;
wi::ecs::ComponentManager<RigidBodyPhysicsComponent> rigidbodies;
wi::ecs::ComponentManager<SoftBodyPhysicsComponent> softbodies;
wi::ecs::ComponentManager<ArmatureComponent> armatures;
wi::ecs::ComponentManager<LightComponent> lights;
wi::ecs::ComponentManager<wi::primitive::AABB> aabb_lights;
wi::ecs::ComponentManager<CameraComponent> cameras;
wi::ecs::ComponentManager<EnvironmentProbeComponent> probes;
wi::ecs::ComponentManager<wi::primitive::AABB> aabb_probes;
wi::ecs::ComponentManager<ForceFieldComponent> forces;
wi::ecs::ComponentManager<DecalComponent> decals;
wi::ecs::ComponentManager<wi::primitive::AABB> aabb_decals;
wi::ecs::ComponentManager<AnimationComponent> animations;
wi::ecs::ComponentManager<AnimationDataComponent> animation_datas;
wi::ecs::ComponentManager<EmittedParticleSystem> emitters;
wi::ecs::ComponentManager<HairParticleSystem> hairs;
wi::ecs::ComponentManager<WeatherComponent> weathers;
wi::ecs::ComponentManager<SoundComponent> sounds;
wi::ecs::ComponentManager<InverseKinematicsComponent> inverse_kinematics;
wi::ecs::ComponentManager<SpringComponent> springs;
wi::ecs::ComponentLibrary componentLibrary;
wi::ecs::ComponentManager<NameComponent>& names = componentLibrary.Register<NameComponent>("wi::scene::Scene::names");
wi::ecs::ComponentManager<LayerComponent>& layers = componentLibrary.Register<LayerComponent>("wi::scene::Scene::layers");
wi::ecs::ComponentManager<TransformComponent>& transforms = componentLibrary.Register<TransformComponent>("wi::scene::Scene::transforms");
wi::ecs::ComponentManager<HierarchyComponent>& hierarchy = componentLibrary.Register<HierarchyComponent>("wi::scene::Scene::hierarchy");
wi::ecs::ComponentManager<MaterialComponent>& materials = componentLibrary.Register<MaterialComponent>("wi::scene::Scene::materials");
wi::ecs::ComponentManager<MeshComponent>& meshes = componentLibrary.Register<MeshComponent>("wi::scene::Scene::meshes");
wi::ecs::ComponentManager<ImpostorComponent>& impostors = componentLibrary.Register<ImpostorComponent>("wi::scene::Scene::impostors");
wi::ecs::ComponentManager<ObjectComponent>& objects = componentLibrary.Register<ObjectComponent>("wi::scene::Scene::objects");
wi::ecs::ComponentManager<wi::primitive::AABB>& aabb_objects = componentLibrary.Register<wi::primitive::AABB>("wi::scene::Scene::aabb_objects");
wi::ecs::ComponentManager<RigidBodyPhysicsComponent>& rigidbodies = componentLibrary.Register<RigidBodyPhysicsComponent>("wi::scene::Scene::rigidbodies");
wi::ecs::ComponentManager<SoftBodyPhysicsComponent>& softbodies = componentLibrary.Register<SoftBodyPhysicsComponent>("wi::scene::Scene::softbodies");
wi::ecs::ComponentManager<ArmatureComponent>& armatures = componentLibrary.Register<ArmatureComponent>("wi::scene::Scene::armatures");
wi::ecs::ComponentManager<LightComponent>& lights = componentLibrary.Register<LightComponent>("wi::scene::Scene::lights");
wi::ecs::ComponentManager<wi::primitive::AABB>& aabb_lights = componentLibrary.Register<wi::primitive::AABB>("wi::scene::Scene::aabb_lights");
wi::ecs::ComponentManager<CameraComponent>& cameras = componentLibrary.Register<CameraComponent>("wi::scene::Scene::cameras");
wi::ecs::ComponentManager<EnvironmentProbeComponent>& probes = componentLibrary.Register<EnvironmentProbeComponent>("wi::scene::Scene::probes");
wi::ecs::ComponentManager<wi::primitive::AABB>& aabb_probes = componentLibrary.Register<wi::primitive::AABB>("wi::scene::Scene::aabb_probes");
wi::ecs::ComponentManager<ForceFieldComponent>& forces = componentLibrary.Register<ForceFieldComponent>("wi::scene::Scene::forces");
wi::ecs::ComponentManager<DecalComponent>& decals = componentLibrary.Register<DecalComponent>("wi::scene::Scene::decals");
wi::ecs::ComponentManager<wi::primitive::AABB>& aabb_decals = componentLibrary.Register<wi::primitive::AABB>("wi::scene::Scene::aabb_decals");
wi::ecs::ComponentManager<AnimationComponent>& animations = componentLibrary.Register<AnimationComponent>("wi::scene::Scene::animations");
wi::ecs::ComponentManager<AnimationDataComponent>& animation_datas = componentLibrary.Register<AnimationDataComponent>("wi::scene::Scene::animation_datas");
wi::ecs::ComponentManager<EmittedParticleSystem>& emitters = componentLibrary.Register<EmittedParticleSystem>("wi::scene::Scene::emitters");
wi::ecs::ComponentManager<HairParticleSystem>& hairs = componentLibrary.Register<HairParticleSystem>("wi::scene::Scene::hairs");
wi::ecs::ComponentManager<WeatherComponent>& weathers = componentLibrary.Register<WeatherComponent>("wi::scene::Scene::weathers");
wi::ecs::ComponentManager<SoundComponent>& sounds = componentLibrary.Register<SoundComponent>("wi::scene::Scene::sounds");
wi::ecs::ComponentManager<InverseKinematicsComponent>& inverse_kinematics = componentLibrary.Register<InverseKinematicsComponent>("wi::scene::Scene::inverse_kinematics");
wi::ecs::ComponentManager<SpringComponent>& springs = componentLibrary.Register<SpringComponent>("wi::scene::Scene::springs");
// Non-serialized attributes:
float dt = 0;
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -9,7 +9,7 @@ namespace wi::version
// minor features, major updates, breaking compatibility changes
const int minor = 71;
// minor bug fixes, alterations, refactors, updates
const int revision = 12;
const int revision = 13;
const std::string version_string = std::to_string(major) + "." + std::to_string(minor) + "." + std::to_string(revision);