diff --git a/Source/Editor/Windows/Assets/TextureWindow.cs b/Source/Editor/Windows/Assets/TextureWindow.cs index 481c48f47..0ae8abe6d 100644 --- a/Source/Editor/Windows/Assets/TextureWindow.cs +++ b/Source/Editor/Windows/Assets/TextureWindow.cs @@ -4,7 +4,10 @@ using System.Xml; using FlaxEditor.Content; using FlaxEditor.Content.Import; using FlaxEditor.CustomEditors; +using FlaxEditor.CustomEditors.Dedicated; using FlaxEditor.CustomEditors.Editors; +using FlaxEditor.GUI; +using FlaxEditor.Scripting; using FlaxEditor.Viewport.Previews; using FlaxEngine; using FlaxEngine.GUI; @@ -18,47 +21,57 @@ namespace FlaxEditor.Windows.Assets /// public sealed class TextureWindow : AssetEditorWindowBase { + private sealed class ProxyEditor : GenericEditor + { + public override void Initialize(LayoutElementsContainer layout) + { + var window = ((PropertiesProxy)Values[0])._window; + var texture = window?.Asset; + if (texture == null || !texture.IsLoaded) + { + layout.Label("Loading...", TextAlignment.Center); + return; + } + + // Texture info + var general = layout.Group("General"); + general.Label("Format: " + texture.Format); + general.Label(string.Format("Size: {0}x{1}", texture.Width, texture.Height)); + general.Label("Mip levels: " + texture.MipLevels); + general.Label("Memory usage: " + Utilities.Utils.FormatBytesCount(texture.TotalMemoryUsage)); + + // Texture properties + var properties = layout.Group("Properties"); + var textureGroup = new CustomValueContainer(new ScriptType(typeof(int)), texture.TextureGroup, + (instance, index) => texture.TextureGroup, + (instance, index, value) => + { + texture.TextureGroup = (int)value; + window.MarkAsEdited(); + }); + properties.Property("Texture Group", textureGroup, new TextureGroupEditor(), "The texture group used by this texture."); + + // Import settings + base.Initialize(layout); + + // Reimport + layout.Space(10); + var reimportButton = layout.Button("Reimport"); + reimportButton.Button.Clicked += () => ((PropertiesProxy)Values[0]).Reimport(); + } + } + /// /// The texture properties proxy object. /// [CustomEditor(typeof(ProxyEditor))] private sealed class PropertiesProxy { - private TextureWindow _window; + internal TextureWindow _window; [EditorOrder(1000), EditorDisplay("Import Settings", EditorDisplayAttribute.InlineStyle)] public TextureImportSettings ImportSettings = new TextureImportSettings(); - public sealed class ProxyEditor : GenericEditor - { - public override void Initialize(LayoutElementsContainer layout) - { - var window = ((PropertiesProxy)Values[0])._window; - if (window == null) - { - layout.Label("Loading...", TextAlignment.Center); - return; - } - - // Texture properties - { - var texture = window.Asset; - - var group = layout.Group("General"); - group.Label("Format: " + texture.Format); - group.Label(string.Format("Size: {0}x{1}", texture.Width, texture.Height)); - group.Label("Mip levels: " + texture.MipLevels); - group.Label("Memory usage: " + Utilities.Utils.FormatBytesCount(texture.TotalMemoryUsage)); - } - - base.Initialize(layout); - - layout.Space(10); - var reimportButton = layout.Button("Reimport"); - reimportButton.Button.Clicked += () => ((PropertiesProxy)Values[0]).Reimport(); - } - } - /// /// Gathers parameters from the specified texture. /// @@ -110,7 +123,7 @@ namespace FlaxEditor.Windows.Assets private readonly SplitPanel _split; private readonly TexturePreview _preview; private readonly CustomEditorPresenter _propertiesEditor; - + private readonly ToolStripButton _saveButton; private readonly PropertiesProxy _properties; private bool _isWaitingForLoad; @@ -140,6 +153,7 @@ namespace FlaxEditor.Windows.Assets _propertiesEditor.Select(_properties); // Toolstrip + _saveButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Save64, Save).LinkTooltip("Save"); _toolstrip.AddButton(Editor.Icons.Import64, () => Editor.ContentImporting.Reimport((BinaryAssetItem)Item)).LinkTooltip("Reimport"); _toolstrip.AddSeparator(); _toolstrip.AddButton(Editor.Icons.CenterView64, _preview.CenterView).LinkTooltip("Center view"); @@ -173,6 +187,14 @@ namespace FlaxEditor.Windows.Assets _isWaitingForLoad = true; } + /// + protected override void UpdateToolstrip() + { + _saveButton.Enabled = IsEdited; + + base.UpdateToolstrip(); + } + /// protected override void OnClose() { @@ -182,6 +204,21 @@ namespace FlaxEditor.Windows.Assets base.OnClose(); } + /// + public override void Save() + { + if (!IsEdited) + return; + + if (Asset.Save()) + { + Editor.LogError("Cannot save asset."); + return; + } + + ClearEditedFlag(); + } + /// public override void Update(float deltaTime) { diff --git a/Source/Engine/Audio/AudioClip.cpp b/Source/Engine/Audio/AudioClip.cpp index 3213850d3..7c1945803 100644 --- a/Source/Engine/Audio/AudioClip.cpp +++ b/Source/Engine/Audio/AudioClip.cpp @@ -401,7 +401,7 @@ Asset::LoadResult AudioClip::load() if (AudioHeader.Streamable) { // Do nothing because data streaming starts when any AudioSource requests the data - startStreaming(false); + StartStreaming(false); return LoadResult::Ok; } @@ -450,7 +450,7 @@ Asset::LoadResult AudioClip::load() void AudioClip::unload(bool isReloading) { - stopStreaming(); + StopStreaming(); StreamingQueue.Clear(); if (Buffers.HasItems()) { diff --git a/Source/Engine/Content/Asset.cpp b/Source/Engine/Content/Asset.cpp index e8536f58d..5436cb6b9 100644 --- a/Source/Engine/Content/Asset.cpp +++ b/Source/Engine/Content/Asset.cpp @@ -111,6 +111,11 @@ Asset::Asset(const SpawnParams& params, const AssetInfo* info) { } +int32 Asset::GetReferencesCount() const +{ + return (int32)Platform::AtomicRead(const_cast(&_refCount)); +} + String Asset::ToString() const { return String::Format(TEXT("{0}, {1}, {2}"), GetTypeName(), GetID(), GetPath()); @@ -435,6 +440,15 @@ void Asset::startLoading() _loadingTask->Start(); } +void Asset::releaseStorage() +{ +} + +bool Asset::IsInternalType() const +{ + return false; +} + bool Asset::onLoad(LoadAssetTask* task) { // It may fail when task is cancelled and new one is created later (don't crash but just end with an error) diff --git a/Source/Engine/Content/Asset.h b/Source/Engine/Content/Asset.h index a831ee773..3d4ca2789 100644 --- a/Source/Engine/Content/Asset.h +++ b/Source/Engine/Content/Asset.h @@ -82,10 +82,7 @@ public: /// /// Gets asset's reference count. Asset will be automatically unloaded when this reaches zero. /// - API_PROPERTY() int32 GetReferencesCount() const - { - return (int32)Platform::AtomicRead(const_cast(&_refCount)); - } + API_PROPERTY() int32 GetReferencesCount() const; /// /// Adds reference to that asset. @@ -213,9 +210,7 @@ protected: /// /// Releases the storage file/container handle to prevent issues when renaming or moving the asset. /// - virtual void releaseStorage() - { - } + virtual void releaseStorage(); /// /// Loads asset @@ -231,10 +226,7 @@ protected: protected: - virtual bool IsInternalType() const - { - return false; - } + virtual bool IsInternalType() const; bool onLoad(LoadAssetTask* task); void onLoaded(); diff --git a/Source/Engine/Content/Assets/Model.cpp b/Source/Engine/Content/Assets/Model.cpp index dd9c484e1..d337c62a2 100644 --- a/Source/Engine/Content/Assets/Model.cpp +++ b/Source/Engine/Content/Assets/Model.cpp @@ -570,7 +570,7 @@ bool Model::Init(const Span& meshesCountPerLod) } // Dispose previous data and disable streaming (will start data uploading tasks manually) - stopStreaming(); + StopStreaming(); // Setup MaterialSlots.Resize(1); @@ -827,7 +827,7 @@ Asset::LoadResult Model::load() #endif // Request resource streaming - startStreaming(true); + StartStreaming(true); return LoadResult::Ok; } diff --git a/Source/Engine/Content/Assets/SkinnedModel.cpp b/Source/Engine/Content/Assets/SkinnedModel.cpp index b98ba2e57..3860aeda8 100644 --- a/Source/Engine/Content/Assets/SkinnedModel.cpp +++ b/Source/Engine/Content/Assets/SkinnedModel.cpp @@ -670,7 +670,7 @@ bool SkinnedModel::Init(const Span& meshesCountPerLod) } // Dispose previous data and disable streaming (will start data uploading tasks manually) - stopStreaming(); + StopStreaming(); // Setup MaterialSlots.Resize(1); @@ -973,7 +973,7 @@ Asset::LoadResult SkinnedModel::load() } // Request resource streaming - startStreaming(true); + StartStreaming(true); return LoadResult::Ok; } diff --git a/Source/Engine/Content/Assets/Texture.cpp b/Source/Engine/Content/Assets/Texture.cpp index 23dbff0e2..08100d4b5 100644 --- a/Source/Engine/Content/Assets/Texture.cpp +++ b/Source/Engine/Content/Assets/Texture.cpp @@ -17,6 +17,16 @@ Texture::Texture(const SpawnParams& params, const AssetInfo* info) { } +TextureFormatType Texture::GetFormatType() const +{ + return _texture.GetFormatType(); +} + +bool Texture::IsNormalMap() const +{ + return _texture.GetFormatType() == TextureFormatType::NormalMap; +} + #if USE_EDITOR bool Texture::Save(const StringView& path, const InitData* customData) diff --git a/Source/Engine/Content/Assets/Texture.h b/Source/Engine/Content/Assets/Texture.h index afefc59d2..2b0a6c127 100644 --- a/Source/Engine/Content/Assets/Texture.h +++ b/Source/Engine/Content/Assets/Texture.h @@ -10,23 +10,16 @@ API_CLASS(NoSpawn) class FLAXENGINE_API Texture : public TextureBase { DECLARE_BINARY_ASSET_HEADER(Texture, TexturesSerializedVersion); -public: /// /// Gets the texture format type. /// - FORCE_INLINE TextureFormatType GetFormatType() const - { - return _texture.GetFormatType(); - } + TextureFormatType GetFormatType() const; /// /// Returns true if texture is a normal map. /// - API_PROPERTY() FORCE_INLINE bool IsNormalMap() const - { - return GetFormatType() == TextureFormatType::NormalMap; - } + API_PROPERTY() bool IsNormalMap() const; public: diff --git a/Source/Engine/Graphics/Textures/StreamingTexture.cpp b/Source/Engine/Graphics/Textures/StreamingTexture.cpp index f4bd6c082..54063f536 100644 --- a/Source/Engine/Graphics/Textures/StreamingTexture.cpp +++ b/Source/Engine/Graphics/Textures/StreamingTexture.cpp @@ -117,7 +117,7 @@ bool StreamingTexture::Create(const TextureHeader& header) #else bool isDynamic = false; #endif - startStreaming(isDynamic); + StartStreaming(isDynamic); return false; } diff --git a/Source/Engine/Graphics/Textures/StreamingTexture.h b/Source/Engine/Graphics/Textures/StreamingTexture.h index 3aa8fee28..2317c52db 100644 --- a/Source/Engine/Graphics/Textures/StreamingTexture.h +++ b/Source/Engine/Graphics/Textures/StreamingTexture.h @@ -11,6 +11,7 @@ /// class FLAXENGINE_API StreamingTexture : public Object, public StreamableResource { + friend class TextureBase; friend class StreamTextureMipTask; friend class StreamTextureResizeTask; protected: @@ -60,7 +61,6 @@ public: /// /// Gets total texture width (in texels) /// - /// Texture width FORCE_INLINE int32 TotalWidth() const { return _header.Width; diff --git a/Source/Engine/Graphics/Textures/TextureBase.cpp b/Source/Engine/Graphics/Textures/TextureBase.cpp index 59acc488e..d9aa392cf 100644 --- a/Source/Engine/Graphics/Textures/TextureBase.cpp +++ b/Source/Engine/Graphics/Textures/TextureBase.cpp @@ -97,6 +97,20 @@ uint64 TextureBase::GetTotalMemoryUsage() const return _texture.GetTotalMemoryUsage(); } +int32 TextureBase::GetTextureGroup() const +{ + return _texture._header.TextureGroup; +} + +void TextureBase::SetTextureGroup(int32 textureGroup) +{ + if (_texture._header.TextureGroup != textureGroup) + { + _texture._header.TextureGroup = textureGroup; + _texture.RequestStreamingUpdate(); + } +} + BytesContainer TextureBase::GetMipData(int32 mipIndex, int32& rowPitch, int32& slicePitch) { BytesContainer result; @@ -114,7 +128,6 @@ BytesContainer TextureBase::GetMipData(int32 mipIndex, int32& rowPitch, int32& s } else { - // Wait for the asset header to be loaded if (WaitForLoaded()) return result; @@ -127,7 +140,7 @@ BytesContainer TextureBase::GetMipData(int32 mipIndex, int32& rowPitch, int32& s slicePitch = slicePitch1; // Ensure to have chunk loaded - if (LoadChunk(calculateChunkIndex(mipIndex))) + if (LoadChunk(CalculateChunkIndex(mipIndex))) return result; } @@ -261,7 +274,7 @@ bool TextureBase::Init(void* ptr) return Init(initData); } -int32 TextureBase::calculateChunkIndex(int32 mipIndex) const +int32 TextureBase::CalculateChunkIndex(int32 mipIndex) const { // Mips are in 0-13 chunks return mipIndex; @@ -287,7 +300,7 @@ Task* TextureBase::RequestMipDataAsync(int32 mipIndex) if (_customData) return nullptr; - auto chunkIndex = calculateChunkIndex(mipIndex); + auto chunkIndex = CalculateChunkIndex(mipIndex); return (Task*)_parent->RequestChunkDataAsync(chunkIndex); } @@ -304,7 +317,7 @@ void TextureBase::GetMipData(int32 mipIndex, BytesContainer& data) const return; } - auto chunkIndex = calculateChunkIndex(mipIndex); + auto chunkIndex = CalculateChunkIndex(mipIndex); _parent->GetChunkData(chunkIndex, data); } @@ -316,7 +329,7 @@ void TextureBase::GetMipDataWithLoading(int32 mipIndex, BytesContainer& data) co return; } - const auto chunkIndex = calculateChunkIndex(mipIndex); + const auto chunkIndex = CalculateChunkIndex(mipIndex); _parent->LoadChunk(chunkIndex); _parent->GetChunkData(chunkIndex, data); } @@ -399,8 +412,6 @@ bool TextureBase::InitData::GenerateMip(int32 mipIndex, bool linear) // Allocate data const int32 dstMipWidth = Math::Max(1, Width >> mipIndex); const int32 dstMipHeight = Math::Max(1, Height >> mipIndex); - const int32 srcMipWidth = Math::Max(1, Width >> (mipIndex - 1)); - const int32 srcMipHeight = Math::Max(1, Height >> (mipIndex - 1)); const int32 pixelStride = PixelFormatExtensions::SizeInBytes(Format); dstMip.RowPitch = dstMipWidth * pixelStride; dstMip.SlicePitch = dstMip.RowPitch * dstMipHeight; diff --git a/Source/Engine/Graphics/Textures/TextureBase.h b/Source/Engine/Graphics/Textures/TextureBase.h index bdc551397..2bb0042df 100644 --- a/Source/Engine/Graphics/Textures/TextureBase.h +++ b/Source/Engine/Graphics/Textures/TextureBase.h @@ -50,9 +50,6 @@ protected: StreamingTexture _texture; InitData* _customData; - -private: - BinaryAsset* _parent; public: @@ -126,6 +123,16 @@ public: /// Gets the total memory usage that texture may have in use (if loaded to the maximum quality). Exact value may differ due to memory alignment and resource allocation policy. /// API_PROPERTY() uint64 GetTotalMemoryUsage() const; + + /// + /// Gets the index of the texture group used by this texture. + /// + API_PROPERTY() int32 GetTextureGroup() const; + + /// + /// Sets the index of the texture group used by this texture. + /// + API_PROPERTY() void SetTextureGroup(int32 textureGroup); public: @@ -155,7 +162,7 @@ public: protected: - virtual int32 calculateChunkIndex(int32 mipIndex) const; + virtual int32 CalculateChunkIndex(int32 mipIndex) const; private: diff --git a/Source/Engine/Streaming/StreamableResource.cpp b/Source/Engine/Streaming/StreamableResource.cpp index af758e786..685faff03 100644 --- a/Source/Engine/Streaming/StreamableResource.cpp +++ b/Source/Engine/Streaming/StreamableResource.cpp @@ -14,10 +14,10 @@ StreamableResource::StreamableResource(StreamingGroup* group) StreamableResource::~StreamableResource() { - stopStreaming(); + StopStreaming(); } -void StreamableResource::startStreaming(bool isDynamic) +void StreamableResource::StartStreaming(bool isDynamic) { _isDynamic = isDynamic; @@ -28,7 +28,7 @@ void StreamableResource::startStreaming(bool isDynamic) } } -void StreamableResource::stopStreaming() +void StreamableResource::StopStreaming() { if (_isStreaming == true) { diff --git a/Source/Engine/Streaming/StreamableResource.h b/Source/Engine/Streaming/StreamableResource.h index 8b8854bfe..a27833149 100644 --- a/Source/Engine/Streaming/StreamableResource.h +++ b/Source/Engine/Streaming/StreamableResource.h @@ -105,7 +105,6 @@ public: struct StreamingCache { - //float MinDstSinceLastUpdate = MAX_float; DateTime LastUpdate = 0; int32 TargetResidency = 0; DateTime TargetResidencyChange = 0; @@ -124,6 +123,6 @@ public: protected: - void startStreaming(bool isDynamic); - void stopStreaming(); + void StartStreaming(bool isDynamic); + void StopStreaming(); }; diff --git a/Source/Engine/Streaming/Streaming.cpp b/Source/Engine/Streaming/Streaming.cpp index 1ce53cf75..a2e010105 100644 --- a/Source/Engine/Streaming/Streaming.cpp +++ b/Source/Engine/Streaming/Streaming.cpp @@ -59,7 +59,6 @@ void UpdateResource(StreamableResource* resource, DateTime now) if (resource->IsDynamic()) { targetQuality = handler->CalculateTargetQuality(resource, now); - // TODO: here we should apply resources group master scale (based on game settings quality level and memory level) targetQuality = Math::Saturate(targetQuality); } diff --git a/Source/Engine/Streaming/StreamingGroup.h b/Source/Engine/Streaming/StreamingGroup.h index 8f7cec045..8e3a78555 100644 --- a/Source/Engine/Streaming/StreamingGroup.h +++ b/Source/Engine/Streaming/StreamingGroup.h @@ -14,9 +14,6 @@ class FLAXENGINE_API StreamingGroup { public: - /// - /// Declares the Group type - /// DECLARE_ENUM_4(Type, Custom, Textures, Models, Audio); protected: @@ -38,7 +35,6 @@ public: /// /// Gets the group type. /// - /// Type FORCE_INLINE Type GetType() const { return _type; @@ -47,7 +43,6 @@ public: /// /// Gets the group type name. /// - /// Typename FORCE_INLINE const Char* GetTypename() const { return ToString(_type); @@ -56,7 +51,6 @@ public: /// /// Gets the group streaming handler used by this group. /// - /// Handler FORCE_INLINE IStreamingHandler* GetHandler() const { return _handler; @@ -88,7 +82,6 @@ public: /// /// Gets textures group. /// - /// Group FORCE_INLINE StreamingGroup* Textures() const { return _textures; @@ -97,7 +90,6 @@ public: /// /// Gets models group. /// - /// Group FORCE_INLINE StreamingGroup* Models() const { return _models; @@ -106,7 +98,6 @@ public: /// /// Gets skinned models group. /// - /// Group FORCE_INLINE StreamingGroup* SkinnedModels() const { return _skinnedModels; @@ -115,7 +106,6 @@ public: /// /// Gets audio group. /// - /// Group FORCE_INLINE StreamingGroup* Audio() const { return _audio; @@ -126,7 +116,6 @@ public: /// /// Gets all the groups. /// - /// Groups. FORCE_INLINE const Array& Groups() const { return _groups; @@ -135,7 +124,6 @@ public: /// /// Gets all the handlers. /// - /// Groups. FORCE_INLINE const Array& Handlers() const { return _handlers;