diff --git a/Source/Editor/Windows/Assets/ParticleEmitterWindow.cs b/Source/Editor/Windows/Assets/ParticleEmitterWindow.cs index 92cf6e61a..0c8653f5b 100644 --- a/Source/Editor/Windows/Assets/ParticleEmitterWindow.cs +++ b/Source/Editor/Windows/Assets/ParticleEmitterWindow.cs @@ -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 Tab _previewTab; + private Tab _previewTab, _layoutTab; private ToolStripButton _showSourceCodeButton; /// @@ -135,12 +182,14 @@ namespace FlaxEditor.Windows.Assets // Preview properties editor _previewTab = new Tab("Preview"); - _previewTab.Presenter.Select(new PreviewProxy - { - Window = this, - }); + _previewTab.Presenter.Select(new PreviewProxy { Window = this }); _tabs.AddTab(_previewTab); + // Particle data layout + _layoutTab = new Tab("Layout"); + _layoutTab.Presenter.Select(new LayoutTabProxy { Window = this }); + _tabs.AddTab(_layoutTab); + // Surface _surface = new ParticleEmitterSurface(this, Save, _undo) { @@ -239,6 +288,7 @@ namespace FlaxEditor.Windows.Assets _asset.WaitForLoaded(); _preview.PreviewActor.ResetSimulation(); _previewTab.Presenter.BuildLayoutOnUpdate(); + _layoutTab.Presenter.BuildLayoutOnUpdate(); } } @@ -255,6 +305,7 @@ namespace FlaxEditor.Windows.Assets // Init asset properties and parameters proxy _properties.OnLoad(this); _previewTab.Presenter.BuildLayoutOnUpdate(); + _layoutTab.Presenter.BuildLayoutOnUpdate(); return false; } diff --git a/Source/Engine/Particles/ParticleEmitter.cpp b/Source/Engine/Particles/ParticleEmitter.cpp index 558c0172f..08344e167 100644 --- a/Source/Engine/Particles/ParticleEmitter.cpp +++ b/Source/Engine/Particles/ParticleEmitter.cpp @@ -519,4 +519,39 @@ bool ParticleEmitter::HasShaderCode() const return false; } +Array ParticleEmitter::GetLayout() const +{ + Array 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 diff --git a/Source/Engine/Particles/ParticleEmitter.h b/Source/Engine/Particles/ParticleEmitter.h index 1398de5db..23ee83e21 100644 --- a/Source/Engine/Particles/ParticleEmitter.h +++ b/Source/Engine/Particles/ParticleEmitter.h @@ -177,10 +177,16 @@ public: void GetReferences(Array& assets, Array& files) const override; bool Save(const StringView& path = StringView::Empty) override; - /// - /// Checks if the particle emitter has valid shader code present. - /// - API_PROPERTY() bool HasShaderCode() const; + API_STRUCT(Internal) struct Attribute + { + DECLARE_SCRIPTING_TYPE_MINIMAL(Attribute); + API_FIELD() PixelFormat Format; + API_FIELD() String Name; + }; + +private: + API_PROPERTY(Internal) bool HasShaderCode() const; + API_PROPERTY(Internal) Array GetLayout() const; #endif protected: diff --git a/Source/Engine/Particles/ParticlesData.cpp b/Source/Engine/Particles/ParticlesData.cpp index dcdef46d7..958ada7cf 100644 --- a/Source/Engine/Particles/ParticlesData.cpp +++ b/Source/Engine/Particles/ParticlesData.cpp @@ -6,6 +6,89 @@ #include "Engine/Graphics/GPUDevice.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() { } diff --git a/Source/Engine/Particles/ParticlesData.h b/Source/Engine/Particles/ParticlesData.h index 2d81d70f0..53f63826f 100644 --- a/Source/Engine/Particles/ParticlesData.h +++ b/Source/Engine/Particles/ParticlesData.h @@ -52,25 +52,7 @@ struct ParticleAttribute /// /// Gets the size of the attribute (in bytes). /// - /// The size (in bytes). - 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; - } - } + int32 GetSize() const; }; /// @@ -93,41 +75,19 @@ public: /// /// Clears the layout data. /// - void Clear() - { - Size = 0; - Attributes.Clear(); - } + void Clear(); /// /// Updates the attributes layout (calculates offset) and updates the total size of the layout. /// - void UpdateLayout() - { - Size = 0; - for (int32 i = 0; i < Attributes.Count(); i++) - { - Attributes[i].Offset = Size; - Size += Attributes[i].GetSize(); - } - } + void UpdateLayout(); /// /// Finds the attribute by the name. /// /// The name. /// The attribute index or -1 if cannot find it. - int32 FindAttribute(const StringView& name) const - { - for (int32 i = 0; i < Attributes.Count(); i++) - { - if (name == Attributes[i].Name) - { - return i; - } - } - return -1; - } + int32 FindAttribute(const StringView& name) const; /// /// Finds the attribute by the name and type. @@ -135,17 +95,7 @@ public: /// The name. /// The type. /// The attribute index or -1 if cannot find it. - 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; - } + int32 FindAttribute(const StringView& name, ParticleAttribute::ValueTypes valueType) const; /// /// Finds the attribute offset by the name. @@ -153,17 +103,7 @@ public: /// The name. /// The fallback value to return if attribute is missing. /// The attribute offset or fallback value if cannot find it. - 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; - } + int32 FindAttributeOffset(const StringView& name, int32 fallbackValue = 0) const; /// /// Finds the attribute offset by the name. @@ -172,17 +112,7 @@ public: /// The type. /// The fallback value to return if attribute is missing. /// The attribute offset or fallback value if cannot find it. - 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; - } + int32 FindAttributeOffset(const StringView& name, ParticleAttribute::ValueTypes valueType, int32 fallbackValue = 0) const; /// /// Gets the attribute offset by the attribute index. @@ -201,13 +131,7 @@ public: /// The name. /// The value type. /// The attribute index or -1 if cannot find it. - 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; - } + int32 AddAttribute(const StringView& name, ParticleAttribute::ValueTypes valueType); }; ///