Add support for editing texture group in editor (without reimporting)

This commit is contained in:
Wojtek Figat
2021-06-18 10:49:04 +02:00
parent 2d88ed17d4
commit 3b31fd7c71
16 changed files with 141 additions and 91 deletions

View File

@@ -4,7 +4,10 @@ using System.Xml;
using FlaxEditor.Content; using FlaxEditor.Content;
using FlaxEditor.Content.Import; using FlaxEditor.Content.Import;
using FlaxEditor.CustomEditors; using FlaxEditor.CustomEditors;
using FlaxEditor.CustomEditors.Dedicated;
using FlaxEditor.CustomEditors.Editors; using FlaxEditor.CustomEditors.Editors;
using FlaxEditor.GUI;
using FlaxEditor.Scripting;
using FlaxEditor.Viewport.Previews; using FlaxEditor.Viewport.Previews;
using FlaxEngine; using FlaxEngine;
using FlaxEngine.GUI; using FlaxEngine.GUI;
@@ -18,47 +21,57 @@ namespace FlaxEditor.Windows.Assets
/// <seealso cref="FlaxEditor.Windows.Assets.AssetEditorWindow" /> /// <seealso cref="FlaxEditor.Windows.Assets.AssetEditorWindow" />
public sealed class TextureWindow : AssetEditorWindowBase<Texture> public sealed class TextureWindow : AssetEditorWindowBase<Texture>
{ {
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();
}
}
/// <summary> /// <summary>
/// The texture properties proxy object. /// The texture properties proxy object.
/// </summary> /// </summary>
[CustomEditor(typeof(ProxyEditor))] [CustomEditor(typeof(ProxyEditor))]
private sealed class PropertiesProxy private sealed class PropertiesProxy
{ {
private TextureWindow _window; internal TextureWindow _window;
[EditorOrder(1000), EditorDisplay("Import Settings", EditorDisplayAttribute.InlineStyle)] [EditorOrder(1000), EditorDisplay("Import Settings", EditorDisplayAttribute.InlineStyle)]
public TextureImportSettings ImportSettings = new TextureImportSettings(); 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();
}
}
/// <summary> /// <summary>
/// Gathers parameters from the specified texture. /// Gathers parameters from the specified texture.
/// </summary> /// </summary>
@@ -110,7 +123,7 @@ namespace FlaxEditor.Windows.Assets
private readonly SplitPanel _split; private readonly SplitPanel _split;
private readonly TexturePreview _preview; private readonly TexturePreview _preview;
private readonly CustomEditorPresenter _propertiesEditor; private readonly CustomEditorPresenter _propertiesEditor;
private readonly ToolStripButton _saveButton;
private readonly PropertiesProxy _properties; private readonly PropertiesProxy _properties;
private bool _isWaitingForLoad; private bool _isWaitingForLoad;
@@ -140,6 +153,7 @@ namespace FlaxEditor.Windows.Assets
_propertiesEditor.Select(_properties); _propertiesEditor.Select(_properties);
// Toolstrip // Toolstrip
_saveButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Save64, Save).LinkTooltip("Save");
_toolstrip.AddButton(Editor.Icons.Import64, () => Editor.ContentImporting.Reimport((BinaryAssetItem)Item)).LinkTooltip("Reimport"); _toolstrip.AddButton(Editor.Icons.Import64, () => Editor.ContentImporting.Reimport((BinaryAssetItem)Item)).LinkTooltip("Reimport");
_toolstrip.AddSeparator(); _toolstrip.AddSeparator();
_toolstrip.AddButton(Editor.Icons.CenterView64, _preview.CenterView).LinkTooltip("Center view"); _toolstrip.AddButton(Editor.Icons.CenterView64, _preview.CenterView).LinkTooltip("Center view");
@@ -173,6 +187,14 @@ namespace FlaxEditor.Windows.Assets
_isWaitingForLoad = true; _isWaitingForLoad = true;
} }
/// <inheritdoc />
protected override void UpdateToolstrip()
{
_saveButton.Enabled = IsEdited;
base.UpdateToolstrip();
}
/// <inheritdoc /> /// <inheritdoc />
protected override void OnClose() protected override void OnClose()
{ {
@@ -182,6 +204,21 @@ namespace FlaxEditor.Windows.Assets
base.OnClose(); base.OnClose();
} }
/// <inheritdoc />
public override void Save()
{
if (!IsEdited)
return;
if (Asset.Save())
{
Editor.LogError("Cannot save asset.");
return;
}
ClearEditedFlag();
}
/// <inheritdoc /> /// <inheritdoc />
public override void Update(float deltaTime) public override void Update(float deltaTime)
{ {

View File

@@ -401,7 +401,7 @@ Asset::LoadResult AudioClip::load()
if (AudioHeader.Streamable) if (AudioHeader.Streamable)
{ {
// Do nothing because data streaming starts when any AudioSource requests the data // Do nothing because data streaming starts when any AudioSource requests the data
startStreaming(false); StartStreaming(false);
return LoadResult::Ok; return LoadResult::Ok;
} }
@@ -450,7 +450,7 @@ Asset::LoadResult AudioClip::load()
void AudioClip::unload(bool isReloading) void AudioClip::unload(bool isReloading)
{ {
stopStreaming(); StopStreaming();
StreamingQueue.Clear(); StreamingQueue.Clear();
if (Buffers.HasItems()) if (Buffers.HasItems())
{ {

View File

@@ -111,6 +111,11 @@ Asset::Asset(const SpawnParams& params, const AssetInfo* info)
{ {
} }
int32 Asset::GetReferencesCount() const
{
return (int32)Platform::AtomicRead(const_cast<int64 volatile*>(&_refCount));
}
String Asset::ToString() const String Asset::ToString() const
{ {
return String::Format(TEXT("{0}, {1}, {2}"), GetTypeName(), GetID(), GetPath()); return String::Format(TEXT("{0}, {1}, {2}"), GetTypeName(), GetID(), GetPath());
@@ -435,6 +440,15 @@ void Asset::startLoading()
_loadingTask->Start(); _loadingTask->Start();
} }
void Asset::releaseStorage()
{
}
bool Asset::IsInternalType() const
{
return false;
}
bool Asset::onLoad(LoadAssetTask* task) 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) // It may fail when task is cancelled and new one is created later (don't crash but just end with an error)

View File

@@ -82,10 +82,7 @@ public:
/// <summary> /// <summary>
/// Gets asset's reference count. Asset will be automatically unloaded when this reaches zero. /// Gets asset's reference count. Asset will be automatically unloaded when this reaches zero.
/// </summary> /// </summary>
API_PROPERTY() int32 GetReferencesCount() const API_PROPERTY() int32 GetReferencesCount() const;
{
return (int32)Platform::AtomicRead(const_cast<int64 volatile*>(&_refCount));
}
/// <summary> /// <summary>
/// Adds reference to that asset. /// Adds reference to that asset.
@@ -213,9 +210,7 @@ protected:
/// <summary> /// <summary>
/// Releases the storage file/container handle to prevent issues when renaming or moving the asset. /// Releases the storage file/container handle to prevent issues when renaming or moving the asset.
/// </summary> /// </summary>
virtual void releaseStorage() virtual void releaseStorage();
{
}
/// <summary> /// <summary>
/// Loads asset /// Loads asset
@@ -231,10 +226,7 @@ protected:
protected: protected:
virtual bool IsInternalType() const virtual bool IsInternalType() const;
{
return false;
}
bool onLoad(LoadAssetTask* task); bool onLoad(LoadAssetTask* task);
void onLoaded(); void onLoaded();

View File

@@ -570,7 +570,7 @@ bool Model::Init(const Span<int32>& meshesCountPerLod)
} }
// Dispose previous data and disable streaming (will start data uploading tasks manually) // Dispose previous data and disable streaming (will start data uploading tasks manually)
stopStreaming(); StopStreaming();
// Setup // Setup
MaterialSlots.Resize(1); MaterialSlots.Resize(1);
@@ -827,7 +827,7 @@ Asset::LoadResult Model::load()
#endif #endif
// Request resource streaming // Request resource streaming
startStreaming(true); StartStreaming(true);
return LoadResult::Ok; return LoadResult::Ok;
} }

View File

@@ -670,7 +670,7 @@ bool SkinnedModel::Init(const Span<int32>& meshesCountPerLod)
} }
// Dispose previous data and disable streaming (will start data uploading tasks manually) // Dispose previous data and disable streaming (will start data uploading tasks manually)
stopStreaming(); StopStreaming();
// Setup // Setup
MaterialSlots.Resize(1); MaterialSlots.Resize(1);
@@ -973,7 +973,7 @@ Asset::LoadResult SkinnedModel::load()
} }
// Request resource streaming // Request resource streaming
startStreaming(true); StartStreaming(true);
return LoadResult::Ok; return LoadResult::Ok;
} }

