// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved. using System; using System.Collections.Generic; using System.Linq; using FlaxEditor.Content; using FlaxEditor.CustomEditors; using FlaxEditor.Scripting; using FlaxEditor.Surface; using FlaxEditor.Viewport.Previews; using FlaxEngine; using FlaxEngine.Windows.Search; // ReSharper disable UnusedMember.Local // ReSharper disable UnusedMember.Global // ReSharper disable MemberCanBePrivate.Local namespace FlaxEditor.Windows.Assets { /// /// Particle Emitter window allows to view and edit asset. /// /// /// /// public sealed class ParticleEmitterWindow : VisjectSurfaceWindow, ISearchWindow { private readonly ScriptType[] _newParameterTypes = { new ScriptType(typeof(float)), new ScriptType(typeof(Texture)), new ScriptType(typeof(CubeTexture)), new ScriptType(typeof(GPUTexture)), new ScriptType(typeof(ChannelMask)), new ScriptType(typeof(bool)), new ScriptType(typeof(int)), new ScriptType(typeof(Float2)), new ScriptType(typeof(Float3)), new ScriptType(typeof(Float4)), new ScriptType(typeof(Vector2)), new ScriptType(typeof(Vector3)), new ScriptType(typeof(Vector4)), new ScriptType(typeof(Color)), new ScriptType(typeof(Quaternion)), new ScriptType(typeof(Transform)), new ScriptType(typeof(Matrix)), }; /// /// The properties proxy object. /// private sealed class PropertiesProxy { [EditorOrder(1000), EditorDisplay("Parameters"), CustomEditor(typeof(ParametersEditor)), NoSerialize] // ReSharper disable once UnusedAutoPropertyAccessor.Local public ParticleEmitterWindow Window { get; set; } [HideInEditor, Serialize] // ReSharper disable once UnusedMember.Local public List Parameters { get => Window.Surface.Parameters; set => throw new Exception("No setter."); } /// /// Gathers parameters from the specified ParticleEmitter. /// /// The ParticleEmitter window. public void OnLoad(ParticleEmitterWindow particleEmitterWin) { // Link Window = particleEmitterWin; } /// /// Clears temporary data. /// public void OnClean() { // Unlink Window = null; } } /// /// The graph parameters preview proxy object. /// private sealed class PreviewProxy { [EditorDisplay("Parameters"), 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 parameters = window.Preview.PreviewActor.Parameters; var data = SurfaceUtils.InitGraphParameters(parameters); SurfaceUtils.DisplayGraphParameters(layout, data, (instance, parameter, tag) => ((ParticleEmitterWindow)instance).Preview.PreviewActor.GetParameterValue(string.Empty, parameter.Name), (instance, value, parameter, tag) => ((ParticleEmitterWindow)instance).Preview.PreviewActor.SetParameterValue(string.Empty, parameter.Name, value), Values); if (!parameters.Any()) layout.Label("No parameters", TextAlignment.Center); } } } private readonly PropertiesProxy _properties; private Tab _previewTab; /// public ParticleEmitterWindow(Editor editor, AssetItem item) : base(editor, item, true) { // Asset preview _preview = new ParticleEmitterPreview(true) { PlaySimulation = true, Parent = _split2.Panel1 }; // Asset properties proxy _properties = new PropertiesProxy(); // Preview properties editor _previewTab = new Tab("Preview"); _previewTab.Presenter.Select(new PreviewProxy { Window = this, }); _tabs.AddTab(_previewTab); // Surface _surface = new ParticleEmitterSurface(this, Save, _undo) { Parent = _split1.Panel1, Enabled = false }; // Toolstrip SurfaceUtils.PerformCommonSetup(this, _toolstrip, _surface, out _saveButton, out _undoButton, out _redoButton); _toolstrip.AddButton(editor.Icons.Code64, ShowSourceCode).LinkTooltip("Show generated shader source code"); _toolstrip.AddSeparator(); _toolstrip.AddButton(editor.Icons.Docs64, () => Platform.OpenUrl(Utilities.Constants.DocsUrl + "manual/particles/index.html")).LinkTooltip("See documentation to learn more"); } private void ShowSourceCode() { var source = Editor.GetShaderSourceCode(_asset); Utilities.Utils.ShowSourceCodeWindow(source, "Particle Emitter GPU Simulation Source", RootWindow.Window); } /// public override void OnParamRenameUndo() { base.OnParamRenameUndo(); _refreshPropertiesOnLoad = true; } /// public override void OnParamAddUndo() { base.OnParamAddUndo(); _refreshPropertiesOnLoad = true; } /// public override void OnParamRemoveUndo() { base.OnParamRemoveUndo(); _refreshPropertiesOnLoad = true; } /// public override IEnumerable NewParameterTypes => _newParameterTypes; /// public override void SetParameter(int index, object value) { try { Preview.PreviewActor.Parameters[index].Value = value; } catch { // Ignored } base.SetParameter(index, value); } /// protected override void UnlinkItem() { _properties.OnClean(); _preview.Emitter = null; base.UnlinkItem(); } /// protected override void OnAssetLinked() { _preview.Emitter = _asset; base.OnAssetLinked(); } /// public override string SurfaceName => "Particle Emitter"; /// public override byte[] SurfaceData { get => _asset.LoadSurface(true); set { if (_asset.SaveSurface(value)) { _surface.MarkAsEdited(); Editor.LogError("Failed to save surface data"); } _asset.Reload(); _asset.WaitForLoaded(); _preview.PreviewActor.ResetSimulation(); _previewTab.Presenter.BuildLayoutOnUpdate(); } } /// protected override bool LoadSurface() { // Load surface graph if (_surface.Load()) { Editor.LogError("Failed to load Particle Emitter surface."); return true; } // Init asset properties and parameters proxy _properties.OnLoad(this); _previewTab.Presenter.BuildLayoutOnUpdate(); return false; } /// protected override bool SaveSurface() { _surface.Save(); return false; } /// protected override void OnSurfaceEditingStart() { _propertiesEditor.Select(_properties); base.OnSurfaceEditingStart(); } /// protected override bool CanEditSurfaceOnAssetLoadError => true; /// protected override bool SaveToOriginal() { // Copy shader cache from the temporary Particle Emitter (will skip compilation on Reload - faster) Guid dstId = _item.ID; Guid srcId = _asset.ID; Editor.Internal_CopyCache(ref dstId, ref srcId); return base.SaveToOriginal(); } /// public SearchAssetTypes AssetType => SearchAssetTypes.ParticleEmitter; } }