mirror of
https://github.com/barkeser2002/flower.git
synced 2026-09-25 16:36:15 +03:00
513 lines
15 KiB
C++
513 lines
15 KiB
C++
#pragma once
|
|
|
|
#include "../utils/utils.h"
|
|
#include <engine/graphics/resource.h>
|
|
#include <fstream>
|
|
#include <lz4.h>
|
|
#define CEREAL_THREAD_SAFE 1
|
|
|
|
#include <cereal/access.hpp>
|
|
#include <cereal/types/memory.hpp>
|
|
#include <cereal/types/string.hpp>
|
|
#include <cereal/types/vector.hpp>
|
|
#include <cereal/types/array.hpp>
|
|
#include <cereal/cereal.hpp>
|
|
#include <cereal/types/unordered_map.hpp>
|
|
#include <cereal/types/unordered_set.hpp>
|
|
#include <cereal/types/list.hpp>
|
|
#include <cereal/types/polymorphic.hpp>
|
|
#include <cereal/archives/binary.hpp>
|
|
#include <cereal/archives/xml.hpp>
|
|
#include <cereal/archives/json.hpp>
|
|
#include <cereal/types/base_class.hpp>
|
|
#include <cereal/types/set.hpp>
|
|
#include <cereal/types/map.hpp>
|
|
|
|
#include <rttr/type.h>
|
|
#include <rttr/registration_friend.h>
|
|
|
|
#include <nlohmann/json.hpp>
|
|
#include <tinygltf/tiny_gltf.h>
|
|
#include <profile/profile.h>
|
|
namespace glm
|
|
{
|
|
template<class Archive> void serialize(Archive& archive, glm::vec2& v) { archive(v.x, v.y); }
|
|
template<class Archive> void serialize(Archive& archive, glm::vec3& v) { archive(v.x, v.y, v.z); }
|
|
template<class Archive> void serialize(Archive& archive, glm::vec4& v) { archive(v.x, v.y, v.z, v.w); }
|
|
template<class Archive> void serialize(Archive& archive, glm::ivec2& v) { archive(v.x, v.y); }
|
|
template<class Archive> void serialize(Archive& archive, glm::ivec3& v) { archive(v.x, v.y, v.z); }
|
|
template<class Archive> void serialize(Archive& archive, glm::ivec4& v) { archive(v.x, v.y, v.z, v.w); }
|
|
template<class Archive> void serialize(Archive& archive, glm::uvec2& v) { archive(v.x, v.y); }
|
|
template<class Archive> void serialize(Archive& archive, glm::uvec3& v) { archive(v.x, v.y, v.z); }
|
|
template<class Archive> void serialize(Archive& archive, glm::uvec4& v) { archive(v.x, v.y, v.z, v.w); }
|
|
template<class Archive> void serialize(Archive& archive, glm::dvec2& v) { archive(v.x, v.y); }
|
|
template<class Archive> void serialize(Archive& archive, glm::dvec3& v) { archive(v.x, v.y, v.z); }
|
|
template<class Archive> void serialize(Archive& archive, glm::dvec4& v) { archive(v.x, v.y, v.z, v.w); }
|
|
template<class Archive> void serialize(Archive& archive, glm::mat2& m) { archive(m[0], m[1]); }
|
|
template<class Archive> void serialize(Archive& archive, glm::dmat2& m) { archive(m[0], m[1]); }
|
|
template<class Archive> void serialize(Archive& archive, glm::mat3& m) { archive(m[0], m[1], m[2]); }
|
|
template<class Archive> void serialize(Archive& archive, glm::mat4& m) { archive(m[0], m[1], m[2], m[3]); }
|
|
template<class Archive> void serialize(Archive& archive, glm::dmat4& m) { archive(m[0], m[1], m[2], m[3]); }
|
|
template<class Archive> void serialize(Archive& archive, glm::quat& q) { archive(q.x, q.y, q.z, q.w); }
|
|
template<class Archive> void serialize(Archive& archive, glm::dquat& q) { archive(q.x, q.y, q.z, q.w); }
|
|
}
|
|
|
|
namespace engine
|
|
{
|
|
extern const uint32_t kAssetVersion;
|
|
}
|
|
|
|
#define ARCHIVE_DECLARE \
|
|
friend class cereal::access; \
|
|
template<class Archive> \
|
|
void serialize(Archive& archive, std::uint32_t const version);
|
|
|
|
#define ARCHIVE_NVP_DEFAULT(Member) archive(cereal::make_nvp(#Member, Member))
|
|
|
|
// Version and type registry.
|
|
#define ASSET_ARCHIVE_IMPL_BASIC(AssetNameXX, Version) \
|
|
CEREAL_CLASS_VERSION(engine::AssetNameXX, Version); \
|
|
CEREAL_REGISTER_TYPE_WITH_NAME(engine::AssetNameXX, "engine::"#AssetNameXX);
|
|
|
|
// Virtual children class.
|
|
#define registerClassMemberInherit(AssetNameXX, AssetNamePP) \
|
|
ASSET_ARCHIVE_IMPL_BASIC(AssetNameXX, engine::kAssetVersion); \
|
|
CEREAL_REGISTER_POLYMORPHIC_RELATION(engine::AssetNamePP, engine::AssetNameXX) \
|
|
template<class Archive> \
|
|
void engine::AssetNameXX::serialize(Archive& archive, std::uint32_t const version) { \
|
|
archive(cereal::base_class<engine::AssetNamePP>(this));
|
|
|
|
// Baisc class.
|
|
#define registerClassMember(AssetNameXX) \
|
|
ASSET_ARCHIVE_IMPL_BASIC(AssetNameXX, engine::kAssetVersion); \
|
|
template<class Archive> \
|
|
void engine::AssetNameXX::serialize(Archive& archive, std::uint32_t const version)
|
|
|
|
#define registerPODClassMember(AssetNameXX) \
|
|
CEREAL_CLASS_VERSION(engine::AssetNameXX, engine::kAssetVersion); \
|
|
template<class Archive> \
|
|
void engine::AssetNameXX::serialize(Archive& archive, std::uint32_t const version)
|
|
|
|
#define REGISTER_BODY_DECLARE(...) \
|
|
ARCHIVE_DECLARE \
|
|
RTTR_ENABLE(__VA_ARGS__); \
|
|
RTTR_REGISTRATION_FRIEND();
|
|
|
|
#define ARCHIVE_ENUM_CLASS(value) \
|
|
{ size_t enum__type__##value = (size_t)value; \
|
|
ARCHIVE_NVP_DEFAULT(enum__type__##value); \
|
|
value = (decltype(value))(enum__type__##value); }
|
|
|
|
namespace engine
|
|
{
|
|
|
|
struct AssetImportConfigInterface
|
|
{
|
|
using ImportAssetPath = std::pair<std::filesystem::path, std::filesystem::path>;
|
|
ImportAssetPath path;
|
|
};
|
|
|
|
enum class EAssetType
|
|
{
|
|
darkscene = 0,
|
|
darktexture,
|
|
darkstaticmesh,
|
|
darkmaterial,
|
|
|
|
max
|
|
};
|
|
|
|
struct AssetReflectionInfo
|
|
{
|
|
using ImportConfigPtr = std::shared_ptr<AssetImportConfigInterface>;
|
|
|
|
std::string name;
|
|
std::string icon;
|
|
std::string decoratedName;
|
|
|
|
struct ImportConfig
|
|
{
|
|
bool bImportable;
|
|
std::string importRawAssetExtension;
|
|
std::function<ImportConfigPtr()> buildAssetImportConfig = nullptr;
|
|
std::function<void(ImportConfigPtr)> drawAssetImportConfig = nullptr;
|
|
std::function<bool(ImportConfigPtr)> importAssetFromConfigThreadSafe = nullptr;
|
|
} importConfig;
|
|
};
|
|
|
|
class AssetSaveInfo
|
|
{
|
|
ARCHIVE_DECLARE;
|
|
|
|
public:
|
|
static const u8str kTempFolderStartChar;
|
|
static const u8str kBuiltinFileStartChar;
|
|
|
|
AssetSaveInfo() = default;
|
|
explicit AssetSaveInfo(const u8str& name, const u8str& storeFolder);
|
|
|
|
static AssetSaveInfo buildTemp(const u8str& name);
|
|
static AssetSaveInfo buildRelativeProject(const std::filesystem::path& savePath);
|
|
|
|
public:
|
|
const UUID getUUID() const
|
|
{
|
|
if (isBuiltin())
|
|
{
|
|
return m_name;
|
|
}
|
|
return m_storePath;
|
|
}
|
|
|
|
const std::u16string getStorePath() const
|
|
{
|
|
return utf8::utf8to16(m_storePath);
|
|
}
|
|
|
|
UUID getSnapshotUUID() const
|
|
{
|
|
if (isTemp())
|
|
{
|
|
// Temp info no exist snapshot.
|
|
return getUUID();
|
|
}
|
|
|
|
const size_t hashID = std::hash<UUID>{}(getUUID() + "SnapShotImage");
|
|
return std::to_string(hashID) + "_SnapShotImage";
|
|
}
|
|
|
|
UUID getBinUUID() const
|
|
{
|
|
if (isTemp())
|
|
{
|
|
// Temp info no exist bin.
|
|
return getUUID();
|
|
}
|
|
|
|
const size_t hashID = std::hash<UUID>{}(getUUID() + "BinFile");
|
|
return std::to_string(hashID) + "_BinFile";
|
|
}
|
|
|
|
const u8str& getStorePathU8() const
|
|
{
|
|
return m_storePath;
|
|
}
|
|
|
|
const std::filesystem::path toPath() const;
|
|
|
|
const u8str& getName() const { return m_name; }
|
|
const u8str& getStoreFolder() const { return m_storeFolder; }
|
|
|
|
void setName(const u8str& newValue);
|
|
void setStoreFolder(const u8str& newValue);
|
|
|
|
bool empty() const
|
|
{
|
|
return m_name.empty() || m_storePath.empty();
|
|
}
|
|
|
|
bool isTemp() const;
|
|
bool isBuiltin() const;
|
|
|
|
// Save info can use for new asset create path or not.
|
|
bool canUseForCreateNewAsset() const;
|
|
|
|
// This save info path already in disk.
|
|
bool alreadyInDisk() const;
|
|
|
|
|
|
auto operator<=>(const AssetSaveInfo&) const = default;
|
|
|
|
private:
|
|
void updateStorePath();
|
|
|
|
private:
|
|
// Asset name.
|
|
u8str m_name = {};
|
|
|
|
// Store folder relative to project asset folder.
|
|
u8str m_storeFolder = {};
|
|
|
|
// Store path relative to project asset folder.
|
|
u8str m_storePath = {};
|
|
};
|
|
|
|
struct AssetSnapshot : NonCopyable
|
|
{
|
|
bool empty() const
|
|
{
|
|
return data.empty();
|
|
}
|
|
|
|
math::uvec2 dimension = {0, 0};
|
|
std::vector<uint8_t> data = {};
|
|
|
|
template<class Archive>
|
|
void serialize(Archive& archive)
|
|
{
|
|
archive(dimension, data);
|
|
}
|
|
|
|
public:
|
|
inline static uint32_t kMaxSnapshotDim = 128u;
|
|
|
|
static void quantifySnapshotDim(
|
|
uint32_t& widthSnapShot,
|
|
uint32_t& heightSnapShot,
|
|
uint32_t texWidth,
|
|
uint32_t texHeight)
|
|
{
|
|
if (texWidth >= kMaxSnapshotDim || texHeight >= kMaxSnapshotDim)
|
|
{
|
|
if (texWidth > texHeight)
|
|
{
|
|
widthSnapShot = kMaxSnapshotDim;
|
|
heightSnapShot = texHeight / (texWidth / kMaxSnapshotDim);
|
|
}
|
|
else if (texHeight > texWidth)
|
|
{
|
|
heightSnapShot = kMaxSnapshotDim;
|
|
widthSnapShot = texWidth / (texHeight / kMaxSnapshotDim);
|
|
}
|
|
else
|
|
{
|
|
widthSnapShot = kMaxSnapshotDim;
|
|
heightSnapShot = kMaxSnapshotDim;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
heightSnapShot = texHeight;
|
|
widthSnapShot = texWidth;
|
|
}
|
|
}
|
|
};
|
|
|
|
class AssetInterface : public std::enable_shared_from_this<AssetInterface>
|
|
{
|
|
REGISTER_BODY_DECLARE();
|
|
|
|
public:
|
|
AssetInterface() = default;
|
|
explicit AssetInterface(const AssetSaveInfo& saveInfo);
|
|
virtual ~AssetInterface() = default;
|
|
|
|
// ~AssetInterface virtual function.
|
|
virtual EAssetType getType() const = 0;
|
|
virtual void onPostAssetConstruct() = 0; // Call back when call AssetManager::createAsset
|
|
virtual VulkanImage* getSnapshotImage();
|
|
// ~AssetInterface virtual function.
|
|
|
|
// Get suffix of asset.
|
|
std::string getSuffix() const;
|
|
|
|
// Get asset store path.
|
|
const std::u16string& getStorePath() const { return m_saveInfo.getStorePath(); }
|
|
|
|
// Get asset store name.
|
|
const u8str& getName() const { return m_saveInfo.getName(); }
|
|
|
|
// Get asset store folder.
|
|
const u8str& getStoreFolder() const { return m_saveInfo.getStoreFolder(); }
|
|
|
|
bool isSavePathEmpty() const { return m_saveInfo.empty(); }
|
|
|
|
// Save asset.
|
|
bool save();
|
|
|
|
void unload();
|
|
|
|
// Asset is dirty or not.
|
|
bool isDirty() const { return m_bDirty; }
|
|
|
|
// Mark asset is dirty.
|
|
bool markDirty();
|
|
|
|
// Get shared ptr.
|
|
template<typename T> std::shared_ptr<T> getptr()
|
|
{
|
|
static_assert(std::is_base_of_v<AssetInterface, T>, "T must derive from AssetInterface!");
|
|
return std::static_pointer_cast<T>(AssetInterface::shared_from_this());
|
|
}
|
|
|
|
// Get asset save path.
|
|
std::filesystem::path getSavePath() const;
|
|
|
|
const AssetSaveInfo& getSaveInfo() const { return m_saveInfo; }
|
|
|
|
void discardChanged();
|
|
|
|
bool changeSaveInfo(const AssetSaveInfo& newInfo);
|
|
|
|
UUID getSnapshotUUID() const;
|
|
std::filesystem::path getSnapshotPath() const;
|
|
|
|
std::filesystem::path getRawAssetPath() const;
|
|
|
|
UUID getBinUUID() const;
|
|
std::filesystem::path getBinPath() const;
|
|
|
|
protected:
|
|
// ~AssetInterface virtual function.
|
|
virtual bool saveImpl() = 0;
|
|
|
|
virtual void unloadImpl() = 0;
|
|
// ~AssetInterface virtual function.
|
|
|
|
private:
|
|
// Asset is dirty or not.
|
|
bool m_bDirty = false;
|
|
|
|
protected:
|
|
AssetSaveInfo m_saveInfo = { };
|
|
|
|
// Raw asset path relative to asset folder.
|
|
u8str m_rawAssetPath = {};
|
|
};
|
|
|
|
struct AssetCompressionHelper
|
|
{
|
|
int originalSize;
|
|
int compressionSize;
|
|
|
|
template<class Archive>
|
|
void serialize(Archive& archive)
|
|
{
|
|
archive(originalSize, compressionSize);
|
|
}
|
|
};
|
|
|
|
extern bool loadAssetBinaryWithDecompression(std::vector<uint8_t>& out, const std::filesystem::path& rawSavePath);
|
|
extern bool saveAssetBinaryWithCompression(const uint8_t* out, int size, const std::filesystem::path& savePath, const char* suffix);
|
|
|
|
inline bool saveAssetBinaryWithCompression(const std::vector<uint8_t>& out, const std::filesystem::path& savePath, const char* suffix)
|
|
{
|
|
return saveAssetBinaryWithCompression(out.data(), (int)out.size(), savePath, suffix);
|
|
}
|
|
|
|
template<typename T>
|
|
inline bool loadAsset(T& out, const std::filesystem::path& savePath)
|
|
{
|
|
if (!std::filesystem::exists(savePath))
|
|
{
|
|
LOG_ERROR("Asset data {} miss!", utf8::utf16to8(savePath.u16string()));
|
|
return false;
|
|
}
|
|
|
|
AssetCompressionHelper sizeHelper;
|
|
std::vector<char> compressionData;
|
|
{
|
|
std::ifstream is(savePath, std::ios::binary);
|
|
cereal::BinaryInputArchive archive(is);
|
|
archive(sizeHelper, compressionData);
|
|
}
|
|
|
|
std::vector<char> decompressionData(sizeHelper.originalSize);
|
|
CHECK(sizeHelper.compressionSize == compressionData.size());
|
|
int decompressSize = LZ4_decompress_safe(compressionData.data(), decompressionData.data(), sizeHelper.compressionSize, sizeHelper.originalSize);
|
|
CHECK(decompressSize == sizeHelper.originalSize);
|
|
|
|
// Exist copsaveAssety-construct.
|
|
{
|
|
std::string str(decompressionData.data(), decompressionData.size());
|
|
std::stringstream ss;
|
|
ss << std::move(str);
|
|
cereal::BinaryInputArchive archive(ss);
|
|
archive(out);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
template<typename T>
|
|
inline bool saveAsset(const T& in, const std::filesystem::path& savePath, bool bRequireNoExist = true)
|
|
{
|
|
std::filesystem::path rawSavePath = savePath;
|
|
if (bRequireNoExist && std::filesystem::exists(rawSavePath))
|
|
{
|
|
LOG_ERROR("Meta data {} already exist, make sure never import save resource at same folder!",
|
|
utf8::utf16to8(rawSavePath.u16string()));
|
|
return false;
|
|
}
|
|
|
|
AssetCompressionHelper sizeHelper;
|
|
std::string originalData;
|
|
{
|
|
std::stringstream ss;
|
|
cereal::BinaryOutputArchive archive(ss);
|
|
archive(in);
|
|
originalData = std::move(ss.str());
|
|
}
|
|
|
|
std::vector<char> compressedData(LZ4_compressBound((int)originalData.size()));
|
|
sizeHelper.compressionSize = LZ4_compress_default(
|
|
originalData.c_str(),
|
|
compressedData.data(),
|
|
(int)originalData.size(),
|
|
(int)compressedData.size());
|
|
|
|
compressedData.resize(sizeHelper.compressionSize);
|
|
sizeHelper.originalSize = (int)originalData.size();
|
|
{
|
|
std::ofstream os(rawSavePath, std::ios::binary);
|
|
cereal::BinaryOutputArchive archive(os);
|
|
archive(sizeHelper, compressedData);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
struct StaticMeshRenderBounds
|
|
{
|
|
ARCHIVE_DECLARE;
|
|
|
|
// AABB min = origin - extents.
|
|
// AABB max = origin + extents.
|
|
// AABB center = origin.
|
|
math::vec3 origin;
|
|
math::vec3 extents;
|
|
|
|
float radius;
|
|
};
|
|
|
|
struct StaticMeshSubMesh
|
|
{
|
|
ARCHIVE_DECLARE;
|
|
|
|
uint32_t indicesStart = 0;
|
|
uint32_t indicesCount = 0;
|
|
|
|
// Material of this submesh.
|
|
UUID material = {};
|
|
StaticMeshRenderBounds bounds = {};
|
|
};
|
|
|
|
// Standard index type in this engine.
|
|
using VertexIndexType = uint32_t;
|
|
|
|
// No sure which vertex layout is better.
|
|
// We default use seperate method instead of interleave.
|
|
// https://frostbite-wp-prd.s3.amazonaws.com/wp-content/uploads/2016/03/29204330/GDC_2016_Compute.pdf
|
|
// https://developer.android.com/games/optimize/vertex-data-management?hl=zh-tw
|
|
using VertexPosition = math::vec3;
|
|
static_assert(sizeof(VertexPosition) == sizeof(float) * 3);
|
|
|
|
using VertexNormal = math::vec3;
|
|
static_assert(sizeof(VertexNormal) == sizeof(float) * 3);
|
|
|
|
using VertexTangent = math::vec4;
|
|
static_assert(sizeof(VertexTangent) == sizeof(float) * 4);
|
|
|
|
using VertexUv0 = math::vec2;
|
|
static_assert(sizeof(VertexUv0) == sizeof(float) * 2);
|
|
|
|
struct StaticMeshBin
|
|
{
|
|
ARCHIVE_DECLARE;
|
|
|
|
std::vector<VertexPosition> positions;
|
|
std::vector<VertexNormal> normals;
|
|
std::vector<VertexTangent> tangents;
|
|
std::vector<VertexUv0> uv0s;
|
|
std::vector<VertexIndexType> indices;
|
|
};
|
|
} |