Skip to content

Commit

Permalink
fix(tesseratos): convert entities in saved scene components
Browse files Browse the repository at this point in the history
  • Loading branch information
RiscadoA committed Feb 15, 2024
1 parent 5b39e64 commit bbeb3d4
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 57 deletions.
8 changes: 8 additions & 0 deletions core/include/cubos/core/ecs/blueprint.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,14 @@

namespace cubos::core::ecs
{
/// @brief Converts entities in a value to their respective new entities. If an entity is not found in the map, it
/// is left unchanged.
/// @param map Map of old entities to new entities.
/// @param type Value type.
/// @param value Value.
void convertEntities(const std::unordered_map<Entity, Entity, EntityHash>& map, const reflection::Type& type,
void* value);

/// @brief Collection of entities and their respective components and relations.
///
/// Blueprints are in a way the 'Prefab' of @b CUBOS. They act as a tiny @ref World which can
Expand Down
89 changes: 43 additions & 46 deletions core/src/ecs/blueprint.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,47 @@ using cubos::core::reflection::ConstructibleTrait;
using cubos::core::reflection::reflect;
using cubos::core::reflection::Type;

void cubos::core::ecs::convertEntities(const std::unordered_map<Entity, Entity, EntityHash>& map, const Type& type,
void* value)
{
using namespace cubos::core::reflection;

if (type.is<Entity>())
{
auto& entity = *static_cast<Entity*>(value);
if (!entity.isNull() && map.contains(entity))
{
entity = map.at(entity);
}
}
else if (type.has<DictionaryTrait>())
{
const auto& trait = type.get<DictionaryTrait>();
CUBOS_ASSERT(!trait.keyType().is<Entity>(), "Dictionaries using entities as keys are not supported");

for (auto [entryKey, entryValue] : trait.view(value))
{
convertEntities(map, trait.valueType(), entryValue);
}
}
else if (type.has<ArrayTrait>())
{
const auto& trait = type.get<ArrayTrait>();
for (auto* element : trait.view(value))
{
convertEntities(map, trait.elementType(), element);
}
}
else if (type.has<FieldsTrait>())
{
const auto& trait = type.get<FieldsTrait>();
for (auto [field, fieldValue] : trait.view(value))
{
convertEntities(map, field->type(), fieldValue);
}
}
}

Entity Blueprint::create(std::string name)
{
CUBOS_ASSERT(!mBimap.containsRight(name), "An entity with the name '{}' already exists on the blueprint", name);
Expand Down Expand Up @@ -112,50 +153,6 @@ const UnorderedBimap<Entity, std::string, EntityHash>& Blueprint::bimap() const
return mBimap;
}

static void convertToInstancedEntities(const std::unordered_map<Entity, Entity, EntityHash>& toInstanced,
const Type& type, void* value)
{
using namespace cubos::core::reflection;

if (type.is<Entity>())
{
auto& entity = *static_cast<Entity*>(value);
if (!entity.isNull())
{
CUBOS_ASSERT(toInstanced.contains(entity), "Entities stored in components/relations must either be null or "
"reference valid entities on their blueprints");
entity = toInstanced.at(entity);
}
}
else if (type.has<DictionaryTrait>())
{
const auto& trait = type.get<DictionaryTrait>();
CUBOS_ASSERT(!trait.keyType().is<Entity>(),
"Dictionaries using entities as keys are not supported on blueprint components");

for (auto [entryKey, entryValue] : trait.view(value))
{
convertToInstancedEntities(toInstanced, trait.valueType(), entryValue);
}
}
else if (type.has<ArrayTrait>())
{
const auto& trait = type.get<ArrayTrait>();
for (auto* element : trait.view(value))
{
convertToInstancedEntities(toInstanced, trait.elementType(), element);
}
}
else if (type.has<FieldsTrait>())
{
const auto& trait = type.get<FieldsTrait>();
for (auto [field, fieldValue] : trait.view(value))
{
convertToInstancedEntities(toInstanced, field->type(), fieldValue);
}
}
}

void Blueprint::instantiate(void* userData, Create create, Add add, Relate relate, bool withName) const
{
// Instantiate our entities and create a map from them to their instanced counterparts.
Expand All @@ -180,7 +177,7 @@ void Blueprint::instantiate(void* userData, Create create, Add add, Relate relat
for (const auto& [entity, component] : components)
{
auto copied = AnyValue::copyConstruct(component.type(), component.get());
convertToInstancedEntities(thisToInstance, copied.type(), copied.get());
convertEntities(thisToInstance, copied.type(), copied.get());
add(userData, thisToInstance.at(entity), std::move(copied));
}
}
Expand All @@ -193,7 +190,7 @@ void Blueprint::instantiate(void* userData, Create create, Add add, Relate relat
for (const auto& [toEntity, relation] : outgoing)
{
auto copied = AnyValue::copyConstruct(relation.type(), relation.get());
convertToInstancedEntities(thisToInstance, copied.type(), copied.get());
convertEntities(thisToInstance, copied.type(), copied.get());
relate(userData, thisToInstance.at(fromEntity), thisToInstance.at(toEntity), std::move(copied));
}
}
Expand Down
34 changes: 23 additions & 11 deletions tools/tesseratos/src/tesseratos/scene_editor/plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
#include <tesseratos/toolbox/plugin.hpp>

using cubos::core::ecs::Blueprint;
using cubos::core::ecs::convertEntities;
using cubos::core::ecs::EntityHash;
using cubos::core::ecs::EphemeralTrait;
using cubos::core::ecs::World;
using cubos::core::memory::AnyValue;
Expand Down Expand Up @@ -337,36 +339,46 @@ static void showSceneHierarchy(SceneInfo& scene, Commands& cmds, EntitySelector&
ImGui::PopID();
}

static Blueprint intoBlueprint(const World& world, const SceneInfo& info)
static Blueprint intoBlueprint(std::unordered_map<Entity, Entity, EntityHash>& worldToBlueprint, const World& world,
const SceneInfo& info)
{
Blueprint blueprint{};

// Add matching entities from the world into the blueprint.
// Merge sub-scene blueprints into it.
for (const auto& [name, subInfo] : info.scenes)
{
blueprint.merge(name, intoBlueprint(worldToBlueprint, world, *subInfo));
}

// Add entities from the world into the blueprint.
for (const auto& [name, entity] : info.entities)
{
worldToBlueprint.emplace(entity, blueprint.create(name));
}

// Add components from the world into the blueprint.
for (const auto& [name, worldEnt] : info.entities)
{
auto blueprintEnt = blueprint.create(name);
auto blueprintEnt = blueprint.bimap().atRight(name);
for (auto [type, value] : world.components(worldEnt))
{
if (!type->has<EphemeralTrait>())
{
blueprint.add(blueprintEnt, AnyValue::copyConstruct(*type, value));
auto component = AnyValue::copyConstruct(*type, value);
convertEntities(worldToBlueprint, component.type(), component.get());
blueprint.add(blueprintEnt, std::move(component));
}
}
}

// Merge sub-scene blueprints into it.
for (const auto& [name, subInfo] : info.scenes)
{
blueprint.merge(name, intoBlueprint(world, *subInfo));
}

return blueprint;
}

static void saveScene(const World& world, Assets& assets, State& state)
{
std::unordered_map<Entity, Entity, EntityHash> worldToBlueprint{};
Scene scene{};
scene.blueprint = intoBlueprint(world, state.root);
scene.blueprint = intoBlueprint(worldToBlueprint, world, state.root);
scene.imports = state.imports;
assets.store(state.asset, std::move(scene));
assets.save(state.asset);
Expand Down

0 comments on commit bbeb3d4

Please sign in to comment.