Prefab editor now somewhat works with live-reload

This commit is contained in:
antopilo
2024-09-19 12:24:05 -04:00
parent 216fa230b6
commit 98797e15f8
5 changed files with 226 additions and 96 deletions

View File

@@ -975,7 +975,7 @@ namespace Nuake {
if (!savePath.empty())
{
newPrefab->SaveAs(savePath);
newPrefab->SaveAs(FileSystem::AbsoluteToRelative(savePath));
Selection.Entity.AddComponent<PrefabComponent>().PrefabInstance = newPrefab;
FileSystem::Scan();
FileSystemUI::m_CurrentDirectory = FileSystem::RootDirectory;
@@ -2282,7 +2282,9 @@ namespace Nuake {
char* file = (char*)payload->Data;
std::string fullPath = std::string(file, 256);
std::string relPath = FileSystem::AbsoluteToRelative(fullPath);
Prefab::New(relPath);
auto newPrefabInstance = Prefab::New(relPath);
newPrefabInstance->Root.GetComponent<PrefabComponent>().SetPrefab(newPrefabInstance);
newPrefabInstance->Root.GetComponent<NameComponent>().IsPrefab = true;
}
ImGui::EndDragDropTarget();
}

View File

@@ -1,5 +1,6 @@
#include "Prefab.h"
#include "src/Scene/Components/ParentComponent.h"
#include <src/Scene/Components/PrefabComponent.h>
namespace Nuake
{
@@ -27,7 +28,7 @@ namespace Nuake
if (!prefabTextContent.empty())
{
newPrefab->Deserialize(json::parse(prefabTextContent));
newPrefab->Deserialize(json::parse(prefabTextContent), false);
}
}
@@ -52,6 +53,153 @@ namespace Nuake
return newPrefab;
}
void Prefab::ReInstance()
{
if (FileSystem::FileExists(Path, false))
{
for (auto& c : Entities)
{
if (c.IsValid() && c != Root)
{
}
}
std::string prefabTextContent = FileSystem::ReadFile(Path);
if (!prefabTextContent.empty())
{
Deserialize(json::parse(prefabTextContent), true);
}
}
}
bool Prefab::Deserialize(const json & j, bool skipRoot)
{
if (j == "")
return false;
Path = j["Path"];
if (j.contains("DisplayName"))
{
DisplayName = j["DisplayName"];
}
if (j.contains("Description"))
{
Description = j["Description"];
}
if (j.contains("Entities"))
{
auto scene = Engine::GetCurrentScene();
std::map<uint32_t, uint32_t> newIdsLut;
for (json e : j["Entities"])
{
if (e["NameComponent"]["ID"] == j["Root"] && skipRoot)
{
newIdsLut[e["NameComponent"]["ID"]] = e["NameComponent"]["ID"];
continue;
}
Entity entity = { scene->m_Registry.create(), scene.get() };
entity.Deserialize(e); // Id gets overriden by serialized id.
auto& nameComponent = entity.GetComponent<NameComponent>();
bool isRoot = false;
if (nameComponent.ID == j["Root"])
{
isRoot = true; // We found the root entity of the prefab.
auto& parentComponent = entity.GetComponent<ParentComponent>();
parentComponent.HasParent = false;
parentComponent.ParentID = 0;
}
uint32_t oldId = nameComponent.ID;
uint32_t newId = OS::GetTime();
nameComponent.Name = nameComponent.Name;
nameComponent.ID = newId;
newIdsLut[oldId] = newId;
if (isRoot)
{
Root = entity;
entity.AddComponent<PrefabComponent>();
}
else
{
entity.AddComponent<PrefabMember>();
}
AddEntity(entity);
}
// Set reference to the parent entity to children
for (auto& e : Entities)
{
if (!e.IsValid())
{
continue;
}
if (e.GetID() == Root.GetID())
{
continue;
}
auto& parentC = e.GetComponent<ParentComponent>();
auto parent = Engine::GetCurrentScene()->GetEntityByID(newIdsLut[parentC.ParentID]);
if (parent.GetHandle() == 0)
{
Root.AddChild(e);
}
else
{
parent.AddChild(e);
}
e.GetComponent<PrefabMember>().owner = Root;
}
// Since the bones point to an entity, and we are instancing a prefab, the new skeleton is gonna be pointing to the wrong
// bones, we need to remap the skeleton to the new entities. We are simply reussing the same map we are using for the
// reparenting. Pretty neat.
std::function<void(SkeletonNode&)> recursiveBoneRemapping = [&recursiveBoneRemapping, &newIdsLut](SkeletonNode& currentBone)
{
currentBone.EntityHandle = newIdsLut[currentBone.EntityHandle];
for (SkeletonNode& bone : currentBone.Children)
{
recursiveBoneRemapping(bone);
}
};
// Do the remapping of the skeleton
for (auto& e : Entities)
{
if (!e.IsValid())
{
continue;
}
if (e.GetID() == Root.GetID() || !e.HasComponent<SkinnedModelComponent>())
{
continue;
}
auto& skinnedModelComponent = e.GetComponent<SkinnedModelComponent>();
if (!skinnedModelComponent.ModelResource)
{
continue;
}
SkeletonNode& currentBone = skinnedModelComponent.ModelResource->GetSkeletonRootNode();
recursiveBoneRemapping(currentBone);
}
}
return true;
}
void Prefab::EntityWalker(Entity entity)
{
Root = entity;

View File

@@ -4,6 +4,7 @@
#include "src/Resource/Serializable.h"
#include "Engine.h"
#include <string>
#include <vector>
#include <map>
@@ -35,6 +36,8 @@ namespace Nuake {
Entities.push_back(entity);
}
void ReInstance();
void SaveAs(const std::string& path)
{
Path = path;
@@ -169,101 +172,11 @@ namespace Nuake {
bool Deserialize(const json& j)
{
if (j == "")
return false;
Path = j["Path"];
if (j.contains("DisplayName"))
{
DisplayName = j["DisplayName"];
}
if (j.contains("Description"))
{
Description = j["Description"];
}
if (j.contains("Entities"))
{
auto scene = Engine::GetCurrentScene();
std::map<uint32_t, uint32_t> newIdsLut;
for (json e : j["Entities"])
{
Entity entity = { scene->m_Registry.create(), scene.get() };
entity.Deserialize(e); // Id gets overriden by serialized id.
auto& nameComponent = entity.GetComponent<NameComponent>();
bool isRoot = false;
if (nameComponent.ID == j["Root"])
{
isRoot = true; // We found the root entity of the prefab.
auto& parentComponent = entity.GetComponent<ParentComponent>();
parentComponent.HasParent = false;
parentComponent.ParentID = 0;
}
uint32_t oldId = nameComponent.ID;
uint32_t newId = OS::GetTime();
nameComponent.Name = nameComponent.Name;
nameComponent.ID = newId;
newIdsLut[oldId] = newId;
if (isRoot)
{
Root = entity;
}
AddEntity(entity);
}
// Set reference to the parent entity to children
for (auto& e : Entities)
{
if (e.GetID() == Root.GetID())
{
continue;
}
auto& parentC = e.GetComponent<ParentComponent>();
auto parent = Engine::GetCurrentScene()->GetEntityByID(newIdsLut[parentC.ParentID]);
parent.AddChild(e);
}
// Since the bones point to an entity, and we are instancing a prefab, the new skeleton is gonna be pointing to the wrong
// bones, we need to remap the skeleton to the new entities. We are simply reussing the same map we are using for the
// reparenting. Pretty neat.
std::function<void(SkeletonNode&)> recursiveBoneRemapping = [&recursiveBoneRemapping, &newIdsLut](SkeletonNode& currentBone)
{
currentBone.EntityHandle = newIdsLut[currentBone.EntityHandle];
for (SkeletonNode& bone : currentBone.Children)
{
recursiveBoneRemapping(bone);
}
};
// Do the remapping of the skeleton
for (auto& e : Entities)
{
if (e.GetID() == Root.GetID() || !e.HasComponent<SkinnedModelComponent>())
{
continue;
}
auto& skinnedModelComponent = e.GetComponent<SkinnedModelComponent>();
if (!skinnedModelComponent.ModelResource)
{
continue;
}
SkeletonNode& currentBone = skinnedModelComponent.ModelResource->GetSkeletonRootNode();
recursiveBoneRemapping(currentBone);
}
}
return true;
return Deserialize(j, false);
}
bool Deserialize(const json& j, bool skipRoot = false);
private:
void EntityWalker(Entity entity);
};

View File

@@ -13,4 +13,10 @@ namespace Nuake {
PrefabInstance = prefab;
}
};
class PrefabMember
{
public:
Entity owner;
};
}

