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

@@ -13,6 +13,7 @@
#include "FlaxEngine.Gen.h"
#if USE_EDITOR
#include "Engine/Level/Level.h"
#include "Engine/Serialization/MemoryWriteStream.h"
#endif
REGISTER_BINARY_ASSET(BehaviorTree, "FlaxEngine.BehaviorTree", false);
@@ -164,7 +165,7 @@ BehaviorTreeNode* BehaviorTree::GetNodeInstance(uint32 id)
return nullptr;
}
BytesContainer BehaviorTree::LoadSurface()
BytesContainer BehaviorTree::LoadSurface() const
{
if (WaitForLoaded())
return BytesContainer();
@@ -182,19 +183,10 @@ BytesContainer BehaviorTree::LoadSurface()
#if USE_EDITOR
bool BehaviorTree::SaveSurface(const BytesContainer& data)
bool BehaviorTree::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
@@ -250,6 +242,19 @@ void BehaviorTree::GetReferences(Array<Guid>& assets, Array<String>& files) cons
}
}
bool BehaviorTree::Save(const StringView& path)
{
if (OnCheckSave(path))
return true;
ScopeLock lock(Locker);
MemoryWriteStream stream;
if (Graph.Save(&stream, true))
return true;
BytesContainer data;
data.Link(ToSpan(stream));
return SaveSurface(data);
}
#endif
void BehaviorTree::OnScriptingDispose()

View File

@@ -77,7 +77,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
/// <summary>
@@ -85,7 +85,7 @@ public:
/// </summary>
/// <param name="data">Stream with 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:
@@ -99,6 +99,7 @@ public:
void OnScriptingDispose() override;
#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