View File

@@ -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 #if USE_EDITOR
bool Texture::Save(const StringView& path, const InitData* customData) bool Texture::Save(const StringView& path, const InitData* customData)

View File

@@ -10,23 +10,16 @@
API_CLASS(NoSpawn) class FLAXENGINE_API Texture : public TextureBase API_CLASS(NoSpawn) class FLAXENGINE_API Texture : public TextureBase
{ {
DECLARE_BINARY_ASSET_HEADER(Texture, TexturesSerializedVersion); DECLARE_BINARY_ASSET_HEADER(Texture, TexturesSerializedVersion);
public:
/// <summary> /// <summary>
/// Gets the texture format type. /// Gets the texture format type.
/// </summary> /// </summary>
FORCE_INLINE TextureFormatType GetFormatType() const TextureFormatType GetFormatType() const;
{
return _texture.GetFormatType();
}
/// <summary> /// <summary>
/// Returns true if texture is a normal map. /// Returns true if texture is a normal map.
/// </summary> /// </summary>
API_PROPERTY() FORCE_INLINE bool IsNormalMap() const API_PROPERTY() bool IsNormalMap() const;
{
return GetFormatType() == TextureFormatType::NormalMap;
}
public: public:

View File

@@ -117,7 +117,7 @@ bool StreamingTexture::Create(const TextureHeader& header)
#else #else
bool isDynamic = false; bool isDynamic = false;
#endif #endif
startStreaming(isDynamic); StartStreaming(isDynamic);
return false; return false;
} }

