Add content deprecation system that auto-saves assets in Editor that use old data format

This commit is contained in:
Wojtek Figat
2025-01-20 23:46:49 +01:00
parent 1497acef58
commit 8a7ceef288
67 changed files with 751 additions and 427 deletions

View File

@@ -226,16 +226,8 @@ void Animation::LoadTimeline(BytesContainer& result) const
bool Animation::SaveTimeline(BytesContainer& data)
{
// Wait for asset to be loaded or don't if last load failed (eg. by shader source compilation error)
if (LastLoadFailed())
{
LOG(Warning, "Saving asset that failed to load.");
}
else if (WaitForLoaded())
{
LOG(Error, "Asset loading failed. Cannot save it.");
if (OnCheckSave())
return true;
}
ScopeLock lock(Locker);
MemoryReadStream stream(data.Get(), data.Length());
@@ -401,17 +393,8 @@ bool Animation::SaveTimeline(BytesContainer& data)
bool Animation::Save(const StringView& path)
{
// Wait for asset to be loaded or don't if last load failed (eg. by shader source compilation error)
if (LastLoadFailed())
{
LOG(Warning, "Saving asset that failed to load.");
}
else if (WaitForLoaded())
{
LOG(Error, "Asset loading failed. Cannot save it.");
if (OnCheckSave(path))
return true;
}
ScopeLock lock(Locker);
// Serialize animation data to the stream

View File

@@ -138,17 +138,10 @@ public:
/// <summary>
/// Saves the serialized timeline data to the asset as animation.
/// </summary>
/// <remarks>The cannot be used by virtual assets.</remarks>
/// <remarks>This cannot be used by virtual assets.</remarks>
/// <param name="data">The timeline data container.</param>
/// <returns><c>true</c> failed to save data; otherwise, <c>false</c>.</returns>
API_FUNCTION() bool SaveTimeline(BytesContainer& data);
/// <summary>
/// Saves the animation data to the asset. Supported only in Editor.
/// </summary>
/// <remarks>The cannot be used by virtual assets.</remarks>
/// <returns><c>true</c> failed to save data; otherwise, <c>false</c>.</returns>
bool Save(const StringView& path = StringView::Empty);
#endif
private:
@@ -161,6 +154,7 @@ public:
// [BinaryAsset]
#if USE_EDITOR
void GetReferences(Array<Guid>& assets, Array<String>& files) const override;
bool Save(const StringView& path = StringView::Empty) override;
#endif
uint64 GetMemoryUsage() const override;
void OnScriptingDispose() override;

View File

@@ -127,7 +127,7 @@ bool AnimationGraph::InitAsAnimation(SkinnedModel* baseModel, Animation* anim, b
return Graph.Load(&readStream, USE_EDITOR);
}
BytesContainer AnimationGraph::LoadSurface()
BytesContainer AnimationGraph::LoadSurface() const
{
if (!IsVirtual() && WaitForLoaded())
return BytesContainer();
@@ -160,19 +160,10 @@ BytesContainer AnimationGraph::LoadSurface()
#if USE_EDITOR
bool AnimationGraph::SaveSurface(BytesContainer& data)
bool AnimationGraph::SaveSurface(const BytesContainer& data)
{
// Wait for asset to be loaded or don't if last load failed
if (LastLoadFailed())
{
LOG(Warning, "Saving asset that failed to load.");
}
else if (WaitForLoaded())
{
LOG(Error, "Asset loading failed. Cannot save it.");
if (OnCheckSave())
return true;
}
ScopeLock lock(Locker);
if (IsVirtual())
@@ -228,4 +219,17 @@ void AnimationGraph::GetReferences(Array<Guid>& assets, Array<String>& files) co
Graph.GetReferences(assets);
}
bool AnimationGraph::Save(const StringView& path)
{
if (OnCheckSave(path))
return true;
ScopeLock lock(Locker);
MemoryWriteStream writeStream;
if (Graph.Save(&writeStream, true))
return true;
BytesContainer data;
data.Link(ToSpan(writeStream));
return SaveSurface(data);
}
#endif

View File

@@ -45,7 +45,7 @@ public:
/// Tries to load surface graph from the asset.
/// </summary>
/// <returns>The surface data or empty if failed to load it.</returns>
API_FUNCTION() BytesContainer LoadSurface();
API_FUNCTION() BytesContainer LoadSurface() const;
#if USE_EDITOR
@@ -54,7 +54,7 @@ public:
/// </summary>
/// <param name="data">Stream with graph data.</param>
/// <returns>True if cannot save it, otherwise false.</returns>
API_FUNCTION() bool SaveSurface(BytesContainer& data);
API_FUNCTION() bool SaveSurface(const BytesContainer& data);
private:
void FindDependencies(AnimGraphBase* graph);
@@ -65,6 +65,7 @@ public:
// [BinaryAsset]
#if USE_EDITOR
void GetReferences(Array<Guid>& assets, Array<String>& files) const override;
bool Save(const StringView& path = StringView::Empty) override;
#endif
protected:

View File

@@ -4,6 +4,9 @@
#include "Engine/Core/Log.h"
#include "Engine/Core/Types/DataContainer.h"
#include "Engine/Serialization/MemoryReadStream.h"
#if USE_EDITOR
#include "Engine/Serialization/MemoryWriteStream.h"
#endif
#include "Engine/Content/Factories/BinaryAssetFactory.h"
#include "Engine/Threading/Threading.h"
@@ -84,19 +87,10 @@ void AnimationGraphFunction::GetSignature(Array<StringView, FixedAllocation<32>>
}
}
bool AnimationGraphFunction::SaveSurface(const BytesContainer& data)
bool AnimationGraphFunction::SaveSurface(const BytesContainer& data) const
{
// Wait for asset to be loaded or don't if last load failed
if (LastLoadFailed())
{
LOG(Warning, "Saving asset that failed to load.");
}
else if (WaitForLoaded())
{
LOG(Error, "Asset loading failed. Cannot save it.");
if (OnCheckSave())
return true;
}
ScopeLock lock(Locker);
// Set Visject Surface data
@@ -185,3 +179,24 @@ void AnimationGraphFunction::ProcessGraphForSignature(AnimGraphBase* graph, bool
}
}
}
#if USE_EDITOR
bool AnimationGraphFunction::Save(const StringView& path)
{
if (OnCheckSave(path))
return true;
ScopeLock lock(Locker);
AnimGraph graph(const_cast<AnimationGraphFunction*>(this), true);
MemoryReadStream readStream(GraphData.Get(), GraphData.Length());
if (graph.Load(&readStream, true))
return true;
MemoryWriteStream writeStream;
if (graph.Save(&writeStream, true))
return true;
BytesContainer data;
data.Link(ToSpan(writeStream));
return SaveSurface(data);
}
#endif

View File

@@ -53,13 +53,19 @@ public:
/// </summary>
/// <param name="data">The surface graph data.</param>
/// <returns>True if cannot save it, otherwise false.</returns>
API_FUNCTION() bool SaveSurface(const BytesContainer& data);
API_FUNCTION() bool SaveSurface(const BytesContainer& data) const;
#endif
private:
void ProcessGraphForSignature(AnimGraphBase* graph, bool canUseOutputs);
public:
// [BinaryAsset]
#if USE_EDITOR
bool Save(const StringView& path = StringView::Empty) override;
#endif
protected:
// [BinaryAsset]
LoadResult load() override;

View File

@@ -191,12 +191,13 @@ Asset::LoadResult Material::load()
auto lock = Storage->Lock();
// Prepare
const String name = ToString();
MaterialGenerator generator;
generator.Error.Bind(&OnGeneratorError);
if (_shaderHeader.Material.GraphVersion != MATERIAL_GRAPH_VERSION)
LOG(Info, "Converting material \'{0}\', from version {1} to {2}...", ToString(), _shaderHeader.Material.GraphVersion, MATERIAL_GRAPH_VERSION);
LOG(Info, "Converting material \'{0}\', from version {1} to {2}...", name, _shaderHeader.Material.GraphVersion, MATERIAL_GRAPH_VERSION);
else
LOG(Info, "Updating material \'{0}\'...", ToString());
LOG(Info, "Updating material \'{0}\'...", name);
// Load or create material surface
MaterialLayer* layer;
@@ -205,7 +206,7 @@ Asset::LoadResult Material::load()
// Load graph
if (LoadChunks(GET_CHUNK_FLAG(SHADER_FILE_CHUNK_VISJECT_SURFACE)))
{
LOG(Warning, "Cannot load \'{0}\' data from chunk {1}.", ToString(), SHADER_FILE_CHUNK_VISJECT_SURFACE);
LOG(Warning, "Cannot load \'{0}\' data from chunk {1}.", name, SHADER_FILE_CHUNK_VISJECT_SURFACE);
return LoadResult::Failed;
}
@@ -214,7 +215,19 @@ Asset::LoadResult Material::load()
MemoryReadStream stream(surfaceChunk->Get(), surfaceChunk->Size());
// Load layer
layer = MaterialLayer::Load(GetID(), &stream, _shaderHeader.Material.Info, ToString());
layer = MaterialLayer::Load(GetID(), &stream, _shaderHeader.Material.Info, name);
if (ContentDeprecated::Clear())
{
// If encountered any deprecated data when loading graph then serialize it
MaterialGraph graph;
MemoryWriteStream writeStream(1024);
stream.SetPosition(0);
if (!graph.Load(&stream, true) && !graph.Save(&writeStream, true))
{
surfaceChunk->Data.Copy(ToSpan(writeStream));
ContentDeprecated::Clear();
}
}
}
else
{
@@ -245,7 +258,7 @@ Asset::LoadResult Material::load()
MaterialInfo info = _shaderHeader.Material.Info;
if (generator.Generate(source, info, materialParamsChunk->Data))
{
LOG(Error, "Cannot generate material source code for \'{0}\'. Please see log for more info.", ToString());
LOG(Error, "Cannot generate material source code for \'{0}\'. Please see log for more info.", name);
return LoadResult::Failed;
}
@@ -282,9 +295,9 @@ Asset::LoadResult Material::load()
// Save to file
#if USE_EDITOR
if (Save())
if (SaveShaderAsset())
{
LOG(Error, "Cannot save \'{0}\'", ToString());
LOG(Error, "Cannot save \'{0}\'", name);
return LoadResult::Failed;
}
#endif
@@ -505,6 +518,25 @@ void Material::InitCompilationOptions(ShaderCompilationOptions& options)
#endif
}
bool Material::Save(const StringView& path)
{
if (OnCheckSave(path))
return true;
ScopeLock lock(Locker);
BytesContainer existingData = LoadSurface(true);
if (existingData.IsInvalid())
return true;
MaterialGraph graph;
MemoryWriteStream writeStream(existingData.Length());
MemoryReadStream readStream(existingData);
if (graph.Load(&readStream, true) || graph.Save(&writeStream, true))
return true;
BytesContainer data;
data.Link(ToSpan(writeStream));
auto materialInfo = _shaderHeader.Material.Info;
return SaveSurface(data, materialInfo);
}
#endif
BytesContainer Material::LoadSurface(bool createDefaultIfMissing)
@@ -555,17 +587,8 @@ BytesContainer Material::LoadSurface(bool createDefaultIfMissing)
bool Material::SaveSurface(const BytesContainer& data, const MaterialInfo& info)
{
// Wait for asset to be loaded or don't if last load failed (eg. by shader source compilation error)
if (LastLoadFailed())
{
LOG(Warning, "Saving asset that failed to load.");
}
else if (WaitForLoaded())
{
LOG(Error, "Asset loading failed. Cannot save it.");
if (OnCheckSave())
return true;
}
ScopeLock lock(Locker);
// Release all chunks
@@ -582,7 +605,7 @@ bool Material::SaveSurface(const BytesContainer& data, const MaterialInfo& info)
ASSERT(visjectSurfaceChunk != nullptr);
visjectSurfaceChunk->Data.Copy(data);
if (Save())
if (SaveShaderAsset())
{
LOG(Error, "Cannot save \'{0}\'", ToString());
return true;

View File

@@ -13,6 +13,7 @@ class MaterialShader;
API_CLASS(NoSpawn) class FLAXENGINE_API Material : public ShaderAssetTypeBase<MaterialBase>
{
DECLARE_BINARY_ASSET_HEADER(Material, ShadersSerializedVersion);
private:
MaterialShader* _materialShader = nullptr;
@@ -25,7 +26,6 @@ public:
API_FUNCTION() BytesContainer LoadSurface(bool createDefaultIfMissing);
#if USE_EDITOR
/// <summary>
/// Updates the material surface (save new one, discard cached data, reload asset).
/// </summary>
@@ -33,7 +33,6 @@ public:
/// <param name="info">The material info structure.</param>
/// <returns>True if cannot save it, otherwise false.</returns>
API_FUNCTION() bool SaveSurface(const BytesContainer& data, const MaterialInfo& info);
#endif
public:
@@ -52,6 +51,7 @@ public:
// [ShaderAssetBase]
#if USE_EDITOR
void InitCompilationOptions(ShaderCompilationOptions& options) override;
bool Save(const StringView& path = StringView::Empty) override;
#endif
protected:

View File

@@ -6,6 +6,7 @@
#include "Engine/Core/Log.h"
#include "Engine/Core/Types/DataContainer.h"
#include "Engine/Serialization/MemoryReadStream.h"
#include "Engine/Serialization/MemoryWriteStream.h"
#endif
#include "Engine/Content/Factories/BinaryAssetFactory.h"
@@ -24,7 +25,7 @@ Asset::LoadResult MaterialFunction::load()
if (!surfaceChunk || !surfaceChunk->IsLoaded())
return LoadResult::MissingDataChunk;
MemoryReadStream stream(surfaceChunk->Get(), surfaceChunk->Size());
if (Graph.Load(&stream, false))
if (Graph.Load(&stream, USE_EDITOR))
return LoadResult::Failed;
// Cache input and output nodes
@@ -89,7 +90,7 @@ BytesContainer MaterialFunction::LoadSurface()
return result;
}
bool MaterialFunction::LoadSurface(MaterialGraph& graph)
bool MaterialFunction::LoadSurface(MaterialGraph& graph, bool loadMeta)
{
if (WaitForLoaded())
return true;
@@ -100,7 +101,7 @@ bool MaterialFunction::LoadSurface(MaterialGraph& graph)
{
const auto surfaceChunk = GetChunk(0);
MemoryReadStream stream(surfaceChunk->Get(), surfaceChunk->Size());
return graph.Load(&stream, false);
return graph.Load(&stream, loadMeta);
}
}
return true;
@@ -128,19 +129,10 @@ void MaterialFunction::GetSignature(Array<StringView, FixedAllocation<32>>& type
#if USE_EDITOR
bool MaterialFunction::SaveSurface(BytesContainer& data)
bool MaterialFunction::SaveSurface(const BytesContainer& data) const
{
// Wait for asset to be loaded or don't if last load failed
if (LastLoadFailed())
{
LOG(Warning, "Saving asset that failed to load.");
}
else if (WaitForLoaded())
{
LOG(Error, "Asset loading failed. Cannot save it.");
if (OnCheckSave())
return true;
}
ScopeLock lock(Locker);
// Set Visject Surface data
@@ -160,4 +152,17 @@ bool MaterialFunction::SaveSurface(BytesContainer& data)
return false;
}
bool MaterialFunction::Save(const StringView& path)
{
if (OnCheckSave(path))
return true;
ScopeLock lock(Locker);
MemoryWriteStream writeStream;
if (Graph.Save(&writeStream, true))
return true;
BytesContainer data;
data.Link(ToSpan(writeStream));
return SaveSurface(data);
}
#endif

View File

@@ -11,9 +11,9 @@
API_CLASS(NoSpawn) class FLAXENGINE_API MaterialFunction : public BinaryAsset
{
DECLARE_BINARY_ASSET_HEADER(MaterialFunction, 1);
public:
#if COMPILE_WITH_MATERIAL_GRAPH
/// <summary>
/// The loaded material function graph.
/// </summary>
@@ -39,8 +39,9 @@ public:
/// Tries to load surface graph from the asset.
/// </summary>
/// <param name="graph">The graph to load.</param>
/// <param name="loadMeta">True if load metadata.</param>
/// <returns>True if failed, otherwise false.</returns>
bool LoadSurface(MaterialGraph& graph);
bool LoadSurface(MaterialGraph& graph, bool loadMeta = false);
// Gets the function signature for Visject Surface editor.
API_FUNCTION() void GetSignature(API_PARAM(Out) Array<StringView, FixedAllocation<32>>& types, API_PARAM(Out) Array<StringView, FixedAllocation<32>>& names);
@@ -48,14 +49,18 @@ public:
#endif
#if USE_EDITOR
/// <summary>
/// Updates the material graph surface (save new one, discards cached data, reloads asset).
/// </summary>
/// <param name="data">The surface graph data.</param>
/// <returns>True if cannot save it, otherwise false.</returns>
API_FUNCTION() bool SaveSurface(BytesContainer& data);
API_FUNCTION() bool SaveSurface(const BytesContainer& data) const;
#endif
public:
// [BinaryAsset]
#if USE_EDITOR
bool Save(const StringView& path = StringView::Empty) override;
#endif
protected:

View File

@@ -310,18 +310,8 @@ void MaterialInstance::ResetParameters()
bool MaterialInstance::Save(const StringView& path)
{
// Validate state
if (WaitForLoaded())
{
LOG(Error, "Asset loading failed. Cannot save it.");
if (OnCheckSave(path))
return true;
}
if (IsVirtual() && path.IsEmpty())
{
LOG(Error, "To save virtual asset asset you need to specify the target asset path location.");
return true;
}
ScopeLock lock(Locker);
// Save instance data

View File

@@ -33,18 +33,6 @@ public:
/// </summary>
API_FUNCTION() void ResetParameters();
#if USE_EDITOR
/// <summary>
/// Saves this asset to the file. Supported only in Editor.
/// </summary>
/// <remarks>If you use saving with the GPU mesh data then the call has to be provided from the thread other than the main game thread.</remarks>
/// <param name="path">The custom asset path to use for the saving. Use empty value to save this asset to its own storage location. Can be used to duplicate asset. Must be specified when saving virtual asset.</param>
/// <returns>True if cannot save data, otherwise false.</returns>
API_FUNCTION() bool Save(const StringView& path = StringView::Empty);
#endif
private:
void OnBaseSet();
void OnBaseUnset();
@@ -56,6 +44,7 @@ public:
bool IsMaterialInstance() const override;
#if USE_EDITOR
void GetReferences(Array<Guid>& assets, Array<String>& files) const override;
bool Save(const StringView& path = StringView::Empty) override;
#endif
// [IMaterial]

View File

@@ -400,7 +400,7 @@ bool Model::LoadHeader(ReadStream& stream, byte& headerVersion)
#if USE_EDITOR
bool Model::SaveHeader(WriteStream& stream)
bool Model::SaveHeader(WriteStream& stream) const
{
if (ModelBase::SaveHeader(stream))
return true;
@@ -457,7 +457,7 @@ bool Model::SaveHeader(WriteStream& stream, const ModelData& modelData)
return false;
}
bool Model::Save(bool withMeshDataFromGpu, Function<FlaxChunk*(int32)>& getChunk)
bool Model::Save(bool withMeshDataFromGpu, Function<FlaxChunk*(int32)>& getChunk) const
{
if (ModelBase::Save(withMeshDataFromGpu, getChunk))
return true;

View File

@@ -271,9 +271,9 @@ private:
bool LoadHeader(ReadStream& stream, byte& headerVersion);
#if USE_EDITOR
friend class ImportModel;
bool SaveHeader(WriteStream& stream) override;
bool SaveHeader(WriteStream& stream) const override;
static bool SaveHeader(WriteStream& stream, const ModelData& modelData);
bool Save(bool withMeshDataFromGpu, Function<FlaxChunk*(int32)>& getChunk) override;
bool Save(bool withMeshDataFromGpu, Function<FlaxChunk*(int32)>& getChunk) const override;
#endif
public:

View File

@@ -218,16 +218,8 @@ void ModelBase::GetLODData(int32 lodIndex, BytesContainer& data) const
bool ModelBase::Save(bool withMeshDataFromGpu, const StringView& path)
{
// Validate state
if (WaitForLoaded())
{
LOG(Error, "Asset loading failed. Cannot save it.");
if (OnCheckSave(path))
return true;
}
if (IsVirtual() && path.IsEmpty())
{
LOG(Error, "To save virtual asset asset you need to specify the target asset path location.");
return true;
}
if (withMeshDataFromGpu && IsInMainThread())
{
LOG(Error, "To save model with GPU mesh buffers it needs to be called from the other thread (not the main thread).");
@@ -238,7 +230,6 @@ bool ModelBase::Save(bool withMeshDataFromGpu, const StringView& path)
LOG(Error, "To save virtual model asset you need to specify 'withMeshDataFromGpu' (it has no other storage container to get data).");
return true;
}
ScopeLock lock(Locker);
// Use a temporary chunks for data storage for virtual assets
@@ -399,7 +390,7 @@ bool ModelBase::LoadMesh(MemoryReadStream& stream, byte meshVersion, MeshBase* m
#if USE_EDITOR
bool ModelBase::SaveHeader(WriteStream& stream)
bool ModelBase::SaveHeader(WriteStream& stream) const
{
// Basic info
static_assert(MODEL_HEADER_VERSION == 2, "Update code");
@@ -852,7 +843,7 @@ bool ModelBase::SaveMesh(WriteStream& stream, const MeshBase* mesh) const
return false;
}
bool ModelBase::Save(bool withMeshDataFromGpu, Function<FlaxChunk*(int32)>& getChunk)
bool ModelBase::Save(bool withMeshDataFromGpu, Function<FlaxChunk*(int32)>& getChunk) const
{
return false;
}
@@ -876,6 +867,11 @@ void ModelBase::GetReferences(Array<Guid>& assets, Array<String>& files) const
assets.Add(slot.Material.GetID());
}
bool ModelBase::Save(const StringView& path)
{
return Save(false, path);
}
#endif
int32 ModelBase::GetCurrentResidency() const

View File

@@ -331,12 +331,12 @@ protected:
virtual bool LoadMesh(class MemoryReadStream& stream, byte meshVersion, MeshBase* mesh, MeshData* dataIfReadOnly = nullptr);
bool LoadHeader(ReadStream& stream, byte& headerVersion);
#if USE_EDITOR
virtual bool SaveHeader(WriteStream& stream);
virtual bool SaveHeader(WriteStream& stream) const;
static bool SaveHeader(WriteStream& stream, const class ModelData& modelData);
bool SaveLOD(WriteStream& stream, int32 lodIndex) const;
static bool SaveLOD(WriteStream& stream, const ModelData& modelData, int32 lodIndex, bool(saveMesh)(WriteStream& stream, const ModelData& modelData, int32 lodIndex, int32 meshIndex) = nullptr);
virtual bool SaveMesh(WriteStream& stream, const MeshBase* mesh) const;
virtual bool Save(bool withMeshDataFromGpu, Function<FlaxChunk*(int32)>& getChunk);
virtual bool Save(bool withMeshDataFromGpu, Function<FlaxChunk*(int32)>& getChunk) const;
#endif
public:
@@ -344,6 +344,7 @@ public:
void CancelStreaming() override;
#if USE_EDITOR
void GetReferences(Array<Guid>& assets, Array<String>& files) const override;
bool Save(const StringView& path = StringView::Empty) override;
#endif
// [StreamableResource]

View File

@@ -18,18 +18,8 @@ RawDataAsset::RawDataAsset(const SpawnParams& params, const AssetInfo* info)
bool RawDataAsset::Save(const StringView& path)
{
// Validate state
if (WaitForLoaded())
{
LOG(Error, "Asset loading failed. Cannot save it.");
if (OnCheckSave(path))
return true;
}
if (IsVirtual() && path.IsEmpty())
{
LOG(Error, "To save virtual asset asset you need to specify the target asset path location.");
return true;
}
ScopeLock lock(Locker);
bool result;

View File

@@ -16,20 +16,11 @@ public:
/// </summary>
API_FIELD() Array<byte> Data;
public:
#if USE_EDITOR
/// <summary>
/// Saves this asset to the file. Supported only in Editor.
/// </summary>
/// <param name="path">The custom asset path to use for the saving. Use empty value to save this asset to its own storage location. Can be used to duplicate asset. Must be specified when saving virtual asset.</param>
/// <returns>True if failed, otherwise false.</returns>
API_FUNCTION() bool Save(const StringView& path = StringView::Empty);
#endif
public:
// [BinaryAsset]
#if USE_EDITOR
bool Save(const StringView& path = StringView::Empty) override;
#endif
uint64 GetMemoryUsage() const override;
protected:

View File

@@ -66,18 +66,8 @@ const BitArray<>& SkeletonMask::GetNodesMask()
bool SkeletonMask::Save(const StringView& path)
{
// Validate state
if (WaitForLoaded())
{
LOG(Error, "Asset loading failed. Cannot save it.");
if (OnCheckSave(path))
return true;
}
if (IsVirtual() && path.IsEmpty())
{
LOG(Error, "To save virtual asset asset you need to specify the target asset path location.");
return true;
}
ScopeLock lock(Locker);
// Write data

View File

@@ -51,17 +51,6 @@ public:
/// <returns>The constant reference to the skeleton nodes mask.</returns>
API_PROPERTY() const BitArray<>& GetNodesMask();
#if USE_EDITOR
/// <summary>
/// Saves this asset to the file. Supported only in Editor.
/// </summary>
/// <param name="path">The custom asset path to use for the saving. Use empty value to save this asset to its own storage location. Can be used to duplicate asset. Must be specified when saving virtual asset.</param>
/// <returns>True if cannot save data, otherwise false.</returns>
API_FUNCTION() bool Save(const StringView& path = StringView::Empty);
#endif
private:
void OnSkeletonUnload();
@@ -73,6 +62,7 @@ public:
BinaryAsset::GetReferences(assets, files);
assets.Add(Skeleton.GetID());
}
bool Save(const StringView& path = StringView::Empty) override;
#endif
protected:

View File

@@ -661,7 +661,7 @@ bool SkinnedModel::LoadHeader(ReadStream& stream, byte& headerVersion)
#if USE_EDITOR
bool SkinnedModel::SaveHeader(WriteStream& stream)
bool SkinnedModel::SaveHeader(WriteStream& stream) const
{
if (ModelBase::SaveHeader(stream))
return true;

View File

@@ -299,7 +299,7 @@ private:
bool LoadHeader(ReadStream& stream, byte& headerVersion);
#if USE_EDITOR
friend class ImportModel;
bool SaveHeader(WriteStream& stream) override;
bool SaveHeader(WriteStream& stream) const override;
static bool SaveHeader(WriteStream& stream, const ModelData& modelData);
bool SaveMesh(WriteStream& stream, const MeshBase* mesh) const override;
static bool SaveMesh(WriteStream& stream, const ModelData& modelData, int32 lodIndex, int32 meshIndex);

View File

@@ -29,24 +29,19 @@ bool Texture::IsNormalMap() const
#if USE_EDITOR
bool Texture::Save(const StringView& path)
{
return Save(path, nullptr);
}
bool Texture::Save(const StringView& path, const InitData* customData)
{
// Validate state
if (WaitForLoaded())
{
LOG(Error, "Asset loading failed. Cannot save it.");
if (OnCheckSave())
return true;
}
if (IsVirtual() && path.IsEmpty())
{
LOG(Error, "To save virtual asset asset you need to specify the target asset path location.");
return true;
}
ScopeLock lock(Locker);
AssetInitData data;
const auto texture = StreamingTexture();
const class StreamingTexture* texture = StreamingTexture();
// Use a temporary chunks for data storage for virtual assets
FlaxChunk* tmpChunks[ASSET_FILE_DATA_CHUNKS];

View File

@@ -23,17 +23,6 @@ API_CLASS(NoSpawn) class FLAXENGINE_API Texture : public TextureBase
public:
#if USE_EDITOR
/// <summary>
/// Saves this asset to the file. Supported only in Editor.
/// </summary>
/// <param name="path">The custom asset path to use for the saving. Use empty value to save this asset to its own storage location. Can be used to duplicate asset. Must be specified when saving virtual asset.</param>
/// <returns>True if cannot save data, otherwise false.</returns>
API_FUNCTION() bool Save(const StringView& path = StringView::Empty)
{
return Save(path, nullptr);
}
/// <summary>
/// Saves this asset to the file. Supported only in Editor.
/// </summary>
@@ -41,7 +30,6 @@ public:
/// <param name="customData">The custom texture data container. Can be used to override the data stored in the asset. Use null to ignore this argument.</param>
/// <returns>True if cannot save data, otherwise false.</returns>
bool Save(const StringView& path, const InitData* customData);
#endif
/// <summary>
@@ -60,4 +48,10 @@ public:
/// <param name="generateMips">True if generate mipmaps for the imported texture.</param>
/// <returns>The loaded texture (virtual asset) or null if fails.</returns>
API_FUNCTION() static Texture* FromFile(const StringView& path, bool generateMips = false);
public:
// [TextureBase]
#if USE_EDITOR
bool Save(const StringView& path = StringView::Empty) override;
#endif
};

View File

@@ -2,6 +2,7 @@
#include "VisualScript.h"
#include "Engine/Core/Log.h"
#include "Engine/Core/Types/Span.h"
#include "Engine/Core/Types/DataContainer.h"
#include "Engine/Content/Content.h"
#include "Engine/Content/Factories/BinaryAssetFactory.h"
@@ -1303,6 +1304,23 @@ VisualScript::VisualScript(const SpawnParams& params, const AssetInfo* info)
{
}
#if USE_EDITOR
bool VisualScript::Save(const StringView& path)
{
if (OnCheckSave(path))
return true;
ScopeLock lock(Locker);
MemoryWriteStream writeStream;
if (Graph.Save(&writeStream, true))
return true;
BytesContainer data;
data.Link(ToSpan(writeStream));
return SaveSurface(data, Meta);
}
#endif
Asset::LoadResult VisualScript::load()
{
// Build Visual Script typename that is based on asset id
@@ -2184,7 +2202,7 @@ const VisualScript::Field* VisualScript::FindField(const StringAnsiView& name) c
return nullptr;
}
BytesContainer VisualScript::LoadSurface()
BytesContainer VisualScript::LoadSurface() const
{
if (WaitForLoaded())
return BytesContainer();
@@ -2202,19 +2220,10 @@ BytesContainer VisualScript::LoadSurface()
#if USE_EDITOR
bool VisualScript::SaveSurface(const BytesContainer& data, const Metadata& meta)
bool VisualScript::SaveSurface(const BytesContainer& data, const Metadata& meta) const
{
// Wait for asset to be loaded or don't if last load failed
if (LastLoadFailed())
{
LOG(Warning, "Saving asset that failed to load.");
}
else if (WaitForLoaded())
{
LOG(Error, "Asset loading failed. Cannot save it.");
if (OnCheckSave())
return true;
}
ScopeLock lock(Locker);
// Release all chunks

View File

@@ -246,7 +246,7 @@ public:
/// Tries to load surface graph from the asset.
/// </summary>
/// <returns>The surface data or empty if failed to load it.</returns>
API_FUNCTION() BytesContainer LoadSurface();
API_FUNCTION() BytesContainer LoadSurface() const;
#if USE_EDITOR
@@ -256,7 +256,7 @@ public:
/// <param name="data">Stream with graph data.</param>
/// <param name="meta">Script metadata.</param>
/// <returns>True if cannot save it, otherwise false.</returns>
API_FUNCTION() bool SaveSurface(const BytesContainer& data, API_PARAM(Ref) const Metadata& meta);
API_FUNCTION() bool SaveSurface(const BytesContainer& data, API_PARAM(Ref) const Metadata& meta) const;
// Returns the amount of methods in the script.
API_FUNCTION() int32 GetMethodsCount() const
@@ -286,6 +286,7 @@ public:
BinaryAsset::GetReferences(assets, files);
Graph.GetReferences(assets);
}
bool Save(const StringView& path = StringView::Empty) override;
#endif
protected:
@@ -406,7 +407,6 @@ public:
static Variant Invoke(VisualScript::Method* method, ScriptingObject* instance, Span<Variant> parameters = Span<Variant>());
#if VISUAL_SCRIPT_DEBUGGING
// Custom event that is called every time the Visual Script signal flows over the graph (including the data connections). Can be used to read nad visualize the Visual Script execution logic.
static Action DebugFlow;
@@ -420,6 +420,5 @@ public:
/// <param name="result">The output value. Valid only if method returned true.</param>
/// <returns>True if could fetch the value, otherwise false.</returns>
static bool Evaluate(VisualScript* script, ScriptingObject* instance, uint32 nodeId, uint32 boxId, Variant& result);
#endif
};