@@ -5,6 +5,7 @@
#include "Engine/Content/Factories/BinaryAssetFactory.h"
#include "Engine/Content/Assets/MaterialBase.h"
#include "Engine/Content/Content.h"
#include "Engine/Content/Deprecated.h"
#include "Engine/Serialization/MemoryReadStream.h"
#include "Engine/Audio/AudioClip.h"
#include "Engine/Graphics/PostProcessSettings.h"
@@ -35,19 +36,166 @@ const BytesContainer& SceneAnimation::LoadTimeline()
#if USE_EDITOR
bool SceneAnimation::SaveTimeline(const BytesContainer& data)
void SceneAnimation::SaveData(MemoryWriteStream& stream) const
{
// 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.");
return true;
}
// Save properties
stream.Write(4);
stream.Write(FramesPerSecond);
stream.Write(DurationFrames);
// Save tracks
stream.Write(Tracks.Count());
for (const auto& track : Tracks)
{
stream.Write((byte)track.Type);
stream.Write((byte)track.Flag);
stream.Write((int32)track.ParentIndex);
stream.Write((int32)track.ChildrenCount);
stream.Write(track.Name, -13);
stream.Write(track.Color);
switch (track.Type)
{
case Track::Types::Folder:
break;
case Track::Types::PostProcessMaterial:
{
auto trackData = stream.Move<PostProcessMaterialTrack::Data>();
trackData->AssetID = track.Asset.GetID();
const auto trackRuntime = track.GetRuntimeData<PostProcessMaterialTrack::Runtime>();
stream.Write((int32)trackRuntime->Count);
stream.WriteBytes(trackRuntime->Media, sizeof(Media) * trackRuntime->Count);
break;
}
case Track::Types::NestedSceneAnimation:
{
auto trackData = stream.Move<NestedSceneAnimationTrack::Data>();
*trackData = *track.GetData<NestedSceneAnimationTrack::Data>();
trackData->AssetID = track.Asset.GetID();
break;
}
case Track::Types::ScreenFade:
{
auto trackData = stream.Move<ScreenFadeTrack::Data>();
*trackData = *track.GetData<ScreenFadeTrack::Data>();
const auto trackRuntime = track.GetRuntimeData<ScreenFadeTrack::Runtime>();
stream.WriteBytes(trackRuntime->GradientStops, sizeof(ScreenFadeTrack::GradientStop) * trackData->GradientStopsCount);
break;
}
case Track::Types::Audio:
{
auto trackData = stream.Move<AudioTrack::Data>();
trackData->AssetID = track.Asset.GetID();
const auto trackRuntime = track.GetRuntimeData<AudioTrack::Runtime>();
stream.Write((int32)trackRuntime->Count);
stream.WriteBytes(trackRuntime->Media, sizeof(AudioTrack::Media) * trackRuntime->Count);
break;
}
case Track::Types::AudioVolume:
{
auto trackData = stream.Move<AudioVolumeTrack::Data>();
*trackData = *track.GetData<AudioVolumeTrack::Data>();
const auto trackRuntime = track.GetRuntimeData<AudioVolumeTrack::Runtime>();
stream.WriteBytes(trackRuntime->Keyframes, sizeof(BezierCurveKeyframe<float>) * trackRuntime->KeyframesCount);
break;
}
case Track::Types::Actor:
{
auto trackData = stream.Move<ActorTrack::Data>();
*trackData = *track.GetData<ActorTrack::Data>();
break;
}
case Track::Types::Script:
{
auto trackData = stream.Move<ScriptTrack::Data>();
*trackData = *track.GetData<ScriptTrack::Data>();
break;
}
case Track::Types::KeyframesProperty:
case Track::Types::ObjectReferenceProperty:
{
auto trackData = stream.Move<KeyframesPropertyTrack::Data>();
*trackData = *track.GetData<KeyframesPropertyTrack::Data>();
const auto trackRuntime = track.GetRuntimeData<KeyframesPropertyTrack::Runtime>();
stream.WriteBytes(trackRuntime->PropertyName, trackData->PropertyNameLength + 1);
stream.WriteBytes(trackRuntime->PropertyTypeName, trackData->PropertyTypeNameLength + 1);
stream.WriteBytes(trackRuntime->Keyframes, trackRuntime->KeyframesSize);
break;
}
case Track::Types::CurveProperty:
{
auto trackData = stream.Move<CurvePropertyTrack::Data>();
*trackData = *track.GetData<CurvePropertyTrack::Data>();
const auto trackRuntime = track.GetRuntimeData<CurvePropertyTrack::Runtime>();
stream.WriteBytes(trackRuntime->PropertyName, trackData->PropertyNameLength + 1);
stream.WriteBytes(trackRuntime->PropertyTypeName, trackData->PropertyTypeNameLength + 1);
const int32 keyframesDataSize = trackData->KeyframesCount * (sizeof(float) + trackData->ValueSize * 3);
stream.WriteBytes(trackRuntime->Keyframes, keyframesDataSize);
break;
}
case Track::Types::StringProperty:
{
const auto trackData = stream.Move<StringPropertyTrack::Data>();
*trackData = *track.GetData<StringPropertyTrack::Data>();
const auto trackRuntime = track.GetRuntimeData<StringPropertyTrack::Runtime>();
stream.WriteBytes(trackRuntime->PropertyName, trackData->PropertyNameLength + 1);
stream.WriteBytes(trackRuntime->PropertyTypeName, trackData->PropertyTypeNameLength + 1);
const auto keyframesTimes = (float*)((byte*)trackRuntime + sizeof(StringPropertyTrack::Runtime));
const auto keyframesLengths = (int32*)((byte*)keyframesTimes + sizeof(float) * trackRuntime->KeyframesCount);
const auto keyframesValues = (Char**)((byte*)keyframesLengths + sizeof(int32) * trackRuntime->KeyframesCount);
for (int32 j = 0; j < trackRuntime->KeyframesCount; j++)
{
stream.Write((float)keyframesTimes[j]);
stream.Write((int32)keyframesLengths[j]);
stream.WriteBytes(keyframesValues[j], keyframesLengths[j] * sizeof(Char));
}
break;
}
case Track::Types::StructProperty:
case Track::Types::ObjectProperty:
{
auto trackData = stream.Move<StructPropertyTrack::Data>();
*trackData = *track.GetData<StructPropertyTrack::Data>();
const auto trackRuntime = track.GetRuntimeData<StructPropertyTrack::Runtime>();
stream.WriteBytes(trackRuntime->PropertyName, trackData->PropertyNameLength + 1);
stream.WriteBytes(trackRuntime->PropertyTypeName, trackData->PropertyTypeNameLength + 1);
break;
}
case Track::Types::Event:
{
const auto trackRuntime = track.GetRuntimeData<EventTrack::Runtime>();
int32 tmp = StringUtils::Length(trackRuntime->EventName);
stream.Write((int32)trackRuntime->EventParamsCount);
stream.Write((int32)trackRuntime->EventsCount);
stream.Write((int32)tmp);
stream.WriteBytes(trackRuntime->EventName, tmp + 1);
for (int j = 0; j < trackRuntime->EventParamsCount; j++)
{
stream.Write((int32)trackRuntime->EventParamSizes[j]);
stream.Write((int32)tmp);
stream.WriteBytes(trackRuntime->EventParamTypes[j], tmp + 1);
}
stream.WriteBytes(trackRuntime->DataBegin, trackRuntime->EventsCount * (sizeof(float) + trackRuntime->EventParamsSize));
break;
}
case Track::Types::CameraCut:
{
auto trackData = stream.Move<CameraCutTrack::Data>();
*trackData = *track.GetData<CameraCutTrack::Data>();
const auto trackRuntime = track.GetRuntimeData<CameraCutTrack::Runtime>();
stream.Write((int32)trackRuntime->Count);
stream.WriteBytes(trackRuntime->Media, sizeof(Media) * trackRuntime->Count);
break;
}
default:
break;
}
}
}
bool SceneAnimation::SaveTimeline(const BytesContainer& data) const
{
if (OnCheckSave())
return true;
ScopeLock lock(Locker);
// Release all chunks
@@ -71,10 +219,6 @@ bool SceneAnimation::SaveTimeline(const BytesContainer& data)
return false;
}
#endif
#if USE_EDITOR
void SceneAnimation::GetReferences(Array<Guid>& assets, Array<String>& files) const
{
// Base
@@ -88,6 +232,18 @@ void SceneAnimation::GetReferences(Array<Guid>& assets, Array<String>& files) co
}
}
bool SceneAnimation::Save(const StringView& path)
{
if (OnCheckSave(path))
return true;
ScopeLock lock(Locker);
MemoryWriteStream stream;
SaveData(stream);
BytesContainer data;
data.Link(ToSpan(stream));
return SaveTimeline(data);
}
#endif
Asset::LoadResult SceneAnimation::load()
@@ -117,6 +273,7 @@ Asset::LoadResult SceneAnimation::load()
{
case 2: // [Deprecated in 2020 expires on 03.09.2023]
case 3: // [Deprecated on 03.09.2021 expires on 03.09.2023]
MARK_CONTENT_DEPRECATED();
case 4:
{
stream.Read(FramesPerSecond);

View File

@@ -415,6 +415,10 @@ private:
BytesContainer _data;
MemoryWriteStream _runtimeData;
#if USE_EDITOR
void SaveData(MemoryWriteStream& stream) const;
#endif
public:
/// <summary>
/// The frames amount per second of the timeline animation.
@@ -457,7 +461,7 @@ public:
/// <remarks>It 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(const BytesContainer& data);
API_FUNCTION() bool SaveTimeline(const BytesContainer& data) const;
#endif
@@ -465,6 +469,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

@@ -2,6 +2,7 @@
#include "Asset.h"
#include "Content.h"
#include "Deprecated.h"
#include "SoftAssetReference.h"
#include "Cache/AssetsCache.h"
#include "Loading/Tasks/LoadAssetTask.h"
@@ -10,6 +11,26 @@
#include "Engine/Profiler/ProfilerCPU.h"
#include "Engine/Scripting/ManagedCLR/MCore.h"
#include "Engine/Threading/MainThreadTask.h"
#include "Engine/Threading/ThreadLocal.h"
#if USE_EDITOR
ThreadLocal<bool> ContentDeprecatedFlags;
void ContentDeprecated::Mark()
{
ContentDeprecatedFlags.Set(true);
}
bool ContentDeprecated::Clear(bool newValue)
{
auto& flag = ContentDeprecatedFlags.Get();
bool result = flag;
flag = newValue;
return result;
}
#endif
AssetReferenceBase::~AssetReferenceBase()
{
@@ -340,6 +361,7 @@ void Asset::Reload()
// Virtual assets are memory-only so reloading them makes no sense
if (IsVirtual())
return;
PROFILE_CPU_NAMED("Asset.Reload");
// It's better to call it from the main thread
if (IsInMainThread())
@@ -476,6 +498,12 @@ Array<Guid> Asset::GetReferences() const
return result;
}
bool Asset::Save(const StringView& path)
{
LOG(Warning, "Asset type '{}' does not support saving.", GetTypeName());
return true;
}
#endif
void Asset::DeleteManaged()
@@ -524,12 +552,21 @@ bool Asset::onLoad(LoadAssetTask* task)
// Load asset
LoadResult result;
#if USE_EDITOR
auto& deprecatedFlag = ContentDeprecatedFlags.Get();
bool prevDeprecated = deprecatedFlag;
deprecatedFlag = false;
#endif
{
PROFILE_CPU_ASSET(this);
result = loadAsset();
}
const bool isLoaded = result == LoadResult::Ok;
const bool failed = !isLoaded;
#if USE_EDITOR
const bool isDeprecated = deprecatedFlag;
deprecatedFlag = prevDeprecated;
#endif
Platform::AtomicStore(&_loadState, (int64)(isLoaded ? LoadState::Loaded : LoadState::LoadFailed));
if (failed)
{
@@ -550,6 +587,19 @@ bool Asset::onLoad(LoadAssetTask* task)
// This allows to reduce mutexes and locks (max one frame delay isn't hurting but provides more safety)
Content::onAssetLoaded(this);
}
#if USE_EDITOR
// Auto-save deprecated assets to get rid of data in an old format
if (isDeprecated && isLoaded)
{
PROFILE_CPU_NAMED("Asset.Save");
LOG(Info, "Resaving asset '{}' that uses deprecated data format", ToString());
if (Save())
{
LOG(Error, "Failed to resave asset '{}'", ToString());
}
}
#endif
return failed;
}
@@ -595,3 +645,26 @@ void Asset::onUnload_MainThread()
loadingTask->Cancel();
}
}
#if USE_EDITOR
bool Asset::OnCheckSave(const StringView& path) const
{
if (LastLoadFailed())
{
LOG(Warning, "Saving asset that failed to load.");
}
if (WaitForLoaded())
{
LOG(Error, "Asset loading failed. Cannot save it.");
return true;
}
if (IsVirtual() && path.IsEmpty())
{
LOG(Error, "To save virtual asset asset you need to specify the target asset path location.");
return true;
}
return false;
}
#endif

