mirror of
https://github.com/barkeser2002/flower.git
synced 2026-09-25 12:46:03 +03:00
354 lines
8.9 KiB
C++
354 lines
8.9 KiB
C++
#include "Pch.h"
|
|
#include "AssetRegistry.h"
|
|
#include "TextureManager.h"
|
|
#include "AssetSystem.h"
|
|
#include "MeshManager.h"
|
|
#include "AssetArchive.h"
|
|
|
|
namespace Flower
|
|
{
|
|
std::unique_ptr<AssetRegistry> GAssetRegistry = std::make_unique<AssetRegistry>();
|
|
|
|
void AssetRegistry::addEntry(std::shared_ptr<RegistryEntry> entry)
|
|
{
|
|
m_registryMap[entry->getRegistryUUID()] = entry;
|
|
m_registryHeaderMap[entry->getAssetHeaderID()] = entry->getRegistryUUID();
|
|
}
|
|
|
|
void AssetRegistry::removeEntry(std::shared_ptr<RegistryEntry> entry)
|
|
{
|
|
m_registryMap.erase(entry->getRegistryUUID());
|
|
m_registryHeaderMap.erase(entry->getAssetHeaderID());
|
|
}
|
|
|
|
void AssetRegistry::registerAssetMap(std::shared_ptr<AssetHeaderInterface> asset, EAssetType type)
|
|
{
|
|
std::unique_lock lock(m_assetMapMutex);
|
|
m_assetMap[asset->getHeaderUUID()] = asset;
|
|
m_assetTypeSet[size_t(type)].insert(asset->getHeaderUUID());
|
|
}
|
|
|
|
void AssetRegistry::save()
|
|
{
|
|
const auto& registryPath = GEngine->getRuntimeModule<AssetSystem>()->getProjectAssetEntryPath();
|
|
|
|
// Save entry tree.
|
|
if(isDirty())
|
|
{
|
|
std::ofstream os(registryPath, std::ios::binary);
|
|
cereal::BinaryOutputArchive archive(os);
|
|
archive(m_registryEntryRoot);
|
|
}
|
|
|
|
const auto& binFolderPath = GEngine->getRuntimeModule<AssetSystem>()->getProjectBinFolderPath();
|
|
const auto& headerFolderPath = GEngine->getRuntimeModule<AssetSystem>()->getProjectHeaderFolderPath();
|
|
|
|
// Save all dirty asset header.
|
|
std::vector<std::future<void>> scanFutures;
|
|
for (auto& pair : m_assetMap)
|
|
{
|
|
auto& asset = pair.second;
|
|
if (asset->isDirty())
|
|
{
|
|
scanFutures.push_back(GThreadPool::get()->submit([&]()
|
|
{
|
|
if (auto binData = asset->getBinData())
|
|
{
|
|
// Archive bin file.
|
|
std::ofstream os(binFolderPath / binData->getBinUUID(), std::ios::binary);
|
|
cereal::BinaryOutputArchive archive(os);
|
|
archive(binData);
|
|
|
|
asset->saveCallback();
|
|
|
|
// Release owner bin file after archive.
|
|
asset->freeBinData();
|
|
}
|
|
|
|
{
|
|
// Archive header file.
|
|
std::ofstream os(headerFolderPath / asset->getHeaderUUID(), std::ios::binary);
|
|
cereal::BinaryOutputArchive archive(os);
|
|
archive(asset);
|
|
}
|
|
|
|
// After self archive, mark undirty.
|
|
asset->setDirty(false);
|
|
}));
|
|
}
|
|
}
|
|
|
|
for (auto& future : scanFutures)
|
|
{
|
|
future.wait();
|
|
}
|
|
m_bDirty = false;
|
|
}
|
|
|
|
void AssetRegistry::setupProject(
|
|
const std::filesystem::path& registryPath,
|
|
const std::filesystem::path& headerFolderPath,
|
|
const std::filesystem::path& binFolderPath)
|
|
{
|
|
if (!std::filesystem::exists(registryPath))
|
|
{
|
|
m_registryEntryRoot = std::make_shared<RegistryEntry>();
|
|
std::filesystem::remove_all(headerFolderPath);
|
|
std::filesystem::remove_all(binFolderPath);
|
|
std::filesystem::create_directory(headerFolderPath);
|
|
std::filesystem::create_directory(binFolderPath);
|
|
return;
|
|
}
|
|
|
|
// Build asset entry tree.
|
|
{
|
|
std::ifstream is(registryPath, std::ios::binary);
|
|
cereal::BinaryInputArchive archive(is);
|
|
archive(m_registryEntryRoot);
|
|
}
|
|
|
|
// Scan whole header folder. And fill asset map.
|
|
{
|
|
std::unordered_set<std::filesystem::path> unvalidPathSet{ };
|
|
std::vector<std::future<void>> scanFutures;
|
|
|
|
for (auto const& dirEntry : std::filesystem::directory_iterator{ headerFolderPath })
|
|
{
|
|
scanFutures.push_back(GThreadPool::get()->submit([dirEntry, this, &unvalidPathSet]()
|
|
{
|
|
const auto& path = dirEntry.path();
|
|
auto newHeader = std::make_shared<AssetHeaderInterface>();
|
|
{
|
|
std::ifstream is(path, std::ios::binary);
|
|
cereal::BinaryInputArchive archive(is);
|
|
archive(newHeader);
|
|
}
|
|
|
|
if (newHeader->getHeaderUUID() == path.stem().string())
|
|
{
|
|
std::unique_lock lock(m_assetMapMutex);
|
|
|
|
// Store in asset map.
|
|
m_assetMap[newHeader->getHeaderUUID()] = newHeader;
|
|
}
|
|
else
|
|
{
|
|
unvalidPathSet.insert(path);
|
|
}
|
|
}));
|
|
}
|
|
for (auto& future : scanFutures)
|
|
{
|
|
future.wait();
|
|
}
|
|
|
|
for (const auto& pathFileNeedDelete : unvalidPathSet)
|
|
{
|
|
std::filesystem::remove(pathFileNeedDelete);
|
|
LOG_WARN("Path {0} unvalid header cache so delete already.", pathFileNeedDelete.string());
|
|
}
|
|
}
|
|
|
|
// Loop asset entry tree ensure all entry header is valid.
|
|
{
|
|
#if 0
|
|
// Don't loop with delete. this is illegal.
|
|
// Can fix with a temp vector store delte result.
|
|
loopNodeDownToTop([this](std::shared_ptr<RegistryEntry> entry)
|
|
{
|
|
// Unvalid leaf entry which store unvalid asset header id.
|
|
// We need to remove it.
|
|
if (entry->isLeaf() && !m_assetMap.contains(entry->getAssetHeaderID()))
|
|
{
|
|
if (auto parent = entry->getParent().lock())
|
|
{
|
|
AssetRegistryManager::get()->removeChild(parent, entry, false);
|
|
}
|
|
}
|
|
}, m_registryEntryRoot);
|
|
#endif
|
|
|
|
// Copy asset map to erase.
|
|
std::unordered_map<UUID, std::shared_ptr<AssetHeaderInterface>> entryAssetSet = m_assetMap;
|
|
loopNodeTopToDown([this, &entryAssetSet](std::shared_ptr<RegistryEntry> entry)
|
|
{
|
|
// Also cache uuid map.
|
|
addEntry(entry);
|
|
|
|
if (entry->isLeaf())
|
|
{
|
|
entryAssetSet.erase(entry->getAssetHeaderID());
|
|
}
|
|
}, m_registryEntryRoot);
|
|
|
|
// Erase unused header asset.
|
|
for(auto& unusedAsset : entryAssetSet)
|
|
{
|
|
auto& asset = unusedAsset.second;
|
|
|
|
// Remove disk unused header file.
|
|
std::filesystem::remove(headerFolderPath / asset->getHeaderUUID());
|
|
|
|
// Also try remove disk unused bin file.
|
|
const auto binFilePath = binFolderPath / asset->getBinUUID();
|
|
if (std::filesystem::exists(binFilePath))
|
|
{
|
|
std::filesystem::remove(binFilePath);
|
|
}
|
|
|
|
// Remove cache map unused header.
|
|
m_assetMap.erase(asset->getHeaderUUID());
|
|
}
|
|
}
|
|
|
|
// Prepare asset type map
|
|
for(auto& assetPair : m_assetMap)
|
|
{
|
|
m_assetTypeSet[(size_t)assetPair.second->getType()].insert(assetPair.first);
|
|
}
|
|
}
|
|
|
|
std::shared_ptr<AssetHeaderInterface> RegistryEntry::getHeader()
|
|
{
|
|
return AssetRegistryManager::get()->getHeaderMap().at(m_assetHeader);
|
|
}
|
|
|
|
void AssetRegistry::loopNodeDownToTop(
|
|
const std::function<void(std::shared_ptr<RegistryEntry>)>& func,
|
|
std::shared_ptr<RegistryEntry> node)
|
|
{
|
|
auto& children = node->getChildren();
|
|
for (auto& child : children)
|
|
{
|
|
loopNodeDownToTop(func, child);
|
|
}
|
|
|
|
func(node);
|
|
}
|
|
|
|
void AssetRegistry::loopNodeTopToDown(
|
|
const std::function<void(std::shared_ptr<RegistryEntry>)>& func,
|
|
std::shared_ptr<RegistryEntry> node)
|
|
{
|
|
func(node);
|
|
|
|
auto& children = node->getChildren();
|
|
for (auto& child : children)
|
|
{
|
|
loopNodeTopToDown(func, child);
|
|
}
|
|
}
|
|
|
|
AssetHeaderUUID AssetRegistry::importAssetTexture(
|
|
const std::filesystem::path& inPath,
|
|
std::shared_ptr<RegistryEntry> entry,
|
|
bool bSRGB,
|
|
float cutoff,
|
|
bool bBuildMipmap,
|
|
bool bHdr)
|
|
{
|
|
CHECK(!entry->isValid());
|
|
|
|
LOG_INFO("Importing asset {0} from disk...", inPath.string());
|
|
|
|
std::shared_ptr<ImageAssetHeader> newAssetTex = std::make_shared<ImageAssetHeader>(inPath.stem().string());
|
|
newAssetTex->setHdr(bHdr);
|
|
|
|
if (bHdr)
|
|
{
|
|
if (!newAssetTex->initFromRaw2DHDR(inPath, bBuildMipmap))
|
|
{
|
|
return {};
|
|
}
|
|
|
|
|
|
}
|
|
else
|
|
{
|
|
if (!newAssetTex->initFromRaw2DLDR(inPath, bSRGB, cutoff, bBuildMipmap))
|
|
{
|
|
return {};
|
|
}
|
|
}
|
|
|
|
registerAssetMap(newAssetTex, EAssetType::Texture);
|
|
|
|
markDirty();
|
|
return newAssetTex->getHeaderUUID();
|
|
}
|
|
|
|
AssetHeaderUUID AssetRegistry::importStaticMesh(const std::filesystem::path& inPath, std::shared_ptr<RegistryEntry> entry)
|
|
{
|
|
if (m_registryEntryRoot != entry)
|
|
{
|
|
CHECK(!entry->isLeaf());
|
|
}
|
|
|
|
LOG_INFO("Importing asset {0} from disk...", inPath.string());
|
|
|
|
std::shared_ptr<StaticMeshAssetHeader> newAssetMesh = std::make_shared<StaticMeshAssetHeader>(inPath.stem().string());
|
|
if (!newAssetMesh->initFromRawStaticMesh(inPath, entry))
|
|
{
|
|
return {};
|
|
}
|
|
|
|
registerAssetMap(newAssetMesh, EAssetType::StaticMesh);
|
|
|
|
markDirty();
|
|
return newAssetMesh->getHeaderUUID();
|
|
}
|
|
|
|
void AssetRegistry::removeChild(
|
|
std::shared_ptr<RegistryEntry> inParent,
|
|
std::shared_ptr<RegistryEntry> inChild,
|
|
bool bRemoveInMap)
|
|
{
|
|
std::unique_lock lock(m_registryMapMutex);
|
|
|
|
std::vector<std::shared_ptr<RegistryEntry>>::iterator it;
|
|
|
|
size_t id = 0;
|
|
while (inParent->m_children[id] != inChild)
|
|
{
|
|
id++;
|
|
}
|
|
|
|
// Swap to the end and pop.
|
|
if (id < inParent->m_children.size())
|
|
{
|
|
if (bRemoveInMap)
|
|
{
|
|
removeEntry(inParent->m_children[id]);
|
|
}
|
|
|
|
std::swap(
|
|
inParent->m_children[id],
|
|
inParent->m_children[inParent->m_children.size() - 1]);
|
|
inParent->m_children.pop_back();
|
|
}
|
|
|
|
AssetRegistryManager::get()->markDirty();
|
|
}
|
|
|
|
void AssetRegistry::addChild(
|
|
std::shared_ptr<RegistryEntry> inParent,
|
|
std::shared_ptr<RegistryEntry> inChild,
|
|
bool bAddInMap)
|
|
{
|
|
std::unique_lock lock(m_registryMapMutex);
|
|
|
|
inParent->m_children.push_back(inChild);
|
|
inChild->m_parent = inParent;
|
|
|
|
if (bAddInMap)
|
|
{
|
|
addEntry(inChild);
|
|
}
|
|
}
|
|
|
|
bool RegistryEntry::isValid() const
|
|
{
|
|
return AssetRegistryManager::get()->getHeaderMap().contains(m_assetHeader);
|
|
}
|
|
}
|