Skip to content

Variadic and template specializations for serialize and deserialize#498

Open
Sebanisu wants to merge 4 commits into
TheCherno:masterfrom
Sebanisu:work_on_serializer
Open

Variadic and template specializations for serialize and deserialize#498
Sebanisu wants to merge 4 commits into
TheCherno:masterfrom
Sebanisu:work_on_serializer

Conversation

@Sebanisu
Copy link
Copy Markdown
Contributor

@Sebanisu Sebanisu commented Oct 6, 2021

Describe the issue (if no issue has been made)

Serialize and Deserialize were 2 large functions. I thought it might become hard to manage.

Proposed fix

This gives you a separate function for each component, for both serializing and deserializing. I grouped them by Component and placed them at the bottom of SceneSerializer.cpp. The default catch all case is deleted so if you add a new Component to AllComponents then you will get a compile time error. So you know what you forgot and can fix it.

template<typename Component>
static void SerializeEntityComponent(YAML::Emitter&, Entity) = delete;
template<typename Component>
void DeserializeEntryComponent(YAML::detail::iterator_value& entity, Entity&) = delete;

PR impact (Make sure to add closing keywords)

List of related issues/PRs this will solve:
I took a different approach than the previous ideas I had in #496. Instead of changing AllComponents, I altered my function to accept more template arguments. So this way we are serializing TagComponent.

template<typename...Component>
static void SerializeEntityComponents(YAML::Emitter& out, Entity entity)
{
(SerializeEntityComponent<Component>(out, entity), ...);
}
template<typename...Component>
static void DeserializeEntryComponents(YAML::detail::iterator_value& entity, Entity& deserializedEntity)
{
(DeserializeEntryComponent<Component>(entity, deserializedEntity), ...);
}
template<typename... PrefixComponent, typename...Component>
static void SerializeEntityComponents(ComponentGroup<Component...>, YAML::Emitter& out, Entity entity)
{
SerializeEntityComponents<PrefixComponent..., Component...>(out, entity);
}
template<typename... PrefixComponent, typename...Component>
static void DeserializeEntryComponents(ComponentGroup<Component...>, YAML::detail::iterator_value& entity, Entity& deserializedEntity)
{
DeserializeEntryComponents<PrefixComponent..., Component...>(entity, deserializedEntity);
}
static void SerializeAllEntityComponents(YAML::Emitter& out, Entity entity)
{
SerializeEntityComponents<TagComponent>(AllComponents{}, out, entity);
}
static void DeserializeAllEntryComponents(YAML::detail::iterator_value& entity, Entity& deserializedEntity)
{
DeserializeEntryComponents(AllComponents{}, entity, deserializedEntity);
}

TagComponent is deserialized on creation of an entity. So we only need it for serialization.

uint64_t uuid = entity["Entity"].as<uint64_t>();
std::string name;
auto tagComponent = entity["TagComponent"];
if (tagComponent)
name = tagComponent["Tag"].as<std::string>();

Additional context

Add any other context about the solution here. Did you test the solution on all (relevant) platforms?
I tested on Windows 10, with a Nvidia 980 TI.

We don't serialize with NativeScriptComponent. I just put a TODO there maybe we can serialize it in the future.

template<>
static void SerializeEntityComponent<NativeScriptComponent>(YAML::Emitter& out, Entity entity)
{
//TODO: serialize NativeScriptComponent?
}
template<>
static void DeserializeEntryComponent<NativeScriptComponent>(YAML::detail::iterator_value&, Entity&)
{
//TODO: deserialize NativeScriptComponent?
}

CircleCollider2DComponent was missing from my original commit:

template<>
static void SerializeEntityComponent<CircleCollider2DComponent>(YAML::Emitter& out, Entity entity)
{
if (entity.HasComponent<CircleCollider2DComponent>())
{
out << YAML::Key << "CircleCollider2DComponent";
out << YAML::BeginMap; // CircleCollider2DComponent
auto& cc2dComponent = entity.GetComponent<CircleCollider2DComponent>();
out << YAML::Key << "Offset" << YAML::Value << cc2dComponent.Offset;
out << YAML::Key << "Radius" << YAML::Value << cc2dComponent.Radius;
out << YAML::Key << "Density" << YAML::Value << cc2dComponent.Density;
out << YAML::Key << "Friction" << YAML::Value << cc2dComponent.Friction;
out << YAML::Key << "Restitution" << YAML::Value << cc2dComponent.Restitution;
out << YAML::Key << "RestitutionThreshold" << YAML::Value << cc2dComponent.RestitutionThreshold;
out << YAML::EndMap; // CircleCollider2DComponent
}
}
template<>
static void DeserializeEntryComponent<CircleCollider2DComponent>(YAML::detail::iterator_value& entity, Entity& deserializedEntity)
{
auto circleCollider2DComponent = entity["CircleCollider2DComponent"];
if (circleCollider2DComponent)
{
auto& bc2d = deserializedEntity.AddComponent<CircleCollider2DComponent>();
bc2d.Offset = circleCollider2DComponent["Offset"].as<glm::vec2>();
bc2d.Radius = circleCollider2DComponent["Radius"].as<float>();
bc2d.Density = circleCollider2DComponent["Density"].as<float>();
bc2d.Friction = circleCollider2DComponent["Friction"].as<float>();
bc2d.Restitution = circleCollider2DComponent["Restitution"].as<float>();
bc2d.RestitutionThreshold = circleCollider2DComponent["RestitutionThreshold"].as<float>();
}
}