View File

@@ -75,7 +75,7 @@ public:
EventType OnUnloaded;
/// <summary>
/// General purpose mutex for an asset object. Should guard most of asset functionalities to be secure.
/// General purpose mutex for an asset object. Should guard most of the asset functionalities to be secure.
/// </summary>
CriticalSection Locker;
@@ -209,6 +209,13 @@ public:
/// </remarks>
/// <returns>The collection of the asset ids referenced by this asset.</returns>
API_FUNCTION() Array<Guid, HeapAllocation> GetReferences() const;
/// <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 when cannot save data, otherwise false.</returns>
API_FUNCTION(Sealed) virtual bool Save(const StringView& path = StringView::Empty);
#endif
/// <summary>
@@ -253,6 +260,7 @@ protected:
virtual void onLoaded_MainThread();
virtual void onUnload_MainThread();
#if USE_EDITOR
bool OnCheckSave(const StringView& path = StringView::Empty) const;
virtual void onRename(const StringView& newPath) = 0;
#endif

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
};

View File

@@ -189,7 +189,7 @@ bool BinaryAsset::HasDependenciesModified() const
#endif
FlaxChunk* BinaryAsset::GetOrCreateChunk(int32 index)
FlaxChunk* BinaryAsset::GetOrCreateChunk(int32 index) const
{
if (IsVirtual()) // Virtual assets don't own storage container
return nullptr;
@@ -205,7 +205,7 @@ FlaxChunk* BinaryAsset::GetOrCreateChunk(int32 index)
// Allocate
ASSERT(Storage);
_header.Chunks[index] = chunk = Storage->AllocateChunk();
const_cast<BinaryAsset*>(this)->_header.Chunks[index] = chunk = Storage->AllocateChunk();
if (chunk)
chunk->RegisterUsage();
@@ -263,10 +263,9 @@ void BinaryAsset::GetChunkData(int32 index, BytesContainer& data) const
data.Link(chunk->Data);
}
bool BinaryAsset::LoadChunk(int32 chunkIndex)
bool BinaryAsset::LoadChunk(int32 chunkIndex) const
{
ASSERT(Storage);
const auto chunk = _header.Chunks[chunkIndex];
if (chunk != nullptr
&& chunk->IsMissing()
@@ -275,19 +274,14 @@ bool BinaryAsset::LoadChunk(int32 chunkIndex)
if (Storage->LoadAssetChunk(chunk))
return true;
}
return false;
}
bool BinaryAsset::LoadChunks(AssetChunksFlag chunks)
bool BinaryAsset::LoadChunks(AssetChunksFlag chunks) const
{
ASSERT(Storage);
// Check if skip loading
if (chunks == 0)
return false;
// Load all missing marked chunks
ASSERT(Storage);
for (int32 i = 0; i < ASSET_FILE_DATA_CHUNKS; i++)
{
auto chunk = _header.Chunks[i];
@@ -300,7 +294,6 @@ bool BinaryAsset::LoadChunks(AssetChunksFlag chunks)
return true;
}
}
return false;
}

View File

@@ -166,7 +166,7 @@ public:
/// </summary>
/// <param name="index">The chunk index.</param>
/// <returns>Flax Chunk object (may be null if asset storage doesn't allow to add new chunks).</returns>
FlaxChunk* GetOrCreateChunk(int32 index);
FlaxChunk* GetOrCreateChunk(int32 index) const;
/// <summary>
/// Determines whether the specified chunk exists.
@@ -238,14 +238,14 @@ public:
/// </summary>
/// <param name="chunkIndex">The chunk index to load.</param>
/// <returns>True if failed, otherwise false.</returns>
bool LoadChunk(int32 chunkIndex);
bool LoadChunk(int32 chunkIndex) const;
/// <summary>
/// Loads the chunks (synchronous, blocks current thread).
/// </summary>
/// <param name="chunks">The chunks to load (packed in a flag).</param>
/// <returns>True if failed, otherwise false.</returns>
bool LoadChunks(AssetChunksFlag chunks);
bool LoadChunks(AssetChunksFlag chunks) const;
#if USE_EDITOR
/// <summary>

View File

