feat: move editor to project / add post processing etc
This commit is contained in:
@@ -0,0 +1,177 @@
|
||||
#include <destrum/Assets/AssetDatabase.h>
|
||||
|
||||
#include <charconv>
|
||||
#include <filesystem>
|
||||
#include <format>
|
||||
#include <fstream>
|
||||
#include <random>
|
||||
|
||||
#include <spdlog/spdlog.h>
|
||||
|
||||
#include <destrum/Assets/AssetTypes.h>
|
||||
#include <destrum/FS/AssetFS.h>
|
||||
|
||||
namespace {
|
||||
[[nodiscard]] bool IsVirtualPath(std::string_view path)
|
||||
{
|
||||
return path.find("://") != std::string_view::npos;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::optional<AssetGuid> ReadMetaFile(const std::filesystem::path& path)
|
||||
{
|
||||
std::ifstream file(path);
|
||||
if (!file) {
|
||||
return std::nullopt;
|
||||
}
|
||||
try {
|
||||
const auto meta = nlohmann::json::parse(file);
|
||||
return AssetGuid::Parse(meta.at("guid").get<std::string>());
|
||||
} catch (const std::exception& exception) {
|
||||
spdlog::warn("Ignoring broken asset meta file {}: {}", path.string(), exception.what());
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AssetGuid AssetGuid::Generate()
|
||||
{
|
||||
static std::mt19937_64 random{std::random_device{}() ^ (static_cast<std::uint64_t>(std::random_device{}()) << 32)};
|
||||
AssetGuid guid;
|
||||
while (!guid.IsValid()) {
|
||||
guid = {random(), random()};
|
||||
}
|
||||
return guid;
|
||||
}
|
||||
|
||||
std::optional<AssetGuid> AssetGuid::Parse(std::string_view text)
|
||||
{
|
||||
if (text.size() != 32) {
|
||||
return std::nullopt;
|
||||
}
|
||||
AssetGuid guid;
|
||||
const auto parseHalf = [](std::string_view half, std::uint64_t& out) {
|
||||
const auto [end, error] = std::from_chars(half.data(), half.data() + half.size(), out, 16);
|
||||
return error == std::errc{} && end == half.data() + half.size();
|
||||
};
|
||||
if (!parseHalf(text.substr(0, 16), guid.high) || !parseHalf(text.substr(16), guid.low) || !guid.IsValid()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return guid;
|
||||
}
|
||||
|
||||
std::string AssetGuid::ToString() const
|
||||
{
|
||||
return std::format("{:016x}{:016x}", high, low);
|
||||
}
|
||||
|
||||
AssetDatabase::MetaFileScope::MetaFileScope()
|
||||
{
|
||||
++AssetDatabase::GetInstance().m_MetaFileScopes;
|
||||
}
|
||||
|
||||
AssetDatabase::MetaFileScope::~MetaFileScope()
|
||||
{
|
||||
--AssetDatabase::GetInstance().m_MetaFileScopes;
|
||||
}
|
||||
|
||||
void AssetDatabase::Clear()
|
||||
{
|
||||
m_Guids.clear();
|
||||
m_Paths.clear();
|
||||
}
|
||||
|
||||
void AssetDatabase::Refresh()
|
||||
{
|
||||
Clear();
|
||||
for (const FSMount& mount : AssetFS::GetInstance().GetMounts()) {
|
||||
std::error_code error;
|
||||
for (const auto& entry : std::filesystem::recursive_directory_iterator(mount.root, error)) {
|
||||
if (!entry.is_regular_file(error) || !AssetTypes::IsMeta(entry.path().string())) {
|
||||
continue;
|
||||
}
|
||||
if (const auto guid = ReadMetaFile(entry.path())) {
|
||||
std::filesystem::path asset = entry.path();
|
||||
asset.replace_extension();
|
||||
Register(mount.scheme + "://" + asset.lexically_relative(mount.root).generic_string(), *guid);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AssetDatabase::Register(std::string_view path, const AssetGuid& guid)
|
||||
{
|
||||
if (const auto existing = m_Paths.find(guid); existing != m_Paths.end() && existing->second != path) {
|
||||
spdlog::warn("Assets {} and {} share the id {}, copy assets without their .meta file",
|
||||
existing->second, path, guid.ToString());
|
||||
return;
|
||||
}
|
||||
m_Guids.insert_or_assign(std::string{path}, guid);
|
||||
m_Paths.insert_or_assign(guid, std::string{path});
|
||||
}
|
||||
|
||||
std::optional<AssetGuid> AssetDatabase::FindGuid(std::string_view path) const
|
||||
{
|
||||
const auto it = m_Guids.find(std::string{path});
|
||||
return it != m_Guids.end() ? std::optional{it->second} : std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<std::string> AssetDatabase::FindPath(const AssetGuid& guid) const
|
||||
{
|
||||
const auto it = m_Paths.find(guid);
|
||||
return it != m_Paths.end() ? std::optional{it->second} : std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<AssetGuid> AssetDatabase::GuidForSave(std::string_view path)
|
||||
{
|
||||
if (auto guid = FindGuid(path)) {
|
||||
return guid;
|
||||
}
|
||||
if (m_MetaFileScopes == 0 || !IsVirtualPath(path)) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
auto& assetFs = AssetFS::GetInstance();
|
||||
std::error_code error;
|
||||
if (!std::filesystem::is_regular_file(assetFs.GetFullPath(path), error)) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
const AssetGuid guid = AssetGuid::Generate();
|
||||
const nlohmann::json meta{{"guid", guid.ToString()}};
|
||||
if (!assetFs.WriteFile(std::string{path} + std::string{AssetTypes::MetaExtension}, meta.dump(4))) {
|
||||
return std::nullopt;
|
||||
}
|
||||
Register(path, guid);
|
||||
return guid;
|
||||
}
|
||||
|
||||
nlohmann::json AssetDatabase::WriteReference(std::string_view path, std::string_view subresource)
|
||||
{
|
||||
nlohmann::json reference{{"path", path}};
|
||||
if (!subresource.empty()) {
|
||||
reference["sub"] = subresource;
|
||||
}
|
||||
if (const auto guid = GuidForSave(path)) {
|
||||
reference["guid"] = guid->ToString();
|
||||
}
|
||||
return reference;
|
||||
}
|
||||
|
||||
std::optional<AssetReference> AssetDatabase::ReadReference(const nlohmann::json& reference) const
|
||||
{
|
||||
if (!reference.is_object() || !reference.contains("path") || !reference.at("path").is_string()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
AssetReference result{
|
||||
.path = reference.at("path").get<std::string>(),
|
||||
.subresource = reference.value("sub", std::string{}),
|
||||
};
|
||||
if (const auto guid = AssetGuid::Parse(reference.value("guid", std::string{}))) {
|
||||
if (auto current = FindPath(*guid); current && *current != result.path) {
|
||||
spdlog::info("{} moved to {}, following its id", result.path, *current);
|
||||
result.path = std::move(*current);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
Reference in New Issue
Block a user