// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
#pragma once
#include "Asset.h"
#include "Engine/Core/Types/Pair.h"
#include "Storage/FlaxStorageReference.h"
// Helper define
#define DECLARE_BINARY_ASSET_HEADER(type, serializedVersion) \
DECLARE_ASSET_HEADER(type); \
static const uint32 SerializedVersion = serializedVersion; \
uint32 GetSerializedVersion() const override { return SerializedVersion; } \
static_assert(true, "")
#define REGISTER_BINARY_ASSET_ABSTRACT(type, typeName) \
const String type::TypeName = TEXT(typeName)
///
/// Base class for all binary assets.
///
///
API_CLASS(Abstract, NoSpawn) class FLAXENGINE_API BinaryAsset : public Asset
{
DECLARE_ASSET_HEADER(BinaryAsset);
protected:
AssetHeader _header;
FlaxStorageReference _storageRef; // Allow asset to have missing storage reference but only before asset is loaded or if it's virtual
bool _isSaving;
Array _dependantAssets;
public:
///
/// Finalizes an instance of the class.
///
~BinaryAsset();
public:
///
/// The asset storage container.
///
FlaxStorage* Storage;
#if USE_EDITOR
///
/// The asset metadata information. Stored in a Json format.
///
BytesContainer Metadata;
///
/// Asset dependencies list used by the asset for tracking (eg. material functions used by material asset). The pair of asset ID and cached file edit time (for tracking modification).
///
Array> Dependencies;
#endif
public:
///
/// Gets the asset serialized version.
///
virtual uint32 GetSerializedVersion() const = 0;
///
/// Initializes the specified asset with asset header and storage only. Asset should be initialized later using will AssetInitData.
///
/// The storage container.
/// The asset header.
bool Init(const FlaxStorageReference& storage, AssetHeader& header);
///
/// Initializes the specified asset.
///
/// The initialize data.
/// True if cannot init, otherwise false
bool Init(AssetInitData& initData);
///
/// Initializes the specified asset (as virtual).
///
/// The initialize data.
/// True if cannot init, otherwise false
bool InitVirtual(AssetInitData& initData);
public:
#if USE_EDITOR
#if COMPILE_WITH_ASSETS_IMPORTER
///
/// Reimports asset from the source file.
///
API_FUNCTION() void Reimport() const;
#endif
///
/// Returns imported file metadata.
///
/// The import path.
/// The import username.
void GetImportMetadata(String& path, String& username) const;
///
/// Gets the imported file path from the asset metadata (can be empty if not available).
///
API_PROPERTY() String GetImportPath() const;
///
/// Clears the asset dependencies list and unregisters from tracking their changes.
///
void ClearDependencies();
///
/// Adds the dependency to the given asset.
///
/// The asset to depend on.
void AddDependency(BinaryAsset* asset);
///
/// Determines whether any of the dependency assets was modified after last modification time of this asset (last file write time check).
///
bool HasDependenciesModified() const;
protected:
///
/// Called when one of the asset dependencies gets modified (it was saved or reloaded or reimported).
///
/// The modified asset that this asset depends on.
virtual void OnDependencyModified(BinaryAsset* asset)
{
}
#endif
protected:
///
/// Initializes the specified asset.
///
/// The initialize data.
/// True if cannot init, otherwise false
virtual bool init(AssetInitData& initData)
{
return false;
}
///
/// Gets packed chunks indices to preload before asset loading action
///
/// Chunks to load flags
virtual AssetChunksFlag getChunksToPreload() const
{
return 0;
}
public:
///
/// Gets the asset chunk.
///
/// The chunk index.
/// Flax Chunk object or null if is missing
FlaxChunk* GetChunk(int32 index) const
{
ASSERT(index >= 0 && index < ASSET_FILE_DATA_CHUNKS);
auto chunk = _header.Chunks[index];
if (chunk)
chunk->RegisterUsage();
return chunk;
}
///
/// Gets or creates the asset chunk.
///
/// The chunk index.
/// Flax Chunk object (may be null if asset storage doesn't allow to add new chunks).
FlaxChunk* GetOrCreateChunk(int32 index);
///
/// Determines whether the specified chunk exists.
///
/// The chunk index.
/// True if the specified chunk exists, otherwise false.
bool HasChunk(int32 index) const
{
ASSERT(index >= 0 && index < ASSET_FILE_DATA_CHUNKS);
return _header.Chunks[index] != nullptr;
}
///
/// Gets the chunk size (in bytes).
///
/// The chunk index.
/// Chunk size (in bytes)
uint32 GetChunkSize(int32 index) const
{
ASSERT(index >= 0 && index < ASSET_FILE_DATA_CHUNKS);
return _header.Chunks[index] != nullptr ? _header.Chunks[index]->LocationInFile.Size : 0;
}
///
/// Determines whether the specified chunk exists and is loaded.
///
/// The chunk index.
/// True if the specified chunk exists and is loaded, otherwise false.
bool HasChunkLoaded(int32 index) const
{
ASSERT(index >= 0 && index < ASSET_FILE_DATA_CHUNKS);
return _header.Chunks[index] != nullptr && _header.Chunks[index]->IsLoaded();
}
///
/// Sets the chunk data (creates new chunk if is missing).
///
/// The chunk index.
/// The chunk data to set.
void SetChunk(int32 index, const Span& data);
///
/// Releases all the chunks data.
///
void ReleaseChunks() const;
///
/// Releases the chunk data (if loaded).
///
/// The chunk index.
void ReleaseChunk(int32 index) const;
///
/// Requests the chunk data asynchronously (creates task that will gather chunk data or null if already here).
///
/// The chunk index.
/// Task that will gather chunk data or null if already here.
ContentLoadTask* RequestChunkDataAsync(int32 index);
///
/// Gets chunk data. May fail if not data requested. See RequestChunkDataAsync.
///
/// The chunk index.
/// Result data (linked chunk data, not copied).
void GetChunkData(int32 index, BytesContainer& data) const;
///
/// Loads the chunk (synchronous, blocks current thread).
///
/// The chunk index to load.
/// True if failed, otherwise false.
bool LoadChunk(int32 chunkIndex);
///
/// Loads the chunks (synchronous, blocks current thread).
///
/// The chunks to load (packed in a flag).
/// True if failed, otherwise false.
bool LoadChunks(AssetChunksFlag chunks);
#if USE_EDITOR
///
/// Saves this asset to the storage container.
///
/// Asset data.
/// In silent mode don't reload opened storage container that is using target file.
/// True if failed, otherwise false.
bool SaveAsset(AssetInitData& data, bool silentMode = false) const;
///
/// Saves this asset to the file.
///
/// Asset data.
/// Asset path (will be used to override the asset or create a new one).
/// In silent mode don't reload opened storage container that is using target file.
/// True if failed, otherwise false.
bool SaveAsset(const StringView& path, AssetInitData& data, bool silentMode = false) const;
///
/// Saves asset data to the storage container. Asset unique ID is handled by auto.
///
/// Asset path (will be used to override the asset or create a new one).
/// Asset data.
/// In silent mode don't reload opened storage container that is using target file.
/// True if failed, otherwise false.
static bool SaveToAsset(const StringView& path, AssetInitData& data, bool silentMode = false);
#endif
protected:
///
/// Load data from the chunks
///
/// Loading result
virtual LoadResult load() = 0;
private:
#if USE_EDITOR
void OnStorageReloaded(FlaxStorage* storage, bool failed);
#endif
public:
// [Asset]
#if USE_EDITOR
void OnDeleteObject() override;
#endif
const String& GetPath() const final override;
uint64 GetMemoryUsage() const override;
protected:
// [Asset]
ContentLoadTask* createLoadingTask() override;
LoadResult loadAsset() override;
void releaseStorage() final override;
#if USE_EDITOR
void onRename(const StringView& newPath) override;
#endif
};