@@ -1156,19 +1156,14 @@ void Content::tryCallOnLoaded(Asset* asset)
void Content::onAssetLoaded(Asset* asset)
{
// This is called by the asset on loading end
ASSERT(asset && asset->IsLoaded());
ScopeLock locker(LoadedAssetsToInvokeLocker);
LoadedAssetsToInvoke.Add(asset);
}
void Content::onAssetUnload(Asset* asset)
{
// This is called by the asset on unloading
ScopeLock locker(AssetsLocker);
Assets.Remove(asset->GetID());
UnloadQueue.Remove(asset);
LoadedAssetsToInvoke.Remove(asset);

View File

@@ -0,0 +1,26 @@
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
#pragma once
#include "Engine/Core/Core.h"
#if USE_EDITOR
// Utility for marking content as deprecated when loading it in Editor. Used to auto-upgrade (by resaving) data during development in editor or during game cooking.
class FLAXENGINE_API ContentDeprecated
{
public:
// Marks content as deprecated (for the current thread).
static void Mark();
// Reads and clears deprecation flag (for the current thread).
static bool Clear(bool newValue = false);
};
// Marks content as deprecated (for the current thread).
#define MARK_CONTENT_DEPRECATED() ContentDeprecated::Mark()
#else
#define MARK_CONTENT_DEPRECATED()
#endif

View File

@@ -161,25 +161,19 @@ void JsonAssetBase::GetReferences(const StringAnsiView& json, Array<Guid>& asset
FindIds(document, assets, files);
}
bool JsonAssetBase::Save(const StringView& path) const
bool JsonAssetBase::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;
}
PROFILE_CPU();
ScopeLock lock(Locker);
// Serialize to json to the buffer
rapidjson_flax::StringBuffer buffer;
PrettyJsonWriter writerObj(buffer);
_isResaving = true;
Save(writerObj);
_isResaving = false;
// Save json to file
if (File::WriteAllBytes(path.HasChars() ? path : StringView(GetPath()), (byte*)buffer.GetString(), (int32)buffer.GetSize()))
@@ -193,12 +187,8 @@ bool JsonAssetBase::Save(const StringView& path) const
bool JsonAssetBase::Save(JsonWriter& writer) const
{
// Validate state
if (WaitForLoaded())
{
LOG(Error, "Asset loading failed. Cannot save it.");
if (OnCheckSave())
return true;
}
ScopeLock lock(Locker);
writer.StartObject();
@@ -348,6 +338,61 @@ uint64 JsonAsset::GetMemoryUsage() const
return result;
}
void JsonAsset::OnGetData(rapidjson_flax::StringBuffer& buffer) const
{
if (Instance && InstanceType && _isResaving)
{
// Serialize instance object that was loaded (from potentially deprecated data, serialize method is always up to date)
const ScriptingType& type = InstanceType.GetType();
PrettyJsonWriter writer(buffer);
bool got = false;
switch (type.Type)
{
case ScriptingTypes::Class:
case ScriptingTypes::Structure:
{
const ScriptingType::InterfaceImplementation* interface = type.GetInterface(ISerializable::TypeInitializer);
writer.StartObject();
((ISerializable*)((byte*)Instance + interface->VTableOffset))->Serialize(writer, nullptr);
got = true;
break;
}
case ScriptingTypes::Script:
{
writer.StartObject();
ToInterface<ISerializable>((ScriptingObject*)Instance)->Serialize(writer, nullptr);
got = true;
break;
}
}
if (got)
{
writer.EndObject();
// Parse json document (CreateInstance uses it to spawn object)
auto* self = const_cast<JsonAsset*>(this);
{
PROFILE_CPU_NAMED("Json.Parse");
self->Document.Parse(buffer.GetString(), buffer.GetSize());
}
if (self->Document.HasParseError())
{
self->Data = nullptr;
Log::JsonParseException(Document.GetParseError(), Document.GetErrorOffset());
}
else
{
self->Data = &self->Document;
self->DataEngineBuild = FLAXENGINE_VERSION_BUILD;
}
return;
}
}
JsonAssetBase::OnGetData(buffer);
}
Asset::LoadResult JsonAsset::loadAsset()
{
const auto result = JsonAssetBase::loadAsset();
@@ -387,6 +432,7 @@ void JsonAsset::onLoaded_MainThread()
JsonAssetBase::onLoaded_MainThread();
// Special case for Settings assets to flush them after edited and saved in Editor
// TODO: add interface for custom JsonAsset interaction of the instance class (eg. OnJsonLoaded, or similar to C# like OnDeserialized from Newtonsoft.Json)
const StringAsANSI<> dataTypeNameAnsi(DataTypeName.Get(), DataTypeName.Length());
const auto typeHandle = Scripting::FindScriptingType(StringAnsiView(dataTypeNameAnsi.Get(), DataTypeName.Length()));
if (Instance && typeHandle && typeHandle.IsSubclassOf(SettingsBase::TypeInitializer) && _isAfterReload)

View File

@@ -16,6 +16,7 @@ API_CLASS(Abstract, NoSpawn) class FLAXENGINE_API JsonAssetBase : public Asset
protected:
String _path;
bool _isVirtualDocument = false;
bool _isResaving = false;
protected:
/// <summary>
@@ -73,13 +74,6 @@ public:
/// <param name="assets">The output list of object IDs references by the asset (appended, not cleared).</param>
API_FUNCTION() static void GetReferences(const StringAnsiView& json, API_PARAM(Out) Array<Guid, HeapAllocation>& assets);
/// <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) const;
/// <summary>
/// Saves this asset to the Json Writer buffer (both ID, Typename header and Data contents). Supported only in Editor.
/// </summary>
@@ -98,6 +92,7 @@ public:
uint64 GetMemoryUsage() const override;
#if USE_EDITOR
void GetReferences(Array<Guid, HeapAllocation>& assets, Array<String, HeapAllocation>& files) const override;
bool Save(const StringView& path = StringView::Empty) override;
#endif
protected:
@@ -149,6 +144,7 @@ public:
protected:
// [JsonAssetBase]
void OnGetData(rapidjson_flax::StringBuffer& buffer) const override;
LoadResult loadAsset() override;
void unload(bool isReloading) override;
void onLoaded_MainThread() override;

View File

@@ -888,10 +888,12 @@ Variant::Variant(const Span<byte>& v)
}
PRAGMA_DISABLE_DEPRECATION_WARNINGS
#include "Engine/Content/Deprecated.h"
Variant::Variant(const CommonValue& value)
: Variant()
{
// [Deprecated on 31.07.2020, expires on 31.07.2022]
MARK_CONTENT_DEPRECATED();
switch (value.Type)
{
case CommonType::Bool:

View File

@@ -154,18 +154,8 @@ void GameplayGlobals::ResetValues()
bool GameplayGlobals::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 to bytes

View File

@@ -80,20 +80,12 @@ public:
/// </summary>
API_FUNCTION() void ResetValues();
#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
public:
// [BinaryAsset]
void InitAsVirtual() override;
#if USE_EDITOR
bool Save(const StringView& path = StringView::Empty) override;
#endif
protected:
// [BinaryAsset]

View File

@@ -7,6 +7,7 @@
#include "Engine/Core/Random.h"
#include "Engine/Engine/Engine.h"
#include "Engine/Graphics/RenderTask.h"
#include "Engine/Content/Deprecated.h"
#if !FOLIAGE_USE_SINGLE_QUAD_TREE
#include "Engine/Threading/JobSystem.h"
#if FOLIAGE_USE_DRAW_CALLS_BATCHING
@@ -1374,6 +1375,7 @@ void Foliage::Deserialize(DeserializeStream& stream, ISerializeModifier* modifie
if (modifier->EngineBuild <= 6189)
{
// [Deprecated on 30.11.2019, expires on 30.11.2021]
MARK_CONTENT_DEPRECATED();
InstanceEncoded1 enc;
for (int32 i = 0; i < foliageInstancesCount; i++)
{

View File

@@ -5,6 +5,7 @@
#include "Engine/Core/Math/Vector4.h"
#include "Engine/Core/Math/Matrix.h"
#include "Engine/Content/Content.h"
#include "Engine/Content/Deprecated.h"
#include "Engine/Graphics/GPUContext.h"
#include "Engine/Engine/GameplayGlobals.h"
#include "Engine/Serialization/MemoryWriteStream.h"
@@ -652,6 +653,8 @@ bool MaterialParams::Load(ReadStream* stream)
{
case 1: // [Deprecated on 15.11.2019, expires on 15.11.2021]
{
MARK_CONTENT_DEPRECATED();
// Size of the collection
uint16 paramsCount;
stream->ReadUint16(&paramsCount);
@@ -727,6 +730,8 @@ bool MaterialParams::Load(ReadStream* stream)
break;
case 2: // [Deprecated on 15.11.2019, expires on 15.11.2021]
{
MARK_CONTENT_DEPRECATED();
// Size of the collection
uint16 paramsCount;
stream->ReadUint16(&paramsCount);

View File

@@ -8,6 +8,7 @@
#include "Engine/Graphics/Shaders/GPUShader.h"
#if COMPILE_WITH_SHADER_COMPILER
#include "Engine/Engine/CommandLine.h"
#include "Engine/Content/Deprecated.h"
#include "Engine/Serialization/MemoryReadStream.h"
#endif
#include "Engine/ShadowsOfMordor/AtlasChartsPacker.h"
@@ -98,8 +99,11 @@ bool ShaderAssetBase::initBase(AssetInitData& initData)
#if USE_EDITOR
bool ShaderAssetBase::Save()
bool ShaderAssetBase::SaveShaderAsset() const
{
// Asset is being saved so no longer need to resave deprecated data in it
ContentDeprecated::Clear();
auto parent = GetShaderAsset();
AssetInitData data;
data.SerializedVersion = ShaderStorage::Header::Version;
@@ -296,7 +300,7 @@ bool ShaderAssetBase::LoadShaderCache(ShaderCacheResult& result)
#if USE_EDITOR
// Save chunks to the asset file
if (Save())
if (SaveShaderAsset())
{
LOG(Warning, "Cannot save '{0}'.", parent->ToString());
return true;

View File

@@ -30,15 +30,13 @@ public:
static int32 GetCacheChunkIndex(ShaderProfile profile);
#if USE_EDITOR
/// <summary>
/// Prepare shader compilation options
/// Prepare shader compilation options.
/// </summary>
/// <param name="options">Options</param>
/// <param name="options">The output options.</param>
virtual void InitCompilationOptions(struct ShaderCompilationOptions& options)
{
}
#endif
protected:
@@ -50,13 +48,11 @@ protected:
virtual BinaryAsset* GetShaderAsset() const = 0;
#if USE_EDITOR
/// <summary>
/// Saves this shader asset to the storage container.
/// </summary>
/// <returns>True if failed, otherwise false.</returns>
bool Save();
bool SaveShaderAsset() const;
#endif
/// <summary>
@@ -70,12 +66,10 @@ protected:
DataContainer<byte> Data;
#if COMPILE_WITH_SHADER_COMPILER
/// <summary>
/// The list of files included by the shader source (used by the given cache on the runtime graphics platform shader profile). Paths are absolute and unique.
/// </summary>
Array<String> Includes;
#endif
};
@@ -87,7 +81,6 @@ protected:
bool LoadShaderCache(ShaderCacheResult& result);
#if COMPILE_WITH_SHADER_COMPILER
/// <summary>
/// Registers shader asset for the automated reloads on source includes changes.
/// </summary>
@@ -100,7 +93,6 @@ protected:
/// </summary>
/// <param name="asset">The asset.</param>
void UnregisterForShaderReloads(Asset* asset);
#endif
};

View File

@@ -75,7 +75,7 @@ public:
};
/// <summary>
/// File header, version 19
/// [Deprecated on 13.07.2022, expires on 13.07.2024]
/// [Deprecated on 13.07.2022, expires on 13.07.2024]
/// </summary>
struct Header19
{

View File

@@ -12,6 +12,7 @@
#include "Engine/Scripting/ManagedCLR/MClass.h"
#include "Engine/Threading/Threading.h"
#include "Engine/Content/Content.h"
#include "Engine/Content/Deprecated.h"
#include "Engine/Core/Cache.h"
#include "Engine/Core/Collections/CollectionPoolCache.h"
#include "Engine/Debug/Exceptions/JsonParseException.h"
@@ -1074,12 +1075,18 @@ void Actor::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier)
// StaticFlags update - added StaticFlags::Navigation
// [Deprecated on 17.05.2020, expires on 17.05.2021]
if (modifier->EngineBuild < 6178 && (int32)_staticFlags == (1 + 2 + 4))
{
MARK_CONTENT_DEPRECATED();
_staticFlags |= StaticFlags::Navigation;
}
// StaticFlags update - added StaticFlags::Shadow
// [Deprecated on 17.05.2020, expires on 17.05.2021]
if (modifier->EngineBuild < 6601 && (int32)_staticFlags == (1 + 2 + 4 + 8))
{
MARK_CONTENT_DEPRECATED();
_staticFlags |= StaticFlags::Shadow;
}
const auto tag = stream.FindMember("Tag");
if (tag != stream.MemberEnd())

View File

@@ -11,6 +11,7 @@
#include "Engine/Core/Math/Matrix3x3.h"
#include "Editor/Editor.h"
#endif
#include "Engine/Content/Deprecated.h"
#include "Engine/Graphics/GPUContext.h"
#include "Engine/Graphics/GPUDevice.h"
#include "Engine/Graphics/RenderTask.h"
@@ -1153,10 +1154,16 @@ void AnimatedModel::Deserialize(DeserializeStream& stream, ISerializeModifier* m
// [Deprecated on 07.02.2022, expires on 07.02.2024]
if (modifier->EngineBuild <= 6330)
{
MARK_CONTENT_DEPRECATED();
DrawModes |= DrawPass::GlobalSDF;
}
// [Deprecated on 27.04.2022, expires on 27.04.2024]
if (modifier->EngineBuild <= 6331)
{
MARK_CONTENT_DEPRECATED();
DrawModes |= DrawPass::GlobalSurfaceAtlas;
}
}
const Span<MaterialSlot> AnimatedModel::GetMaterialSlots() const

View File

@@ -11,6 +11,7 @@
#include "Engine/Renderer/ProbesRenderer.h"
#include "Engine/Renderer/ReflectionsPass.h"
#include "Engine/Content/Content.h"
#include "Engine/Content/Deprecated.h"
#include "Engine/ContentExporters/AssetExporters.h"
#include "Engine/ContentImporters/AssetsImportingManager.h"
#include "Engine/Graphics/RenderTools.h"
@@ -255,6 +256,7 @@ void EnvironmentProbe::Deserialize(DeserializeStream& stream, ISerializeModifier
// [Deprecated on 18.07.2022, expires on 18.07.2022]
if (modifier->EngineBuild <= 6332)
{
MARK_CONTENT_DEPRECATED();
const auto member = stream.FindMember("AutoUpdate");
if (member != stream.MemberEnd() && member->value.IsBool() && member->value.GetBool())
UpdateMode = ProbeUpdateMode::WhenMoved;

View File

@@ -6,6 +6,7 @@
#include "Engine/Core/Math/Matrix3x4.h"
#include "Engine/Engine/Engine.h"
#include "Engine/Serialization/Serialization.h"
#include "Engine/Content/Deprecated.h"
#include "Engine/Graphics/GPUBufferDescription.h"
#include "Engine/Graphics/GPUDevice.h"
#include "Engine/Graphics/GPUBuffer.h"
@@ -514,10 +515,16 @@ void SplineModel::Deserialize(DeserializeStream& stream, ISerializeModifier* mod
// [Deprecated on 07.02.2022, expires on 07.02.2024]
if (modifier->EngineBuild <= 6330)
{
MARK_CONTENT_DEPRECATED();
DrawModes |= DrawPass::GlobalSDF;
}
// [Deprecated on 27.04.2022, expires on 27.04.2024]
if (modifier->EngineBuild <= 6331)
{
MARK_CONTENT_DEPRECATED();
DrawModes |= DrawPass::GlobalSurfaceAtlas;
}
}
void SplineModel::OnActiveInTreeChanged()

View File

@@ -2,6 +2,7 @@
#include "StaticModel.h"
#include "Engine/Engine/Engine.h"
#include "Engine/Content/Deprecated.h"
#include "Engine/Graphics/GPUBuffer.h"
#include "Engine/Graphics/GPUBufferDescription.h"
#include "Engine/Graphics/GPUContext.h"
@@ -534,15 +535,22 @@ void StaticModel::Deserialize(DeserializeStream& stream, ISerializeModifier* mod
const auto member = stream.FindMember("HiddenShadow");
if (member != stream.MemberEnd() && member->value.IsBool() && member->value.GetBool())
{
MARK_CONTENT_DEPRECATED();
DrawModes = DrawPass::Depth;
}
}
// [Deprecated on 07.02.2022, expires on 07.02.2024]
if (modifier->EngineBuild <= 6330)
{
MARK_CONTENT_DEPRECATED();
DrawModes |= DrawPass::GlobalSDF;
}
// [Deprecated on 27.04.2022, expires on 27.04.2024]
if (modifier->EngineBuild <= 6331)
{
MARK_CONTENT_DEPRECATED();
DrawModes |= DrawPass::GlobalSurfaceAtlas;
}
{
const auto member = stream.FindMember("RenderPasses");

View File

@@ -4,6 +4,7 @@
#include "NavigationSettings.h"
#include "NavMeshRuntime.h"
#include "NavMeshBuilder.h"
#include "NavMesh.h"
#include "Engine/Core/Config/GameSettings.h"
#include "Engine/Content/Content.h"
#include "Engine/Content/JsonAsset.h"
@@ -14,8 +15,7 @@
#include "Engine/Level/Level.h"
#include "Engine/Level/Scene/Scene.h"
#endif
#include "NavMesh.h"
#include "Engine/Content/Deprecated.h"
#include "Engine/Engine/EngineService.h"
#include "Engine/Profiler/ProfilerCPU.h"
#include "Engine/Serialization/Serialization.h"
@@ -255,6 +255,7 @@ void NavigationSettings::Deserialize(DeserializeStream& stream, ISerializeModifi
else
{
// [Deprecated on 12.01.2021, expires on 12.01.2022]
MARK_CONTENT_DEPRECATED();
float WalkableRadius = 34.0f;
float WalkableHeight = 144.0f;
float WalkableMaxClimb = 35.0f;

View File

@@ -3,6 +3,7 @@
#include "ParticleEffect.h"
#include "Particles.h"
#include "Engine/Core/Types/CommonValue.h"
#include "Engine/Content/Deprecated.h"
#include "Engine/Serialization/JsonTools.h"
#include "Engine/Serialization/Serialization.h"
#include "Engine/Level/Scene/SceneRendering.h"
@@ -664,6 +665,7 @@ void ParticleEffect::Deserialize(DeserializeStream& stream, ISerializeModifier*
if (modifier->EngineBuild < 6197)
{
PRAGMA_DISABLE_DEPRECATION_WARNINGS
MARK_CONTENT_DEPRECATED();
const auto& overrides = overridesMember->value;
ASSERT(overrides.IsArray());
_parametersOverrides.EnsureCapacity(_parametersOverrides.Count() + overrides.Size());

View File

@@ -154,6 +154,16 @@ Asset::LoadResult ParticleEmitter::load()
LOG(Warning, "Cannot load Particle Emitter GPU graph '{0}'.", GetPath());
return LoadResult::CannotLoadData;
}
if (ContentDeprecated::Clear())
{
// If encountered any deprecated data when loading graph then serialize it
MemoryWriteStream writeStream(1024);
if (!Graph.Save(&writeStream, true))
{
surfaceChunk->Data.Copy(ToSpan(writeStream));
ContentDeprecated::Clear();
}
}
generator.AddGraph(graph);
// Get chunk with material parameters
@@ -199,7 +209,7 @@ Asset::LoadResult ParticleEmitter::load()
// Save to file
#if USE_EDITOR
if (Save())
if (SaveShaderAsset())
{
LOG(Error, "Cannot save \'{0}\'", ToString());
return LoadResult::Failed;
@@ -318,10 +328,6 @@ void ParticleEmitter::OnDependencyModified(BinaryAsset* asset)
Reload();
}
#endif
#if USE_EDITOR
void ParticleEmitter::InitCompilationOptions(ShaderCompilationOptions& options)
{
// Base
@@ -377,19 +383,10 @@ BytesContainer ParticleEmitter::LoadSurface(bool createDefaultIfMissing)
#if USE_EDITOR
bool ParticleEmitter::SaveSurface(BytesContainer& data)
bool ParticleEmitter::SaveSurface(const 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);
// Release all chunks
@@ -406,7 +403,7 @@ bool ParticleEmitter::SaveSurface(BytesContainer& data)
ASSERT(visjectSurfaceChunk != nullptr);
visjectSurfaceChunk->Data.Copy(data);
if (Save())
if (SaveShaderAsset())
{
LOG(Error, "Cannot save \'{0}\'", ToString());
return true;
@@ -420,4 +417,23 @@ bool ParticleEmitter::SaveSurface(BytesContainer& data)
return false;
}
void ParticleEmitter::GetReferences(Array<Guid>& assets, Array<String>& files) const
{
BinaryAsset::GetReferences(assets, files);
Graph.GetReferences(assets);
}
bool ParticleEmitter::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

@@ -20,6 +20,7 @@ class ParticleEmitterInstance;
API_CLASS(NoSpawn) class FLAXENGINE_API ParticleEmitter : public ShaderAssetTypeBase<BinaryAsset>
{
DECLARE_BINARY_ASSET_HEADER(ParticleEmitter, ShadersSerializedVersion);
public:
/// <summary>
/// The loaded particle graph.
@@ -84,14 +85,12 @@ public:
API_FUNCTION() BytesContainer LoadSurface(bool createDefaultIfMissing);
#if USE_EDITOR
/// <summary>
/// Updates surface (saves new one, discard 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);
#endif
public:
@@ -100,7 +99,7 @@ public:
/// </summary>
/// <param name="position">The spawn position.</param>
/// <param name="duration">The effect playback duration (in seconds).</param>
/// <param name="autoDestroy">If set to <c>true</c> effect be be auto-destroyed after duration.</param>
/// <param name="autoDestroy">If set to <c>true</c> effect will be auto-destroyed after duration.</param>
/// <returns>The spawned effect.</returns>
API_FUNCTION() ParticleEffect* Spawn(const Vector3& position, float duration = MAX_float, bool autoDestroy = false)
{
@@ -113,7 +112,7 @@ public:
/// <param name="position">The spawn position.</param>
/// <param name="rotation">The spawn rotation.</param>
/// <param name="duration">The effect playback duration (in seconds).</param>
/// <param name="autoDestroy">If set to <c>true</c> effect be be auto-destroyed after duration.</param>
/// <param name="autoDestroy">If set to <c>true</c> effect will be auto-destroyed after duration.</param>
/// <returns>The spawned effect.</returns>
API_FUNCTION() ParticleEffect* Spawn(const Vector3& position, const Quaternion& rotation, float duration = MAX_float, bool autoDestroy = false)
{
@@ -125,7 +124,7 @@ public:
/// </summary>
/// <param name="transform">The spawn transform.</param>
/// <param name="duration">The effect playback duration (in seconds).</param>
/// <param name="autoDestroy">If set to <c>true</c> effect be be auto-destroyed after duration.</param>
/// <param name="autoDestroy">If set to <c>true</c> effect will be auto-destroyed after duration.</param>
/// <returns>The spawned effect.</returns>
API_FUNCTION() ParticleEffect* Spawn(const Transform& transform, float duration = MAX_float, bool autoDestroy = false)
{
@@ -138,7 +137,7 @@ public:
/// <param name="parent">The parent actor (can be null to link it to the first loaded scene).</param>
/// <param name="position">The spawn position.</param>
/// <param name="duration">The effect playback duration (in seconds).</param>
/// <param name="autoDestroy">If set to <c>true</c> effect be be auto-destroyed after duration.</param>
/// <param name="autoDestroy">If set to <c>true</c> effect will be auto-destroyed after duration.</param>
/// <returns>The spawned effect.</returns>
API_FUNCTION() ParticleEffect* Spawn(Actor* parent, const Vector3& position, float duration = MAX_float, bool autoDestroy = false)
{
@@ -152,7 +151,7 @@ public:
/// <param name="position">The spawn position.</param>
/// <param name="rotation">The spawn rotation.</param>
/// <param name="duration">The effect playback duration (in seconds).</param>
/// <param name="autoDestroy">If set to <c>true</c> effect be be auto-destroyed after duration.</param>
/// <param name="autoDestroy">If set to <c>true</c> effect will be auto-destroyed after duration.</param>
/// <returns>The spawned effect.</returns>
API_FUNCTION() ParticleEffect* Spawn(Actor* parent, const Vector3& position, const Quaternion& rotation, float duration = MAX_float, bool autoDestroy = false)
{
@@ -165,18 +164,15 @@ public:
/// <param name="parent">The parent actor (can be null to link it to the first loaded scene).</param>
/// <param name="transform">The spawn transform.</param>
/// <param name="duration">The effect playback duration (in seconds).</param>
/// <param name="autoDestroy">If set to <c>true</c> effect be be auto-destroyed after duration.</param>
/// <param name="autoDestroy">If set to <c>true</c> effect will be auto-destroyed after duration.</param>
/// <returns>The spawned effect.</returns>
API_FUNCTION() ParticleEffect* Spawn(Actor* parent, const Transform& transform, float duration = MAX_float, bool autoDestroy = false);
public:
// [BinaryAsset]
#if USE_EDITOR
void GetReferences(Array<Guid>& assets, Array<String>& files) const override
{
BinaryAsset::GetReferences(assets, files);
Graph.GetReferences(assets);
}
void GetReferences(Array<Guid>& assets, Array<String>& files) const override;
bool Save(const StringView& path = StringView::Empty) override;
#endif
protected:
@@ -186,8 +182,6 @@ protected:
AssetChunksFlag getChunksToPreload() const override;
#if USE_EDITOR
void OnDependencyModified(BinaryAsset* asset) override;
#endif
#if USE_EDITOR
void InitCompilationOptions(ShaderCompilationOptions& options) override;
#endif
};

View File

@@ -6,6 +6,7 @@
#include "Engine/Threading/Threading.h"
#if USE_EDITOR
#include "Engine/Core/Types/DataContainer.h"
#include "Engine/Serialization/MemoryWriteStream.h"
#endif
#include "Engine/Content/Factories/BinaryAssetFactory.h"
@@ -44,7 +45,7 @@ Asset::LoadResult ParticleEmitterFunction::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;
for (int32 i = 0; i < Graph.Nodes.Count(); i++)
{
@@ -103,7 +104,7 @@ AssetChunksFlag ParticleEmitterFunction::getChunksToPreload() const
return GET_CHUNK_FLAG(0);
}
bool ParticleEmitterFunction::LoadSurface(ParticleEmitterGraphCPU& graph)
bool ParticleEmitterFunction::LoadSurface(ParticleEmitterGraphCPU& graph, bool loadMeta)
{
if (WaitForLoaded())
return true;
@@ -114,7 +115,7 @@ bool ParticleEmitterFunction::LoadSurface(ParticleEmitterGraphCPU& graph)
{
const auto surfaceChunk = GetChunk(0);
MemoryReadStream stream(surfaceChunk->Get(), surfaceChunk->Size());
return graph.Load(&stream, false);
return graph.Load(&stream, loadMeta);
}
}
return true;
@@ -141,7 +142,7 @@ BytesContainer ParticleEmitterFunction::LoadSurface()
#if COMPILE_WITH_PARTICLE_GPU_GRAPH
bool ParticleEmitterFunction::LoadSurface(ParticleEmitterGraphGPU& graph)
bool ParticleEmitterFunction::LoadSurface(ParticleEmitterGraphGPU& graph) const
{
if (WaitForLoaded())
return true;
@@ -178,19 +179,10 @@ void ParticleEmitterFunction::GetSignature(Array<StringView, FixedAllocation<32>
}
}
bool ParticleEmitterFunction::SaveSurface(BytesContainer& data)
bool ParticleEmitterFunction::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
@@ -210,4 +202,17 @@ bool ParticleEmitterFunction::SaveSurface(BytesContainer& data)
return false;
}
bool ParticleEmitterFunction::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

@@ -31,23 +31,21 @@ public:
Array<int32, FixedAllocation<16>> Outputs;
#if COMPILE_WITH_PARTICLE_GPU_GRAPH
/// <summary>
/// The loaded GPU particle function graph.
/// </summary>
ParticleEmitterGraphGPU GraphGPU;
#endif
/// <summary>
/// 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(ParticleEmitterGraphCPU& graph);
bool LoadSurface(ParticleEmitterGraphCPU& graph, bool loadMeta = false);
#if USE_EDITOR
/// <summary>
/// Tries to load surface graph from the asset.
/// </summary>
@@ -55,14 +53,12 @@ public:
API_FUNCTION() BytesContainer LoadSurface();
#if COMPILE_WITH_PARTICLE_GPU_GRAPH
/// <summary>
/// Tries to load surface graph from the asset.
/// </summary>
/// <param name="graph">The graph to load.</param>
/// <returns>True if failed, otherwise false.</returns>
bool LoadSurface(ParticleEmitterGraphGPU& graph);
bool LoadSurface(ParticleEmitterGraphGPU& graph) const;
#endif
// Gets the function signature for Visject Surface editor.
@@ -73,8 +69,13 @@ public:
/// </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

@@ -4,6 +4,7 @@
#include "ParticleEffect.h"
#include "Engine/Core/Types/CommonValue.h"
#include "Engine/Level/Level.h"
#include "Engine/Content/Deprecated.h"
#include "Engine/Content/Factories/BinaryAssetFactory.h"
#include "Engine/Serialization/MemoryReadStream.h"
#include "Engine/Serialization/MemoryWriteStream.h"
@@ -46,7 +47,7 @@ void ParticleSystem::Init(ParticleEmitter* emitter, float duration, float fps)
}
}
BytesContainer ParticleSystem::LoadTimeline()
BytesContainer ParticleSystem::LoadTimeline() const
{
BytesContainer result;
ScopeLock lock(Locker);
@@ -108,25 +109,16 @@ BytesContainer ParticleSystem::LoadTimeline()
}
// Set output data
result.Copy(stream.GetHandle(), stream.GetPosition());
result.Copy(ToSpan(stream));
return result;
}
#if USE_EDITOR
bool ParticleSystem::SaveTimeline(BytesContainer& data)
bool ParticleSystem::SaveTimeline(const BytesContainer& data) const
{
// 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
@@ -197,6 +189,15 @@ void ParticleSystem::GetReferences(Array<Guid>& assets, Array<String>& files) co
}
}
bool ParticleSystem::Save(const StringView& path)
{
if (OnCheckSave(path))
return true;
ScopeLock lock(Locker);
BytesContainer data = LoadTimeline();
return SaveTimeline(data);
}
#endif
Asset::LoadResult ParticleSystem::load()
@@ -225,6 +226,7 @@ Asset::LoadResult ParticleSystem::load()
case 1:
{
// [Deprecated on 23.07.2019, expires on 27.04.2021]
MARK_CONTENT_DEPRECATED();
// Load properties
stream.ReadFloat(&FramesPerSecond);
@@ -299,6 +301,7 @@ Asset::LoadResult ParticleSystem::load()
case 2:
{
// [Deprecated on 31.07.2020, expires on 31.07.2022]
MARK_CONTENT_DEPRECATED();
// Load properties
stream.ReadFloat(&FramesPerSecond);
@@ -372,6 +375,7 @@ Asset::LoadResult ParticleSystem::load()
}
PRAGMA_ENABLE_DEPRECATION_WARNINGS
case 3: // [Deprecated on 03.09.2021 expires on 03.09.2023]
MARK_CONTENT_DEPRECATED();
case 4:
{
// Load properties

View File

@@ -156,17 +156,15 @@ public:
/// Loads the serialized timeline data.
/// </summary>
/// <returns>The output surface data, or empty if failed to load.</returns>
API_FUNCTION() BytesContainer LoadTimeline();
API_FUNCTION() BytesContainer LoadTimeline() const;
#if USE_EDITOR
/// <summary>
/// Saves the serialized timeline data to the asset.
/// </summary>
/// <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);
API_FUNCTION() bool SaveTimeline(const BytesContainer& data) const;
#endif
public:
@@ -243,6 +241,7 @@ public:
void InitAsVirtual() override;
#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

@@ -1,10 +1,11 @@
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
#include "WheeledVehicle.h"
#include "Engine/Physics/Physics.h"
#include "Engine/Level/Scene/Scene.h"
#include "Engine/Physics/Physics.h"
#include "Engine/Physics/PhysicsBackend.h"
#include "Engine/Physics/PhysicsScene.h"
#include "Engine/Content/Deprecated.h"
#include "Engine/Serialization/Serialization.h"
#if USE_EDITOR
#include "Engine/Level/Scene/SceneRendering.h"
@@ -476,7 +477,11 @@ void WheeledVehicle::Deserialize(DeserializeStream& stream, ISerializeModifier*
DESERIALIZE_MEMBER(AntiRollBars, _antiRollBars);
// [Deprecated on 13.06.2023, expires on 13.06.2025]
_fixInvalidForwardDir |= modifier->EngineBuild < 6341;
if (modifier->EngineBuild < 6341)
{
MARK_CONTENT_DEPRECATED();
_fixInvalidForwardDir = true;
}
}
void WheeledVehicle::OnColliderChanged(Collider* c)

View File

@@ -167,18 +167,8 @@ bool FontAsset::Init(const BytesContainer& fontFile)
bool FontAsset::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);
AssetInitData data;

View File

@@ -166,15 +166,6 @@ public:
/// <returns>True if cannot init, otherwise false.</returns>
API_FUNCTION() bool Init(const BytesContainer& fontFile);
#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
/// <summary>
/// Check if the font contains the glyph of a char.
/// </summary>
@@ -190,6 +181,9 @@ public:
public:
// [BinaryAsset]
uint64 GetMemoryUsage() const override;
#if USE_EDITOR
bool Save(const StringView& path = StringView::Empty) override;
#endif
protected:
// [BinaryAsset]

View File

@@ -285,9 +285,11 @@ DateTime JsonTools::GetDateTime(const Value& value)
}
PRAGMA_DISABLE_DEPRECATION_WARNINGS
#include "Engine/Content/Deprecated.h"
CommonValue JsonTools::GetCommonValue(const Value& value)
{
// [Deprecated on 31.07.2020, expires on 31.07.2022]
MARK_CONTENT_DEPRECATED();
CommonValue result;
const auto typeMember = value.FindMember("Type");
const auto valueMember = value.FindMember("Value");

View File

@@ -7,13 +7,14 @@
#include "Engine/Level/Scene/SceneRendering.h"
#include "Engine/Serialization/Serialization.h"
#include "Engine/Physics/Physics.h"
#include "Engine/Physics/PhysicsScene.h"
#include "Engine/Physics/PhysicalMaterial.h"
#include "Engine/Physics/PhysicsBackend.h"
#include "Engine/Content/Deprecated.h"
#include "Engine/Graphics/RenderView.h"
#include "Engine/Graphics/RenderTask.h"
#include "Engine/Graphics/Textures/GPUTexture.h"
#include "Engine/Level/Scene/Scene.h"
#include "Engine/Physics/PhysicsScene.h"
#include "Engine/Profiler/ProfilerCPU.h"
#include "Engine/Renderer/GlobalSignDistanceFieldPass.h"
#include "Engine/Renderer/GI/GlobalSurfaceAtlasPass.h"
@@ -810,16 +811,23 @@ void Terrain::Deserialize(DeserializeStream& stream, ISerializeModifier* modifie
// [Deprecated on 07.02.2022, expires on 07.02.2024]
if (modifier->EngineBuild <= 6330)
{
MARK_CONTENT_DEPRECATED();
DrawModes |= DrawPass::GlobalSDF;
}
// [Deprecated on 27.04.2022, expires on 27.04.2024]
if (modifier->EngineBuild <= 6331)
{
MARK_CONTENT_DEPRECATED();
DrawModes |= DrawPass::GlobalSurfaceAtlas;
}
// [Deprecated on 15.02.2024, expires on 15.02.2026]
JsonAssetReference<PhysicalMaterial> PhysicalMaterial;
DESERIALIZE(PhysicalMaterial);
if (PhysicalMaterial)
{
MARK_CONTENT_DEPRECATED();
for (auto& e : _physicalMaterials)
e = PhysicalMaterial;
}

View File

@@ -16,6 +16,7 @@
#include "Engine/Serialization/Serialization.h"
#include "Engine/Content/Assets/MaterialInstance.h"
#include "Engine/Content/Content.h"
#include "Engine/Content/Deprecated.h"
#include "Engine/Core/Types/Variant.h"
#include "Engine/Graphics/RenderTools.h"
#include "Engine/Graphics/Shaders/GPUVertexLayout.h"
@@ -482,10 +483,16 @@ void TextRender::Deserialize(DeserializeStream& stream, ISerializeModifier* modi
// [Deprecated on 07.02.2022, expires on 07.02.2024]
if (modifier->EngineBuild <= 6330)
{
MARK_CONTENT_DEPRECATED();
DrawModes |= DrawPass::GlobalSDF;
}
// [Deprecated on 27.04.2022, expires on 27.04.2024]
if (modifier->EngineBuild <= 6331)
{
MARK_CONTENT_DEPRECATED();
DrawModes |= DrawPass::GlobalSurfaceAtlas;
}
_isDirty = true;
}

View File

@@ -7,6 +7,7 @@
#include "VisjectMeta.h"
#include "GraphNode.h"
#include "GraphParameter.h"
#include "Engine/Content/Deprecated.h"
#include "Engine/Serialization/ReadStream.h"
#include "Engine/Serialization/WriteStream.h"
@@ -183,6 +184,7 @@ public:
if (version < 7000)
{
// [Deprecated on 31.07.2020, expires on 31.07.2022]
MARK_CONTENT_DEPRECATED();
// Time saved
int64 timeSaved;