View File

@@ -11,6 +11,7 @@
/// </summary> /// </summary>
class FLAXENGINE_API StreamingTexture : public Object, public StreamableResource class FLAXENGINE_API StreamingTexture : public Object, public StreamableResource
{ {
friend class TextureBase;
friend class StreamTextureMipTask; friend class StreamTextureMipTask;
friend class StreamTextureResizeTask; friend class StreamTextureResizeTask;
protected: protected:
@@ -60,7 +61,6 @@ public:
/// <summary> /// <summary>
/// Gets total texture width (in texels) /// Gets total texture width (in texels)
/// </summary> /// </summary>
/// <returns>Texture width</returns>
FORCE_INLINE int32 TotalWidth() const FORCE_INLINE int32 TotalWidth() const
{ {
return _header.Width; return _header.Width;

View File

@@ -97,6 +97,20 @@ uint64 TextureBase::GetTotalMemoryUsage() const
return _texture.GetTotalMemoryUsage(); 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 TextureBase::GetMipData(int32 mipIndex, int32& rowPitch, int32& slicePitch)
{ {
BytesContainer result; BytesContainer result;
@@ -114,7 +128,6 @@ BytesContainer TextureBase::GetMipData(int32 mipIndex, int32& rowPitch, int32& s
} }
else else
{ {
// Wait for the asset header to be loaded
if (WaitForLoaded()) if (WaitForLoaded())
return result; return result;
@@ -127,7 +140,7 @@ BytesContainer TextureBase::GetMipData(int32 mipIndex, int32& rowPitch, int32& s
slicePitch = slicePitch1; slicePitch = slicePitch1;
// Ensure to have chunk loaded // Ensure to have chunk loaded
if (LoadChunk(calculateChunkIndex(mipIndex))) if (LoadChunk(CalculateChunkIndex(mipIndex)))
return result; return result;
} }
@@ -261,7 +274,7 @@ bool TextureBase::Init(void* ptr)
return Init(initData); return Init(initData);
} }
int32 TextureBase::calculateChunkIndex(int32 mipIndex) const int32 TextureBase::CalculateChunkIndex(int32 mipIndex) const
{ {
// Mips are in 0-13 chunks // Mips are in 0-13 chunks
return mipIndex; return mipIndex;
@@ -287,7 +300,7 @@ Task* TextureBase::RequestMipDataAsync(int32 mipIndex)
if (_customData) if (_customData)
return nullptr; return nullptr;
auto chunkIndex = calculateChunkIndex(mipIndex); auto chunkIndex = CalculateChunkIndex(mipIndex);
return (Task*)_parent->RequestChunkDataAsync(chunkIndex); return (Task*)_parent->RequestChunkDataAsync(chunkIndex);
} }
@@ -304,7 +317,7 @@ void TextureBase::GetMipData(int32 mipIndex, BytesContainer& data) const
return; return;
} }
auto chunkIndex = calculateChunkIndex(mipIndex); auto chunkIndex = CalculateChunkIndex(mipIndex);
_parent->GetChunkData(chunkIndex, data); _parent->GetChunkData(chunkIndex, data);
} }
@@ -316,7 +329,7 @@ void TextureBase::GetMipDataWithLoading(int32 mipIndex, BytesContainer& data) co
return; return;
} }
const auto chunkIndex = calculateChunkIndex(mipIndex); const auto chunkIndex = CalculateChunkIndex(mipIndex);
_parent->LoadChunk(chunkIndex); _parent->LoadChunk(chunkIndex);
_parent->GetChunkData(chunkIndex, data); _parent->GetChunkData(chunkIndex, data);
} }
@@ -399,8 +412,6 @@ bool TextureBase::InitData::GenerateMip(int32 mipIndex, bool linear)
// Allocate data // Allocate data
const int32 dstMipWidth = Math::Max(1, Width >> mipIndex); const int32 dstMipWidth = Math::Max(1, Width >> mipIndex);
const int32 dstMipHeight = Math::Max(1, Height >> 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); const int32 pixelStride = PixelFormatExtensions::SizeInBytes(Format);
dstMip.RowPitch = dstMipWidth * pixelStride; dstMip.RowPitch = dstMipWidth * pixelStride;
dstMip.SlicePitch = dstMip.RowPitch * dstMipHeight; dstMip.SlicePitch = dstMip.RowPitch * dstMipHeight;

View File

@@ -50,9 +50,6 @@ protected:
StreamingTexture _texture; StreamingTexture _texture;
InitData* _customData; InitData* _customData;
private:
BinaryAsset* _parent; BinaryAsset* _parent;
public: public:
@@ -127,6 +124,16 @@ public:
/// </summary> /// </summary>
API_PROPERTY() uint64 GetTotalMemoryUsage() const; API_PROPERTY() uint64 GetTotalMemoryUsage() const;
/// <summary>
/// Gets the index of the texture group used by this texture.
/// </summary>
API_PROPERTY() int32 GetTextureGroup() const;
/// <summary>
/// Sets the index of the texture group used by this texture.
/// </summary>
API_PROPERTY() void SetTextureGroup(int32 textureGroup);
public: public:
/// <summary> /// <summary>
@@ -155,7 +162,7 @@ public:
protected: protected:
virtual int32 calculateChunkIndex(int32 mipIndex) const; virtual int32 CalculateChunkIndex(int32 mipIndex) const;
private: private:

View File

@@ -14,10 +14,10 @@ StreamableResource::StreamableResource(StreamingGroup* group)
StreamableResource::~StreamableResource() StreamableResource::~StreamableResource()
{ {
stopStreaming(); StopStreaming();
} }
void StreamableResource::startStreaming(bool isDynamic) void StreamableResource::StartStreaming(bool isDynamic)
{ {
_isDynamic = isDynamic; _isDynamic = isDynamic;
@@ -28,7 +28,7 @@ void StreamableResource::startStreaming(bool isDynamic)
} }
} }
void StreamableResource::stopStreaming() void StreamableResource::StopStreaming()
{ {
if (_isStreaming == true) if (_isStreaming == true)
{ {

View File

@@ -105,7 +105,6 @@ public:
struct StreamingCache struct StreamingCache
{ {
//float MinDstSinceLastUpdate = MAX_float;
DateTime LastUpdate = 0; DateTime LastUpdate = 0;
int32 TargetResidency = 0; int32 TargetResidency = 0;
DateTime TargetResidencyChange = 0; DateTime TargetResidencyChange = 0;
@@ -124,6 +123,6 @@ public:
protected: protected:
void startStreaming(bool isDynamic); void StartStreaming(bool isDynamic);
void stopStreaming(); void StopStreaming();
}; };

View File

@@ -59,7 +59,6 @@ void UpdateResource(StreamableResource* resource, DateTime now)
if (resource->IsDynamic()) if (resource->IsDynamic())
{ {
targetQuality = handler->CalculateTargetQuality(resource, now); 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); targetQuality = Math::Saturate(targetQuality);
} }

View File

@@ -14,9 +14,6 @@ class FLAXENGINE_API StreamingGroup
{ {
public: public:
/// <summary>
/// Declares the Group type
/// </summary>
DECLARE_ENUM_4(Type, Custom, Textures, Models, Audio); DECLARE_ENUM_4(Type, Custom, Textures, Models, Audio);
protected: protected:
@@ -38,7 +35,6 @@ public:
/// <summary> /// <summary>
/// Gets the group type. /// Gets the group type.
/// </summary> /// </summary>
/// <returns>Type</returns>
FORCE_INLINE Type GetType() const FORCE_INLINE Type GetType() const
{ {
return _type; return _type;
@@ -47,7 +43,6 @@ public:
/// <summary> /// <summary>
/// Gets the group type name. /// Gets the group type name.
/// </summary> /// </summary>
/// <returns>Typename</returns>
FORCE_INLINE const Char* GetTypename() const FORCE_INLINE const Char* GetTypename() const
{ {
return ToString(_type); return ToString(_type);
@@ -56,7 +51,6 @@ public:
/// <summary> /// <summary>
/// Gets the group streaming handler used by this group. /// Gets the group streaming handler used by this group.
/// </summary> /// </summary>
/// <returns>Handler</returns>
FORCE_INLINE IStreamingHandler* GetHandler() const FORCE_INLINE IStreamingHandler* GetHandler() const
{ {
return _handler; return _handler;
@@ -88,7 +82,6 @@ public:
/// <summary> /// <summary>
/// Gets textures group. /// Gets textures group.
/// </summary> /// </summary>
/// <returns>Group</returns>
FORCE_INLINE StreamingGroup* Textures() const FORCE_INLINE StreamingGroup* Textures() const
{ {
return _textures; return _textures;
@@ -97,7 +90,6 @@ public:
/// <summary> /// <summary>
/// Gets models group. /// Gets models group.
/// </summary> /// </summary>
/// <returns>Group</returns>
FORCE_INLINE StreamingGroup* Models() const FORCE_INLINE StreamingGroup* Models() const
{ {
return _models; return _models;
@@ -106,7 +98,6 @@ public:
/// <summary> /// <summary>
/// Gets skinned models group. /// Gets skinned models group.
/// </summary> /// </summary>
/// <returns>Group</returns>
FORCE_INLINE StreamingGroup* SkinnedModels() const FORCE_INLINE StreamingGroup* SkinnedModels() const
{ {
return _skinnedModels; return _skinnedModels;
@@ -115,7 +106,6 @@ public:
/// <summary> /// <summary>
/// Gets audio group. /// Gets audio group.
/// </summary> /// </summary>
/// <returns>Group</returns>
FORCE_INLINE StreamingGroup* Audio() const FORCE_INLINE StreamingGroup* Audio() const
{ {
return _audio; return _audio;
@@ -126,7 +116,6 @@ public:
/// <summary> /// <summary>
/// Gets all the groups. /// Gets all the groups.
/// </summary> /// </summary>
/// <returns>Groups.</returns>
FORCE_INLINE const Array<StreamingGroup*>& Groups() const FORCE_INLINE const Array<StreamingGroup*>& Groups() const
{ {
return _groups; return _groups;
@@ -135,7 +124,6 @@ public:
/// <summary> /// <summary>
/// Gets all the handlers. /// Gets all the handlers.
/// </summary> /// </summary>
/// <returns>Groups.</returns>
FORCE_INLINE const Array<IStreamingHandler*>& Handlers() const FORCE_INLINE const Array<IStreamingHandler*>& Handlers() const
{ {
return _handlers; return _handlers;