View File

@@ -296,6 +296,67 @@ namespace Nuake
builder.BuildQuakeMap(entity, map.HasCollisions);
}
}
std::map<std::string, Ref<File>> prefabToReimport;
const auto& prefabView = m_Registry.view<PrefabComponent>();
for (const auto& e : prefabView)
{
auto& prefabComponent = prefabView.get<PrefabComponent>(e);
const std::string& filePath = prefabComponent.PrefabInstance->Path;
if (!FileSystem::FileExists(filePath))
{
continue;
}
Ref<File> file = FileSystem::GetFile(filePath);
if (file->GetHasBeenModified())
{
prefabToReimport[filePath] = file;
}
}
if (prefabToReimport.size() > 0)
{
for (const auto& e : prefabView)
{
auto& prefabComponent = prefabView.get<PrefabComponent>(e);
auto& prefabInstance = prefabComponent.PrefabInstance;
if (prefabInstance == nullptr)
{
continue;
}
// We don't need to reimport that one
if (!prefabToReimport.contains(prefabInstance->Path))
{
continue;
}
for (auto& ent : prefabInstance->Entities)
{
// Destroy all children, not the root!
if (ent.IsValid() && ent != prefabInstance->Root)
{
this->DestroyEntity(ent);
}
}
std::remove_if(prefabInstance->Entities.begin(), prefabInstance->Entities.end(), [](const Entity& entity)
{
return !entity.IsValid();
});
prefabInstance->ReInstance();
}
for (auto& [path, prefabFile] : prefabToReimport)
{
prefabFile->SetHasBeenModified(false);
}
}
}
for (auto& system : m_Systems)