Cherno fixed this on his stream as well. So I just had to fix the merge conflicts.
Texture wasn't being serialized #561:
Added GetPath()

virtual const std::string & GetPath() const = 0;

virtual const std::string& GetPath() const override { return m_Path; }

Serialize TexturePath:
template<>
static void SerializeEntityComponent<SpriteRendererComponent>(YAML::Emitter& out, Entity entity)
{
if (entity.HasComponent<SpriteRendererComponent>())
{
out << YAML::Key << "SpriteRendererComponent";
out << YAML::BeginMap; // SpriteRendererComponent
auto& spriteRendererComponent = entity.GetComponent<SpriteRendererComponent>();
if (spriteRendererComponent.Texture && std::filesystem::exists(spriteRendererComponent.Texture->GetPath()))
{
out << YAML::Key << "TexturePath" << YAML::Value << spriteRendererComponent.Texture->GetPath();
}
out << YAML::Key << "Color" << YAML::Value << spriteRendererComponent.Color;
out << YAML::Key << "TilingFactor" << YAML::Value << spriteRendererComponent.TilingFactor;
out << YAML::EndMap; // SpriteRendererComponent
}
}
template<>
static void DeserializeEntryComponent<SpriteRendererComponent>(YAML::detail::iterator_value& entity, Entity& deserializedEntity)
{
auto spriteRendererComponent = entity["SpriteRendererComponent"];
if (spriteRendererComponent)
{
auto& src = deserializedEntity.AddComponent<SpriteRendererComponent>();
if(spriteRendererComponent["TexturePath"])
src.Texture = Texture2D::Create(spriteRendererComponent["TexturePath"].as<std::string>());
src.Color = spriteRendererComponent["Color"].as<glm::vec4>();
if (spriteRendererComponent["TilingFactor"])
src.TilingFactor = spriteRendererComponent["TilingFactor"].as<float>();
}
}

@Sebanisu
Copy link
Copy Markdown
Contributor Author

Sebanisu commented Oct 6, 2021

One other way we could of done this is the move the serialize code into member functions of each component. Then we just call that. Though This seemed more in line with how you were already doing stuff.

Comment thread Hazel/src/Hazel/Scene/SceneSerializer.cpp Outdated
@Sebanisu
Copy link
Copy Markdown
Contributor Author

I just merged the upstream/master into my branch as it was several months old. I wanted to make sure stuff was still working. 6e309d4

I noticed CircleCollider2DComponent was missing from Deserialize and Serialize code. So I added that. c64ec68

Texture wasn't being saved. OpenGLTexture does store a path. So I added a virtual GetPath() function. So we had something to serialize. #561 c64ec68

@Sebanisu Sebanisu changed the base branch from dev to master June 28, 2022 17:07
Comment thread Hazel/src/Hazel/Scene/Entity.h Outdated
@Sebanisu
Copy link
Copy Markdown
Contributor Author

This pull request was originally on dev and now on it's master. Some of the changes in my pull request are from dev.
entt.hpp Entity.h are included but I haven't touched those files. TheCherno made those commits.

There was one commit I accidentally reverted. So I had a revert revert. 7b19ce5

Sebanisu added 2 commits June 28, 2022 14:00
I split the serialize and deserialize functions into template specializations. Then I used the `AllComponents{}` variadic templates to call each of the templated specializations.  The catch all / default function is deleted so you get a compile time error if you forgot to handle one of the Components.

add tag to Serialize All.
add comments
add static
Correct spelling mistake
Added CircleCollider2DComponent to SerializeEntityComponent and DeserializeEntryComponent

Added `GetPath()` to `Texture` and `OpenGLTexture`

Added TextureExample.hazel
@Sebanisu Sebanisu force-pushed the work_on_serializer branch from 7b19ce5 to 73d478e Compare June 28, 2022 18:05
@Sebanisu
Copy link
Copy Markdown
Contributor Author

Sebanisu commented Jun 28, 2022

I cherry picked my commits and reset this branch so I no longer have anything from the dev branch in my pull request.

@Sebanisu Sebanisu changed the title Variadic and template specializations for serialize and deserialize Variadic and template specializations for serialize and deserialize. Fix texture serialize. Jun 28, 2022
@Sebanisu Sebanisu changed the title Variadic and template specializations for serialize and deserialize. Fix texture serialize. Variadic and template specializations for serialize and deserialize Jul 5, 2022
@Sebanisu
Copy link
Copy Markdown
Contributor Author

Sebanisu commented Jul 5, 2022

Cherno added TexturePath and Tiling Factor.
Main thing I did differently is I checked to see if the path exists on saving.
I resolved the merge conflicts from the latest stream.
Back to compiling again. :)

@Sebanisu Sebanisu force-pushed the work_on_serializer branch from ff8f5ff to d6336c6 Compare July 5, 2022 04:46
delete duplicate GetPath()

The example isn't really needed to pull request. Cherno has his own.
@Sebanisu Sebanisu force-pushed the work_on_serializer branch from d6336c6 to 44ea684 Compare July 5, 2022 04:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants