Add particle data layout debugger tab
This commit is contained in:
@@ -113,8 +113,55 @@ namespace FlaxEditor.Windows.Assets
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private sealed class LayoutTabProxy
|
||||||
|
{
|
||||||
|
[EditorDisplay("Layout"), CustomEditor(typeof(Editor)), NoSerialize]
|
||||||
|
// ReSharper disable once UnusedAutoPropertyAccessor.Local
|
||||||
|
public ParticleEmitterWindow Window;
|
||||||
|
|
||||||
|
private class Editor : CustomEditor
|
||||||
|
{
|
||||||
|
public override DisplayStyle Style => DisplayStyle.InlineIntoParent;
|
||||||
|
|
||||||
|
public override void Initialize(LayoutElementsContainer layout)
|
||||||
|
{
|
||||||
|
var window = (ParticleEmitterWindow)Values[0];
|
||||||
|
var emitter = window.Preview.Emitter;
|
||||||
|
if (emitter == null || !emitter.IsLoaded)
|
||||||
|
return;
|
||||||
|
var attributes = emitter.Layout;
|
||||||
|
var size = 0;
|
||||||
|
var height = 14;
|
||||||
|
foreach (var attribute in attributes)
|
||||||
|
{
|
||||||
|
layout.Label($" - {GetAttributeType(attribute.Format)} {attribute.Name}").Label.Height = height;
|
||||||
|
size += PixelFormatExtensions.SizeInBytes(attribute.Format);
|
||||||
|
}
|
||||||
|
var capacity = 0;
|
||||||
|
if (window.Surface != null && window.Surface.RootNode != null && window.Surface.RootNode.Values.Length > 0)
|
||||||
|
capacity = (int)window.Surface.RootNode.Values[0];
|
||||||
|
layout.Space(10);
|
||||||
|
layout.Label($"Particle size: {size} bytes\nParticle buffer size: {Utilities.Utils.FormatBytesCount((ulong)(size * capacity))}").Label.Height = height * 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetAttributeType(PixelFormat format)
|
||||||
|
{
|
||||||
|
switch (format)
|
||||||
|
{
|
||||||
|
case PixelFormat.R32_Float: return "float";
|
||||||
|
case PixelFormat.R32G32_Float: return "Float2";
|
||||||
|
case PixelFormat.R32G32B32_Float: return "Float3";
|
||||||
|
case PixelFormat.R32G32B32A32_Float: return "Float4";
|
||||||
|
case PixelFormat.R32_SInt: return "int";
|
||||||
|
case PixelFormat.R32_UInt: return "uint";
|
||||||
|
default: return format.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private readonly PropertiesProxy _properties;
|
private readonly PropertiesProxy _properties;
|
||||||
private Tab _previewTab;
|
private Tab _previewTab, _layoutTab;
|
||||||
private ToolStripButton _showSourceCodeButton;
|
private ToolStripButton _showSourceCodeButton;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
@@ -135,12 +182,14 @@ namespace FlaxEditor.Windows.Assets
|
|||||||
|
|
||||||
// Preview properties editor
|
// Preview properties editor
|
||||||
_previewTab = new Tab("Preview");
|
_previewTab = new Tab("Preview");
|
||||||
_previewTab.Presenter.Select(new PreviewProxy
|
_previewTab.Presenter.Select(new PreviewProxy { Window = this });
|
||||||
{
|
|
||||||
Window = this,
|
|
||||||
});
|
|
||||||
_tabs.AddTab(_previewTab);
|
_tabs.AddTab(_previewTab);
|
||||||
|
|
||||||
|
// Particle data layout
|
||||||
|
_layoutTab = new Tab("Layout");
|
||||||
|
_layoutTab.Presenter.Select(new LayoutTabProxy { Window = this });
|
||||||
|
_tabs.AddTab(_layoutTab);
|
||||||
|
|
||||||
// Surface
|
// Surface
|
||||||
_surface = new ParticleEmitterSurface(this, Save, _undo)
|
_surface = new ParticleEmitterSurface(this, Save, _undo)
|
||||||
{
|
{
|
||||||
@@ -239,6 +288,7 @@ namespace FlaxEditor.Windows.Assets
|
|||||||
_asset.WaitForLoaded();
|
_asset.WaitForLoaded();
|
||||||
_preview.PreviewActor.ResetSimulation();
|
_preview.PreviewActor.ResetSimulation();
|
||||||
_previewTab.Presenter.BuildLayoutOnUpdate();
|
_previewTab.Presenter.BuildLayoutOnUpdate();
|
||||||
|
_layoutTab.Presenter.BuildLayoutOnUpdate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -255,6 +305,7 @@ namespace FlaxEditor.Windows.Assets
|
|||||||
// Init asset properties and parameters proxy
|
// Init asset properties and parameters proxy
|
||||||
_properties.OnLoad(this);
|
_properties.OnLoad(this);
|
||||||
_previewTab.Presenter.BuildLayoutOnUpdate();
|
_previewTab.Presenter.BuildLayoutOnUpdate();
|
||||||
|
_layoutTab.Presenter.BuildLayoutOnUpdate();
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -519,4 +519,39 @@ bool ParticleEmitter::HasShaderCode() const
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Array<ParticleEmitter::Attribute> ParticleEmitter::GetLayout() const
|
||||||
|
{
|
||||||
|
Array<Attribute> result;
|
||||||
|
ScopeLock lock(Locker);
|
||||||
|
result.Resize(Graph.Layout.Attributes.Count());
|
||||||
|
for (int32 i = 0; i < result.Count(); i++)
|
||||||
|
{
|
||||||
|
auto& dst = result[i];
|
||||||
|
const auto& src = Graph.Layout.Attributes[i];
|
||||||
|
dst.Name = src.Name;
|
||||||
|
switch (src.ValueType)
|
||||||
|
{
|
||||||
|
case ParticleAttribute::ValueTypes::Float:
|
||||||
|
dst.Format = PixelFormat::R32_Float;
|
||||||
|
break;
|
||||||
|
case ParticleAttribute::ValueTypes::Float2:
|
||||||
|
dst.Format = PixelFormat::R32G32_Float;
|
||||||
|
break;
|
||||||
|
case ParticleAttribute::ValueTypes::Float3:
|
||||||
|
dst.Format = PixelFormat::R32G32B32_Float;
|
||||||
|
break;
|
||||||
|
case ParticleAttribute::ValueTypes::Float4:
|
||||||
|
dst.Format = PixelFormat::R32G32B32A32_Float;
|
||||||
|
break;
|
||||||
|
case ParticleAttribute::ValueTypes::Int:
|
||||||
|
dst.Format = PixelFormat::R32_SInt;
|
||||||
|
break;
|
||||||
|
case ParticleAttribute::ValueTypes::Uint:
|
||||||
|
dst.Format = PixelFormat::R32_UInt;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -177,10 +177,16 @@ public:
|
|||||||
void GetReferences(Array<Guid>& assets, Array<String>& files) const override;
|
void GetReferences(Array<Guid>& assets, Array<String>& files) const override;
|
||||||
bool Save(const StringView& path = StringView::Empty) override;
|
bool Save(const StringView& path = StringView::Empty) override;
|
||||||
|
|
||||||
/// <summary>
|
API_STRUCT(Internal) struct Attribute
|
||||||
/// Checks if the particle emitter has valid shader code present.
|
{
|
||||||
/// </summary>
|
DECLARE_SCRIPTING_TYPE_MINIMAL(Attribute);
|
||||||
API_PROPERTY() bool HasShaderCode() const;
|
API_FIELD() PixelFormat Format;
|
||||||
|
API_FIELD() String Name;
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
API_PROPERTY(Internal) bool HasShaderCode() const;
|
||||||
|
API_PROPERTY(Internal) Array<Attribute> GetLayout() const;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|||||||
@@ -6,6 +6,89 @@
|
|||||||
#include "Engine/Graphics/GPUDevice.h"
|
#include "Engine/Graphics/GPUDevice.h"
|
||||||
#include "Engine/Graphics/DynamicBuffer.h"
|
#include "Engine/Graphics/DynamicBuffer.h"
|
||||||
|
|
||||||
|
int32 ParticleAttribute::GetSize() const
|
||||||
|
{
|
||||||
|
switch (ValueType)
|
||||||
|
{
|
||||||
|
case ValueTypes::Float2:
|
||||||
|
return 8;
|
||||||
|
case ValueTypes::Float3:
|
||||||
|
return 12;
|
||||||
|
case ValueTypes::Float4:
|
||||||
|
return 16;
|
||||||
|
case ValueTypes::Float:
|
||||||
|
case ValueTypes::Int:
|
||||||
|
case ValueTypes::Uint:
|
||||||
|
return 4;
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParticleLayout::Clear()
|
||||||
|
{
|
||||||
|
Size = 0;
|
||||||
|
Attributes.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParticleLayout::UpdateLayout()
|
||||||
|
{
|
||||||
|
Size = 0;
|
||||||
|
for (int32 i = 0; i < Attributes.Count(); i++)
|
||||||
|
{
|
||||||
|
Attributes[i].Offset = Size;
|
||||||
|
Size += Attributes[i].GetSize();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int32 ParticleLayout::FindAttribute(const StringView& name) const
|
||||||
|
{
|
||||||
|
for (int32 i = 0; i < Attributes.Count(); i++)
|
||||||
|
{
|
||||||
|
if (name == Attributes[i].Name)
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32 ParticleLayout::FindAttribute(const StringView& name, ParticleAttribute::ValueTypes valueType) const
|
||||||
|
{
|
||||||
|
for (int32 i = 0; i < Attributes.Count(); i++)
|
||||||
|
{
|
||||||
|
if (Attributes[i].ValueType == valueType && name == Attributes[i].Name)
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32 ParticleLayout::FindAttributeOffset(const StringView& name, int32 fallbackValue) const
|
||||||
|
{
|
||||||
|
for (int32 i = 0; i < Attributes.Count(); i++)
|
||||||
|
{
|
||||||
|
if (name == Attributes[i].Name)
|
||||||
|
return Attributes[i].Offset;
|
||||||
|
}
|
||||||
|
return fallbackValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32 ParticleLayout::FindAttributeOffset(const StringView& name, ParticleAttribute::ValueTypes valueType, int32 fallbackValue) const
|
||||||
|
{
|
||||||
|
for (int32 i = 0; i < Attributes.Count(); i++)
|
||||||
|
{
|
||||||
|
if (Attributes[i].ValueType == valueType && name == Attributes[i].Name)
|
||||||
|
return Attributes[i].Offset;
|
||||||
|
}
|
||||||
|
return fallbackValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32 ParticleLayout::AddAttribute(const StringView& name, ParticleAttribute::ValueTypes valueType)
|
||||||
|
{
|
||||||
|
auto& a = Attributes.AddOne();
|
||||||
|
a.Name = String(*name, name.Length());
|
||||||
|
a.ValueType = valueType;
|
||||||
|
return Attributes.Count() - 1;
|
||||||
|
}
|
||||||
|
|
||||||
ParticleBuffer::ParticleBuffer()
|
ParticleBuffer::ParticleBuffer()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,25 +52,7 @@ struct ParticleAttribute
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the size of the attribute (in bytes).
|
/// Gets the size of the attribute (in bytes).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>The size (in bytes).</returns>
|
int32 GetSize() const;
|
||||||
int32 GetSize() const
|
|
||||||
{
|
|
||||||
switch (ValueType)
|
|
||||||
{
|
|
||||||
case ValueTypes::Float2:
|
|
||||||
return 8;
|
|
||||||
case ValueTypes::Float3:
|
|
||||||
return 12;
|
|
||||||
case ValueTypes::Float4:
|
|
||||||
return 16;
|
|
||||||
case ValueTypes::Float:
|
|
||||||
case ValueTypes::Int:
|
|
||||||
case ValueTypes::Uint:
|
|
||||||
return 4;
|
|
||||||
default:
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -93,41 +75,19 @@ public:
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Clears the layout data.
|
/// Clears the layout data.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void Clear()
|
void Clear();
|
||||||
{
|
|
||||||
Size = 0;
|
|
||||||
Attributes.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Updates the attributes layout (calculates offset) and updates the total size of the layout.
|
/// Updates the attributes layout (calculates offset) and updates the total size of the layout.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void UpdateLayout()
|
void UpdateLayout();
|
||||||
{
|
|
||||||
Size = 0;
|
|
||||||
for (int32 i = 0; i < Attributes.Count(); i++)
|
|
||||||
{
|
|
||||||
Attributes[i].Offset = Size;
|
|
||||||
Size += Attributes[i].GetSize();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Finds the attribute by the name.
|
/// Finds the attribute by the name.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="name">The name.</param>
|
/// <param name="name">The name.</param>
|
||||||
/// <returns>The attribute index or -1 if cannot find it.</returns>
|
/// <returns>The attribute index or -1 if cannot find it.</returns>
|
||||||
int32 FindAttribute(const StringView& name) const
|
int32 FindAttribute(const StringView& name) const;
|
||||||
{
|
|
||||||
for (int32 i = 0; i < Attributes.Count(); i++)
|
|
||||||
{
|
|
||||||
if (name == Attributes[i].Name)
|
|
||||||
{
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Finds the attribute by the name and type.
|
/// Finds the attribute by the name and type.
|
||||||
@@ -135,17 +95,7 @@ public:
|
|||||||
/// <param name="name">The name.</param>
|
/// <param name="name">The name.</param>
|
||||||
/// <param name="valueType">The type.</param>
|
/// <param name="valueType">The type.</param>
|
||||||
/// <returns>The attribute index or -1 if cannot find it.</returns>
|
/// <returns>The attribute index or -1 if cannot find it.</returns>
|
||||||
int32 FindAttribute(const StringView& name, ParticleAttribute::ValueTypes valueType) const
|
int32 FindAttribute(const StringView& name, ParticleAttribute::ValueTypes valueType) const;
|
||||||
{
|
|
||||||
for (int32 i = 0; i < Attributes.Count(); i++)
|
|
||||||
{
|
|
||||||
if (Attributes[i].ValueType == valueType && name == Attributes[i].Name)
|
|
||||||
{
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Finds the attribute offset by the name.
|
/// Finds the attribute offset by the name.
|
||||||
@@ -153,17 +103,7 @@ public:
|
|||||||
/// <param name="name">The name.</param>
|
/// <param name="name">The name.</param>
|
||||||
/// <param name="fallbackValue">The fallback value to return if attribute is missing.</param>
|
/// <param name="fallbackValue">The fallback value to return if attribute is missing.</param>
|
||||||
/// <returns>The attribute offset or fallback value if cannot find it.</returns>
|
/// <returns>The attribute offset or fallback value if cannot find it.</returns>
|
||||||
int32 FindAttributeOffset(const StringView& name, int32 fallbackValue = 0) const
|
int32 FindAttributeOffset(const StringView& name, int32 fallbackValue = 0) const;
|
||||||
{
|
|
||||||
for (int32 i = 0; i < Attributes.Count(); i++)
|
|
||||||
{
|
|
||||||
if (name == Attributes[i].Name)
|
|
||||||
{
|
|
||||||
return Attributes[i].Offset;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return fallbackValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Finds the attribute offset by the name.
|
/// Finds the attribute offset by the name.
|
||||||
@@ -172,17 +112,7 @@ public:
|
|||||||
/// <param name="valueType">The type.</param>
|
/// <param name="valueType">The type.</param>
|
||||||
/// <param name="fallbackValue">The fallback value to return if attribute is missing.</param>
|
/// <param name="fallbackValue">The fallback value to return if attribute is missing.</param>
|
||||||
/// <returns>The attribute offset or fallback value if cannot find it.</returns>
|
/// <returns>The attribute offset or fallback value if cannot find it.</returns>
|
||||||
int32 FindAttributeOffset(const StringView& name, ParticleAttribute::ValueTypes valueType, int32 fallbackValue = 0) const
|
int32 FindAttributeOffset(const StringView& name, ParticleAttribute::ValueTypes valueType, int32 fallbackValue = 0) const;
|
||||||
{
|
|
||||||
for (int32 i = 0; i < Attributes.Count(); i++)
|
|
||||||
{
|
|
||||||
if (Attributes[i].ValueType == valueType && name == Attributes[i].Name)
|
|
||||||
{
|
|
||||||
return Attributes[i].Offset;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return fallbackValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the attribute offset by the attribute index.
|
/// Gets the attribute offset by the attribute index.
|
||||||
@@ -201,13 +131,7 @@ public:
|
|||||||
/// <param name="name">The name.</param>
|
/// <param name="name">The name.</param>
|
||||||
/// <param name="valueType">The value type.</param>
|
/// <param name="valueType">The value type.</param>
|
||||||
/// <returns>The attribute index or -1 if cannot find it.</returns>
|
/// <returns>The attribute index or -1 if cannot find it.</returns>
|
||||||
int32 AddAttribute(const StringView& name, ParticleAttribute::ValueTypes valueType)
|
int32 AddAttribute(const StringView& name, ParticleAttribute::ValueTypes valueType);
|
||||||
{
|
|
||||||
auto& a = Attributes.AddOne();
|
|
||||||
a.Name = String(*name, name.Length());
|
|
||||||
a.ValueType = valueType;
|
|
||||||
return Attributes.Count() - 1;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
Reference in New Issue
Block a user