diff --git a/WickedEngine/ArchiveVersionHistory.txt b/WickedEngine/ArchiveVersionHistory.txt index 2fa2df4b9..49059e020 100644 --- a/WickedEngine/ArchiveVersionHistory.txt +++ b/WickedEngine/ArchiveVersionHistory.txt @@ -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 diff --git a/WickedEngine/wiArchive.cpp b/WickedEngine/wiArchive.cpp index 850233550..951257a12 100644 --- a/WickedEngine/wiArchive.cpp +++ b/WickedEngine/wiArchive.cpp @@ -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; diff --git a/WickedEngine/wiArchive.h b/WickedEngine/wiArchive.h index 099ea8e95..ab472cae5 100644 --- a/WickedEngine/wiArchive.h +++ b/WickedEngine/wiArchive.h @@ -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 diff --git a/WickedEngine/wiECS.h b/WickedEngine/wiECS.h index e665026c2..538e7b956 100644 --- a/WickedEngine/wiECS.h +++ b/WickedEngine/wiECS.h @@ -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 #include #include +#include +#include // 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 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& GetEntityArray() const = 0; + }; + // The ComponentManager is a container that stores components and matches them with entities template - 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&)other); + } + + inline void Merge(ComponentManager_Interface& other) + { + Merge((ComponentManager&)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 component_manager; + uint64_t version = 0; + }; + wi::unordered_map 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 + inline ComponentManager& Register(std::string name, uint64_t version = 0) + { + entries[name].component_manager = std::make_unique>(); + entries[name].version = version; + return static_cast&>(*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 diff --git a/WickedEngine/wiScene.cpp b/WickedEngine/wiScene.cpp index d834d604c..7d8f1aa21 100644 --- a/WickedEngine/wiScene.cpp +++ b/WickedEngine/wiScene.cpp @@ -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& 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) { diff --git a/WickedEngine/wiScene.h b/WickedEngine/wiScene.h index d061df195..8848c4e50 100644 --- a/WickedEngine/wiScene.h +++ b/WickedEngine/wiScene.h @@ -1308,34 +1308,36 @@ namespace wi::scene struct Scene { - wi::ecs::ComponentManager names; - wi::ecs::ComponentManager layers; - wi::ecs::ComponentManager transforms; - wi::ecs::ComponentManager hierarchy; - wi::ecs::ComponentManager materials; - wi::ecs::ComponentManager meshes; - wi::ecs::ComponentManager impostors; - wi::ecs::ComponentManager objects; - wi::ecs::ComponentManager aabb_objects; - wi::ecs::ComponentManager rigidbodies; - wi::ecs::ComponentManager softbodies; - wi::ecs::ComponentManager armatures; - wi::ecs::ComponentManager lights; - wi::ecs::ComponentManager aabb_lights; - wi::ecs::ComponentManager cameras; - wi::ecs::ComponentManager probes; - wi::ecs::ComponentManager aabb_probes; - wi::ecs::ComponentManager forces; - wi::ecs::ComponentManager decals; - wi::ecs::ComponentManager aabb_decals; - wi::ecs::ComponentManager animations; - wi::ecs::ComponentManager animation_datas; - wi::ecs::ComponentManager emitters; - wi::ecs::ComponentManager hairs; - wi::ecs::ComponentManager weathers; - wi::ecs::ComponentManager sounds; - wi::ecs::ComponentManager inverse_kinematics; - wi::ecs::ComponentManager springs; + wi::ecs::ComponentLibrary componentLibrary; + + wi::ecs::ComponentManager& names = componentLibrary.Register("wi::scene::Scene::names"); + wi::ecs::ComponentManager& layers = componentLibrary.Register("wi::scene::Scene::layers"); + wi::ecs::ComponentManager& transforms = componentLibrary.Register("wi::scene::Scene::transforms"); + wi::ecs::ComponentManager& hierarchy = componentLibrary.Register("wi::scene::Scene::hierarchy"); + wi::ecs::ComponentManager& materials = componentLibrary.Register("wi::scene::Scene::materials"); + wi::ecs::ComponentManager& meshes = componentLibrary.Register("wi::scene::Scene::meshes"); + wi::ecs::ComponentManager& impostors = componentLibrary.Register("wi::scene::Scene::impostors"); + wi::ecs::ComponentManager& objects = componentLibrary.Register("wi::scene::Scene::objects"); + wi::ecs::ComponentManager& aabb_objects = componentLibrary.Register("wi::scene::Scene::aabb_objects"); + wi::ecs::ComponentManager& rigidbodies = componentLibrary.Register("wi::scene::Scene::rigidbodies"); + wi::ecs::ComponentManager& softbodies = componentLibrary.Register("wi::scene::Scene::softbodies"); + wi::ecs::ComponentManager& armatures = componentLibrary.Register("wi::scene::Scene::armatures"); + wi::ecs::ComponentManager& lights = componentLibrary.Register("wi::scene::Scene::lights"); + wi::ecs::ComponentManager& aabb_lights = componentLibrary.Register("wi::scene::Scene::aabb_lights"); + wi::ecs::ComponentManager& cameras = componentLibrary.Register("wi::scene::Scene::cameras"); + wi::ecs::ComponentManager& probes = componentLibrary.Register("wi::scene::Scene::probes"); + wi::ecs::ComponentManager& aabb_probes = componentLibrary.Register("wi::scene::Scene::aabb_probes"); + wi::ecs::ComponentManager& forces = componentLibrary.Register("wi::scene::Scene::forces"); + wi::ecs::ComponentManager& decals = componentLibrary.Register("wi::scene::Scene::decals"); + wi::ecs::ComponentManager& aabb_decals = componentLibrary.Register("wi::scene::Scene::aabb_decals"); + wi::ecs::ComponentManager& animations = componentLibrary.Register("wi::scene::Scene::animations"); + wi::ecs::ComponentManager& animation_datas = componentLibrary.Register("wi::scene::Scene::animation_datas"); + wi::ecs::ComponentManager& emitters = componentLibrary.Register("wi::scene::Scene::emitters"); + wi::ecs::ComponentManager& hairs = componentLibrary.Register("wi::scene::Scene::hairs"); + wi::ecs::ComponentManager& weathers = componentLibrary.Register("wi::scene::Scene::weathers"); + wi::ecs::ComponentManager& sounds = componentLibrary.Register("wi::scene::Scene::sounds"); + wi::ecs::ComponentManager& inverse_kinematics = componentLibrary.Register("wi::scene::Scene::inverse_kinematics"); + wi::ecs::ComponentManager& springs = componentLibrary.Register("wi::scene::Scene::springs"); // Non-serialized attributes: float dt = 0; diff --git a/WickedEngine/wiScene_Serializers.cpp b/WickedEngine/wiScene_Serializers.cpp index 495175e5f..2b9a66665 100644 --- a/WickedEngine/wiScene_Serializers.cpp +++ b/WickedEngine/wiScene_Serializers.cpp @@ -1406,69 +1406,78 @@ namespace wi::scene // With this we will ensure that serialized entities are unique and persistent across the scene: EntitySerializer seri; - names.Serialize(archive, seri); - layers.Serialize(archive, seri); - transforms.Serialize(archive, seri); - if (archive.GetVersion() < 75) + if(archive.GetVersion() >= 84) { - ComponentManager prev_transforms; - prev_transforms.Serialize(archive, seri); + // New scene serialization path with component library: + componentLibrary.Serialize(archive, seri); } - hierarchy.Serialize(archive, seri); - materials.Serialize(archive, seri); - meshes.Serialize(archive, seri); - impostors.Serialize(archive, seri); - objects.Serialize(archive, seri); - aabb_objects.Serialize(archive, seri); - rigidbodies.Serialize(archive, seri); - softbodies.Serialize(archive, seri); - armatures.Serialize(archive, seri); - lights.Serialize(archive, seri); - aabb_lights.Serialize(archive, seri); - cameras.Serialize(archive, seri); - probes.Serialize(archive, seri); - aabb_probes.Serialize(archive, seri); - forces.Serialize(archive, seri); - decals.Serialize(archive, seri); - aabb_decals.Serialize(archive, seri); - animations.Serialize(archive, seri); - emitters.Serialize(archive, seri); - hairs.Serialize(archive, seri); - weathers.Serialize(archive, seri); - if (archive.GetVersion() >= 30) + else { - sounds.Serialize(archive, seri); - } - if (archive.GetVersion() >= 37) - { - inverse_kinematics.Serialize(archive, seri); - } - if (archive.GetVersion() >= 38) - { - springs.Serialize(archive, seri); - } - if (archive.GetVersion() >= 46) - { - animation_datas.Serialize(archive, seri); - } - - if (archive.GetVersion() < 46) - { - // Fixing the animation import from archive that didn't have separate animation data components: - for (size_t i = 0; i < animations.GetCount(); ++i) + // Old serialization path with hard coded componentn types: + names.Serialize(archive, seri); + layers.Serialize(archive, seri); + transforms.Serialize(archive, seri); + if (archive.GetVersion() < 75) { - AnimationComponent& animation = animations[i]; - for (const AnimationComponent::AnimationChannel& channel : animation.channels) + ComponentManager prev_transforms; + prev_transforms.Serialize(archive, seri); + } + hierarchy.Serialize(archive, seri); + materials.Serialize(archive, seri); + meshes.Serialize(archive, seri); + impostors.Serialize(archive, seri); + objects.Serialize(archive, seri); + aabb_objects.Serialize(archive, seri); + rigidbodies.Serialize(archive, seri); + softbodies.Serialize(archive, seri); + armatures.Serialize(archive, seri); + lights.Serialize(archive, seri); + aabb_lights.Serialize(archive, seri); + cameras.Serialize(archive, seri); + probes.Serialize(archive, seri); + aabb_probes.Serialize(archive, seri); + forces.Serialize(archive, seri); + decals.Serialize(archive, seri); + aabb_decals.Serialize(archive, seri); + animations.Serialize(archive, seri); + emitters.Serialize(archive, seri); + hairs.Serialize(archive, seri); + weathers.Serialize(archive, seri); + if (archive.GetVersion() >= 30) + { + sounds.Serialize(archive, seri); + } + if (archive.GetVersion() >= 37) + { + inverse_kinematics.Serialize(archive, seri); + } + if (archive.GetVersion() >= 38) + { + springs.Serialize(archive, seri); + } + if (archive.GetVersion() >= 46) + { + animation_datas.Serialize(archive, seri); + } + + if (archive.GetVersion() < 46) + { + // Fixing the animation import from archive that didn't have separate animation data components: + for (size_t i = 0; i < animations.GetCount(); ++i) { - assert(channel.samplerIndex < (int)animation.samplers.size()); - AnimationComponent::AnimationSampler& sampler = animation.samplers[channel.samplerIndex]; - if (sampler.data == INVALID_ENTITY) + AnimationComponent& animation = animations[i]; + for (const AnimationComponent::AnimationChannel& channel : animation.channels) { - // backwards-compatibility mode - sampler.data = CreateEntity(); - animation_datas.Create(sampler.data) = sampler.backwards_compatibility_data; - sampler.backwards_compatibility_data.keyframe_times.clear(); - sampler.backwards_compatibility_data.keyframe_data.clear(); + assert(channel.samplerIndex < (int)animation.samplers.size()); + AnimationComponent::AnimationSampler& sampler = animation.samplers[channel.samplerIndex]; + if (sampler.data == INVALID_ENTITY) + { + // backwards-compatibility mode + sampler.data = CreateEntity(); + animation_datas.Create(sampler.data) = sampler.backwards_compatibility_data; + sampler.backwards_compatibility_data.keyframe_times.clear(); + sampler.backwards_compatibility_data.keyframe_data.clear(); + } } } } @@ -1491,670 +1500,736 @@ namespace wi::scene { seri.allow_remap = false; } - - if (archive.IsReadMode()) + + if (archive.GetVersion() >= 84) { - // Check for each components if it exists, and if yes, READ it: - { - bool component_exists; - archive >> component_exists; - if (component_exists) - { - auto& component = names.Create(entity); - component.Serialize(archive, seri); - } - } - { - bool component_exists; - archive >> component_exists; - if (component_exists) - { - auto& component = layers.Create(entity); - component.Serialize(archive, seri); - } - } - { - bool component_exists; - archive >> component_exists; - if (component_exists) - { - auto& component = transforms.Create(entity); - component.Serialize(archive, seri); - } - } - if(archive.GetVersion() < 75) - { - bool component_exists; - archive >> component_exists; - if (component_exists) - { - ComponentManager prev_transforms; - auto& component = prev_transforms.Create(entity); - component.Serialize(archive, seri); - } - } - { - bool component_exists; - archive >> component_exists; - if (component_exists) - { - auto& component = hierarchy.Create(entity); - component.Serialize(archive, seri); - } - } - { - bool component_exists; - archive >> component_exists; - if (component_exists) - { - auto& component = materials.Create(entity); - component.Serialize(archive, seri); - } - } - { - bool component_exists; - archive >> component_exists; - if (component_exists) - { - auto& component = meshes.Create(entity); - component.Serialize(archive, seri); - } - } - { - bool component_exists; - archive >> component_exists; - if (component_exists) - { - auto& component = impostors.Create(entity); - component.Serialize(archive, seri); - } - } - { - bool component_exists; - archive >> component_exists; - if (component_exists) - { - auto& component = objects.Create(entity); - component.Serialize(archive, seri); - } - } - { - bool component_exists; - archive >> component_exists; - if (component_exists) - { - auto& component = aabb_objects.Create(entity); - component.Serialize(archive, seri); - } - } - { - bool component_exists; - archive >> component_exists; - if (component_exists) - { - auto& component = rigidbodies.Create(entity); - component.Serialize(archive, seri); - } - } - { - bool component_exists; - archive >> component_exists; - if (component_exists) - { - auto& component = softbodies.Create(entity); - component.Serialize(archive, seri); - } - } - { - bool component_exists; - archive >> component_exists; - if (component_exists) - { - auto& component = armatures.Create(entity); - component.Serialize(archive, seri); - } - } - { - bool component_exists; - archive >> component_exists; - if (component_exists) - { - auto& component = lights.Create(entity); - component.Serialize(archive, seri); - } - } - { - bool component_exists; - archive >> component_exists; - if (component_exists) - { - auto& component = aabb_lights.Create(entity); - component.Serialize(archive, seri); - } - } - { - bool component_exists; - archive >> component_exists; - if (component_exists) - { - auto& component = cameras.Create(entity); - component.Serialize(archive, seri); - } - } - { - bool component_exists; - archive >> component_exists; - if (component_exists) - { - auto& component = probes.Create(entity); - component.Serialize(archive, seri); - } - } - { - bool component_exists; - archive >> component_exists; - if (component_exists) - { - auto& component = aabb_probes.Create(entity); - component.Serialize(archive, seri); - } - } - { - bool component_exists; - archive >> component_exists; - if (component_exists) - { - auto& component = forces.Create(entity); - component.Serialize(archive, seri); - } - } - { - bool component_exists; - archive >> component_exists; - if (component_exists) - { - auto& component = decals.Create(entity); - component.Serialize(archive, seri); - } - } - { - bool component_exists; - archive >> component_exists; - if (component_exists) - { - auto& component = aabb_decals.Create(entity); - component.Serialize(archive, seri); - } - } - { - bool component_exists; - archive >> component_exists; - if (component_exists) - { - auto& component = animations.Create(entity); - component.Serialize(archive, seri); - } - } - { - bool component_exists; - archive >> component_exists; - if (component_exists) - { - auto& component = emitters.Create(entity); - component.Serialize(archive, seri); - } - } - { - bool component_exists; - archive >> component_exists; - if (component_exists) - { - auto& component = hairs.Create(entity); - component.Serialize(archive, seri); - } - } - { - bool component_exists; - archive >> component_exists; - if (component_exists) - { - auto& component = weathers.Create(entity); - component.Serialize(archive, seri); - } - } - if (archive.GetVersion() >= 30) - { - bool component_exists; - archive >> component_exists; - if (component_exists) - { - auto& component = sounds.Create(entity); - component.Serialize(archive, seri); - } - } - if (archive.GetVersion() >= 37) - { - bool component_exists; - archive >> component_exists; - if (component_exists) - { - auto& component = inverse_kinematics.Create(entity); - component.Serialize(archive, seri); - } - } - if (archive.GetVersion() >= 38) - { - bool component_exists; - archive >> component_exists; - if (component_exists) - { - auto& component = springs.Create(entity); - component.Serialize(archive, seri); - } - } - if (archive.GetVersion() >= 46) - { - bool component_exists; - archive >> component_exists; - if (component_exists) - { - auto& component = animation_datas.Create(entity); - component.Serialize(archive, seri); - } - } + // New entity serialization path with component library: + componentLibrary.Entity_Serialize(entity, archive, seri); - // Wait the job system, because from this point, component managers could be resized - // due to more serialization tasks in recursive operation - // The pointers must not be invalidated while serialization jobs are not finished - wi::jobsystem::Wait(seri.ctx); - - if (archive.GetVersion() >= 72 && has_flag(flags, EntitySerializeFlags::RECURSIVE)) + if (archive.IsReadMode()) { - // serialize children: - seri.allow_remap = restore_remap; - size_t childCount = 0; - archive >> childCount; - for (size_t i = 0; i < childCount; ++i) + // Wait the job system, because from this point, component managers could be resized + // due to more serialization tasks in recursive operation + // The pointers must not be invalidated while serialization jobs are not finished + wi::jobsystem::Wait(seri.ctx); + + if (archive.GetVersion() >= 72 && has_flag(flags, EntitySerializeFlags::RECURSIVE)) { - Entity child = Entity_Serialize(archive, seri, INVALID_ENTITY, flags); - if (child != INVALID_ENTITY) + // serialize children: + seri.allow_remap = restore_remap; + size_t childCount = 0; + archive >> childCount; + for (size_t i = 0; i < childCount; ++i) { - HierarchyComponent* hier = hierarchy.GetComponent(child); - if (hier != nullptr) + Entity child = Entity_Serialize(archive, seri, INVALID_ENTITY, flags); + if (child != INVALID_ENTITY) { - hier->parentID = entity; + HierarchyComponent* hier = hierarchy.GetComponent(child); + if (hier != nullptr) + { + hier->parentID = entity; + } } } } } + else + { + // Wait the job system, because from this point, component managers could be resized + // due to more serialization tasks in recursive operation + // The pointers must not be invalidated while serialization jobs are not finished + wi::jobsystem::Wait(seri.ctx); + + if (archive.GetVersion() >= 72 && has_flag(flags, EntitySerializeFlags::RECURSIVE)) + { + // Recursive serialization for all children: + seri.allow_remap = restore_remap; + wi::vector children; + for (size_t i = 0; i < hierarchy.GetCount(); ++i) + { + const HierarchyComponent& hier = hierarchy[i]; + if (hier.parentID == entity) + { + Entity child = hierarchy.GetEntity(i); + children.push_back(child); + } + } + archive << children.size(); + for (Entity child : children) + { + Entity_Serialize(archive, seri, child, flags); + } + } + } + } else { - // Find existing components one-by-one and WRITE them out: + // Old entity serialization path code for hard coded component types: + if (archive.IsReadMode()) { - auto component = names.GetComponent(entity); - if (component != nullptr) + // Check for each components if it exists, and if yes, READ it: { - archive << true; - component->Serialize(archive, seri); - } - else - { - archive << false; - } - } - { - auto component = layers.GetComponent(entity); - if (component != nullptr) - { - archive << true; - component->Serialize(archive, seri); - } - else - { - archive << false; - } - } - { - auto component = transforms.GetComponent(entity); - if (component != nullptr) - { - archive << true; - component->Serialize(archive, seri); - } - else - { - archive << false; - } - } - { - auto component = hierarchy.GetComponent(entity); - if (component != nullptr) - { - archive << true; - component->Serialize(archive, seri); - } - else - { - archive << false; - } - } - { - auto component = materials.GetComponent(entity); - if (component != nullptr) - { - archive << true; - component->Serialize(archive, seri); - } - else - { - archive << false; - } - } - { - auto component = meshes.GetComponent(entity); - if (component != nullptr) - { - archive << true; - component->Serialize(archive, seri); - } - else - { - archive << false; - } - } - { - auto component = impostors.GetComponent(entity); - if (component != nullptr) - { - archive << true; - component->Serialize(archive, seri); - } - else - { - archive << false; - } - } - { - auto component = objects.GetComponent(entity); - if (component != nullptr) - { - archive << true; - component->Serialize(archive, seri); - } - else - { - archive << false; - } - } - { - auto component = aabb_objects.GetComponent(entity); - if (component != nullptr) - { - archive << true; - component->Serialize(archive, seri); - } - else - { - archive << false; - } - } - { - auto component = rigidbodies.GetComponent(entity); - if (component != nullptr) - { - archive << true; - component->Serialize(archive, seri); - } - else - { - archive << false; - } - } - { - auto component = softbodies.GetComponent(entity); - if (component != nullptr) - { - archive << true; - component->Serialize(archive, seri); - } - else - { - archive << false; - } - } - { - auto component = armatures.GetComponent(entity); - if (component != nullptr) - { - archive << true; - component->Serialize(archive, seri); - } - else - { - archive << false; - } - } - { - auto component = lights.GetComponent(entity); - if (component != nullptr) - { - archive << true; - component->Serialize(archive, seri); - } - else - { - archive << false; - } - } - { - auto component = aabb_lights.GetComponent(entity); - if (component != nullptr) - { - archive << true; - component->Serialize(archive, seri); - } - else - { - archive << false; - } - } - { - auto component = cameras.GetComponent(entity); - if (component != nullptr) - { - archive << true; - component->Serialize(archive, seri); - } - else - { - archive << false; - } - } - { - auto component = probes.GetComponent(entity); - if (component != nullptr) - { - archive << true; - component->Serialize(archive, seri); - } - else - { - archive << false; - } - } - { - auto component = aabb_probes.GetComponent(entity); - if (component != nullptr) - { - archive << true; - component->Serialize(archive, seri); - } - else - { - archive << false; - } - } - { - auto component = forces.GetComponent(entity); - if (component != nullptr) - { - archive << true; - component->Serialize(archive, seri); - } - else - { - archive << false; - } - } - { - auto component = decals.GetComponent(entity); - if (component != nullptr) - { - archive << true; - component->Serialize(archive, seri); - } - else - { - archive << false; - } - } - { - auto component = aabb_decals.GetComponent(entity); - if (component != nullptr) - { - archive << true; - component->Serialize(archive, seri); - } - else - { - archive << false; - } - } - { - auto component = animations.GetComponent(entity); - if (component != nullptr) - { - archive << true; - component->Serialize(archive, seri); - } - else - { - archive << false; - } - } - { - auto component = emitters.GetComponent(entity); - if (component != nullptr) - { - archive << true; - component->Serialize(archive, seri); - } - else - { - archive << false; - } - } - { - auto component = hairs.GetComponent(entity); - if (component != nullptr) - { - archive << true; - component->Serialize(archive, seri); - } - else - { - archive << false; - } - } - { - auto component = weathers.GetComponent(entity); - if (component != nullptr) - { - archive << true; - component->Serialize(archive, seri); - } - else - { - archive << false; - } - } - if(archive.GetVersion() >= 30) - { - auto component = sounds.GetComponent(entity); - if (component != nullptr) - { - archive << true; - component->Serialize(archive, seri); - } - else - { - archive << false; - } - } - if (archive.GetVersion() >= 37) - { - auto component = inverse_kinematics.GetComponent(entity); - if (component != nullptr) - { - archive << true; - component->Serialize(archive, seri); - } - else - { - archive << false; - } - } - if (archive.GetVersion() >= 38) - { - auto component = springs.GetComponent(entity); - if (component != nullptr) - { - archive << true; - component->Serialize(archive, seri); - } - else - { - archive << false; - } - } - if (archive.GetVersion() >= 46) - { - auto component = animation_datas.GetComponent(entity); - if (component != nullptr) - { - archive << true; - component->Serialize(archive, seri); - } - else - { - archive << false; - } - } - - // Wait the job system, because from this point, component managers could be resized - // due to more serialization tasks in recursive operation - // The pointers must not be invalidated while serialization jobs are not finished - wi::jobsystem::Wait(seri.ctx); - - if (archive.GetVersion() >= 72 && has_flag(flags, EntitySerializeFlags::RECURSIVE)) - { - // Recursive serialization for all children: - seri.allow_remap = restore_remap; - wi::vector children; - for (size_t i = 0; i < hierarchy.GetCount(); ++i) - { - const HierarchyComponent& hier = hierarchy[i]; - if (hier.parentID == entity) + bool component_exists; + archive >> component_exists; + if (component_exists) { - Entity child = hierarchy.GetEntity(i); - children.push_back(child); + auto& component = names.Create(entity); + component.Serialize(archive, seri); } } - archive << children.size(); - for (Entity child : children) { - Entity_Serialize(archive, seri, child, flags); + bool component_exists; + archive >> component_exists; + if (component_exists) + { + auto& component = layers.Create(entity); + component.Serialize(archive, seri); + } + } + { + bool component_exists; + archive >> component_exists; + if (component_exists) + { + auto& component = transforms.Create(entity); + component.Serialize(archive, seri); + } + } + if(archive.GetVersion() < 75) + { + bool component_exists; + archive >> component_exists; + if (component_exists) + { + ComponentManager prev_transforms; + auto& component = prev_transforms.Create(entity); + component.Serialize(archive, seri); + } + } + { + bool component_exists; + archive >> component_exists; + if (component_exists) + { + auto& component = hierarchy.Create(entity); + component.Serialize(archive, seri); + } + } + { + bool component_exists; + archive >> component_exists; + if (component_exists) + { + auto& component = materials.Create(entity); + component.Serialize(archive, seri); + } + } + { + bool component_exists; + archive >> component_exists; + if (component_exists) + { + auto& component = meshes.Create(entity); + component.Serialize(archive, seri); + } + } + { + bool component_exists; + archive >> component_exists; + if (component_exists) + { + auto& component = impostors.Create(entity); + component.Serialize(archive, seri); + } + } + { + bool component_exists; + archive >> component_exists; + if (component_exists) + { + auto& component = objects.Create(entity); + component.Serialize(archive, seri); + } + } + { + bool component_exists; + archive >> component_exists; + if (component_exists) + { + auto& component = aabb_objects.Create(entity); + component.Serialize(archive, seri); + } + } + { + bool component_exists; + archive >> component_exists; + if (component_exists) + { + auto& component = rigidbodies.Create(entity); + component.Serialize(archive, seri); + } + } + { + bool component_exists; + archive >> component_exists; + if (component_exists) + { + auto& component = softbodies.Create(entity); + component.Serialize(archive, seri); + } + } + { + bool component_exists; + archive >> component_exists; + if (component_exists) + { + auto& component = armatures.Create(entity); + component.Serialize(archive, seri); + } + } + { + bool component_exists; + archive >> component_exists; + if (component_exists) + { + auto& component = lights.Create(entity); + component.Serialize(archive, seri); + } + } + { + bool component_exists; + archive >> component_exists; + if (component_exists) + { + auto& component = aabb_lights.Create(entity); + component.Serialize(archive, seri); + } + } + { + bool component_exists; + archive >> component_exists; + if (component_exists) + { + auto& component = cameras.Create(entity); + component.Serialize(archive, seri); + } + } + { + bool component_exists; + archive >> component_exists; + if (component_exists) + { + auto& component = probes.Create(entity); + component.Serialize(archive, seri); + } + } + { + bool component_exists; + archive >> component_exists; + if (component_exists) + { + auto& component = aabb_probes.Create(entity); + component.Serialize(archive, seri); + } + } + { + bool component_exists; + archive >> component_exists; + if (component_exists) + { + auto& component = forces.Create(entity); + component.Serialize(archive, seri); + } + } + { + bool component_exists; + archive >> component_exists; + if (component_exists) + { + auto& component = decals.Create(entity); + component.Serialize(archive, seri); + } + } + { + bool component_exists; + archive >> component_exists; + if (component_exists) + { + auto& component = aabb_decals.Create(entity); + component.Serialize(archive, seri); + } + } + { + bool component_exists; + archive >> component_exists; + if (component_exists) + { + auto& component = animations.Create(entity); + component.Serialize(archive, seri); + } + } + { + bool component_exists; + archive >> component_exists; + if (component_exists) + { + auto& component = emitters.Create(entity); + component.Serialize(archive, seri); + } + } + { + bool component_exists; + archive >> component_exists; + if (component_exists) + { + auto& component = hairs.Create(entity); + component.Serialize(archive, seri); + } + } + { + bool component_exists; + archive >> component_exists; + if (component_exists) + { + auto& component = weathers.Create(entity); + component.Serialize(archive, seri); + } + } + if (archive.GetVersion() >= 30) + { + bool component_exists; + archive >> component_exists; + if (component_exists) + { + auto& component = sounds.Create(entity); + component.Serialize(archive, seri); + } + } + if (archive.GetVersion() >= 37) + { + bool component_exists; + archive >> component_exists; + if (component_exists) + { + auto& component = inverse_kinematics.Create(entity); + component.Serialize(archive, seri); + } + } + if (archive.GetVersion() >= 38) + { + bool component_exists; + archive >> component_exists; + if (component_exists) + { + auto& component = springs.Create(entity); + component.Serialize(archive, seri); + } + } + if (archive.GetVersion() >= 46) + { + bool component_exists; + archive >> component_exists; + if (component_exists) + { + auto& component = animation_datas.Create(entity); + component.Serialize(archive, seri); + } + } + + // Wait the job system, because from this point, component managers could be resized + // due to more serialization tasks in recursive operation + // The pointers must not be invalidated while serialization jobs are not finished + wi::jobsystem::Wait(seri.ctx); + + if (archive.GetVersion() >= 72 && has_flag(flags, EntitySerializeFlags::RECURSIVE)) + { + // serialize children: + seri.allow_remap = restore_remap; + size_t childCount = 0; + archive >> childCount; + for (size_t i = 0; i < childCount; ++i) + { + Entity child = Entity_Serialize(archive, seri, INVALID_ENTITY, flags); + if (child != INVALID_ENTITY) + { + HierarchyComponent* hier = hierarchy.GetComponent(child); + if (hier != nullptr) + { + hier->parentID = entity; + } + } + } + } + } + else + { + // Find existing components one-by-one and WRITE them out: + { + auto component = names.GetComponent(entity); + if (component != nullptr) + { + archive << true; + component->Serialize(archive, seri); + } + else + { + archive << false; + } + } + { + auto component = layers.GetComponent(entity); + if (component != nullptr) + { + archive << true; + component->Serialize(archive, seri); + } + else + { + archive << false; + } + } + { + auto component = transforms.GetComponent(entity); + if (component != nullptr) + { + archive << true; + component->Serialize(archive, seri); + } + else + { + archive << false; + } + } + { + auto component = hierarchy.GetComponent(entity); + if (component != nullptr) + { + archive << true; + component->Serialize(archive, seri); + } + else + { + archive << false; + } + } + { + auto component = materials.GetComponent(entity); + if (component != nullptr) + { + archive << true; + component->Serialize(archive, seri); + } + else + { + archive << false; + } + } + { + auto component = meshes.GetComponent(entity); + if (component != nullptr) + { + archive << true; + component->Serialize(archive, seri); + } + else + { + archive << false; + } + } + { + auto component = impostors.GetComponent(entity); + if (component != nullptr) + { + archive << true; + component->Serialize(archive, seri); + } + else + { + archive << false; + } + } + { + auto component = objects.GetComponent(entity); + if (component != nullptr) + { + archive << true; + component->Serialize(archive, seri); + } + else + { + archive << false; + } + } + { + auto component = aabb_objects.GetComponent(entity); + if (component != nullptr) + { + archive << true; + component->Serialize(archive, seri); + } + else + { + archive << false; + } + } + { + auto component = rigidbodies.GetComponent(entity); + if (component != nullptr) + { + archive << true; + component->Serialize(archive, seri); + } + else + { + archive << false; + } + } + { + auto component = softbodies.GetComponent(entity); + if (component != nullptr) + { + archive << true; + component->Serialize(archive, seri); + } + else + { + archive << false; + } + } + { + auto component = armatures.GetComponent(entity); + if (component != nullptr) + { + archive << true; + component->Serialize(archive, seri); + } + else + { + archive << false; + } + } + { + auto component = lights.GetComponent(entity); + if (component != nullptr) + { + archive << true; + component->Serialize(archive, seri); + } + else + { + archive << false; + } + } + { + auto component = aabb_lights.GetComponent(entity); + if (component != nullptr) + { + archive << true; + component->Serialize(archive, seri); + } + else + { + archive << false; + } + } + { + auto component = cameras.GetComponent(entity); + if (component != nullptr) + { + archive << true; + component->Serialize(archive, seri); + } + else + { + archive << false; + } + } + { + auto component = probes.GetComponent(entity); + if (component != nullptr) + { + archive << true; + component->Serialize(archive, seri); + } + else + { + archive << false; + } + } + { + auto component = aabb_probes.GetComponent(entity); + if (component != nullptr) + { + archive << true; + component->Serialize(archive, seri); + } + else + { + archive << false; + } + } + { + auto component = forces.GetComponent(entity); + if (component != nullptr) + { + archive << true; + component->Serialize(archive, seri); + } + else + { + archive << false; + } + } + { + auto component = decals.GetComponent(entity); + if (component != nullptr) + { + archive << true; + component->Serialize(archive, seri); + } + else + { + archive << false; + } + } + { + auto component = aabb_decals.GetComponent(entity); + if (component != nullptr) + { + archive << true; + component->Serialize(archive, seri); + } + else + { + archive << false; + } + } + { + auto component = animations.GetComponent(entity); + if (component != nullptr) + { + archive << true; + component->Serialize(archive, seri); + } + else + { + archive << false; + } + } + { + auto component = emitters.GetComponent(entity); + if (component != nullptr) + { + archive << true; + component->Serialize(archive, seri); + } + else + { + archive << false; + } + } + { + auto component = hairs.GetComponent(entity); + if (component != nullptr) + { + archive << true; + component->Serialize(archive, seri); + } + else + { + archive << false; + } + } + { + auto component = weathers.GetComponent(entity); + if (component != nullptr) + { + archive << true; + component->Serialize(archive, seri); + } + else + { + archive << false; + } + } + if(archive.GetVersion() >= 30) + { + auto component = sounds.GetComponent(entity); + if (component != nullptr) + { + archive << true; + component->Serialize(archive, seri); + } + else + { + archive << false; + } + } + if (archive.GetVersion() >= 37) + { + auto component = inverse_kinematics.GetComponent(entity); + if (component != nullptr) + { + archive << true; + component->Serialize(archive, seri); + } + else + { + archive << false; + } + } + if (archive.GetVersion() >= 38) + { + auto component = springs.GetComponent(entity); + if (component != nullptr) + { + archive << true; + component->Serialize(archive, seri); + } + else + { + archive << false; + } + } + if (archive.GetVersion() >= 46) + { + auto component = animation_datas.GetComponent(entity); + if (component != nullptr) + { + archive << true; + component->Serialize(archive, seri); + } + else + { + archive << false; + } + } + + // Wait the job system, because from this point, component managers could be resized + // due to more serialization tasks in recursive operation + // The pointers must not be invalidated while serialization jobs are not finished + wi::jobsystem::Wait(seri.ctx); + + if (archive.GetVersion() >= 72 && has_flag(flags, EntitySerializeFlags::RECURSIVE)) + { + // Recursive serialization for all children: + seri.allow_remap = restore_remap; + wi::vector children; + for (size_t i = 0; i < hierarchy.GetCount(); ++i) + { + const HierarchyComponent& hier = hierarchy[i]; + if (hier.parentID == entity) + { + Entity child = hierarchy.GetEntity(i); + children.push_back(child); + } + } + archive << children.size(); + for (Entity child : children) + { + Entity_Serialize(archive, seri, child, flags); + } } } } diff --git a/WickedEngine/wiVersion.cpp b/WickedEngine/wiVersion.cpp index c27a3d207..2c7d485c0 100644 --- a/WickedEngine/wiVersion.cpp +++ b/WickedEngine/wiVersion.cpp @@ -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);