diff --git a/Content/Editor/Scripting/ScriptTemplate.cs b/Content/Editor/Scripting/ScriptTemplate.cs
index 2a150306d..663acf05f 100644
--- a/Content/Editor/Scripting/ScriptTemplate.cs
+++ b/Content/Editor/Scripting/ScriptTemplate.cs
@@ -2,35 +2,35 @@
using System.Collections.Generic;
using FlaxEngine;
-namespace %namespace%
+namespace %namespace%;
+
+///
+/// %class% Script.
+///
+public class %class% : Script
{
- ///
- /// %class% Script.
- ///
- public class %class% : Script
+ ///
+ public override void OnStart()
{
- ///
- public override void OnStart()
- {
- // Here you can add code that needs to be called when script is created, just before the first game update
- }
-
- ///
- public override void OnEnable()
- {
- // Here you can add code that needs to be called when script is enabled (eg. register for events)
- }
+ // Here you can add code that needs to be called when script is created, just before the first game update
+ }
+
+ ///
+ public override void OnEnable()
+ {
+ // Here you can add code that needs to be called when script is enabled (eg. register for events)
+ }
- ///
- public override void OnDisable()
- {
- // Here you can add code that needs to be called when script is disabled (eg. unregister from events)
- }
+ ///
+ public override void OnDisable()
+ {
+ // Here you can add code that needs to be called when script is disabled (eg. unregister from events)
+ }
- ///
- public override void OnUpdate()
- {
- // Here you can add code that needs to be called every frame
- }
+ ///
+ public override void OnUpdate()
+ {
+ // Here you can add code that needs to be called every frame
}
}
+
diff --git a/Flax.flaxproj b/Flax.flaxproj
index fbf48cc2b..ebcdc2830 100644
--- a/Flax.flaxproj
+++ b/Flax.flaxproj
@@ -2,8 +2,8 @@
"Name": "Flax",
"Version": {
"Major": 1,
- "Minor": 6,
- "Build": 6345
+ "Minor": 7,
+ "Build": 6401
},
"Company": "Flax",
"Copyright": "Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.",
diff --git a/Flax.sln.DotSettings b/Flax.sln.DotSettings
index 6a22f211b..ff396d824 100644
--- a/Flax.sln.DotSettings
+++ b/Flax.sln.DotSettings
@@ -256,6 +256,8 @@
True
True
True
+ True
+ True
True
True
True
@@ -321,6 +323,7 @@
True
True
True
+ True
True
True
True
diff --git a/Source/Editor/Content/GUI/ContentView.DragDrop.cs b/Source/Editor/Content/GUI/ContentView.DragDrop.cs
index 348e2b443..ffce81e2d 100644
--- a/Source/Editor/Content/GUI/ContentView.DragDrop.cs
+++ b/Source/Editor/Content/GUI/ContentView.DragDrop.cs
@@ -68,7 +68,7 @@ namespace FlaxEditor.Content.GUI
_validDragOver = true;
result = DragDropEffect.Copy;
}
- else if (_dragActors.HasValidDrag)
+ else if (_dragActors != null && _dragActors.HasValidDrag)
{
_validDragOver = true;
result = DragDropEffect.Move;
@@ -94,7 +94,7 @@ namespace FlaxEditor.Content.GUI
result = DragDropEffect.Copy;
}
// Check if drop actor(s)
- else if (_dragActors.HasValidDrag)
+ else if (_dragActors != null && _dragActors.HasValidDrag)
{
// Import actors
var currentFolder = Editor.Instance.Windows.ContentWin.CurrentViewFolder;
diff --git a/Source/Editor/Content/GUI/ContentView.cs b/Source/Editor/Content/GUI/ContentView.cs
index 5e054e5c6..e1a3acdf6 100644
--- a/Source/Editor/Content/GUI/ContentView.cs
+++ b/Source/Editor/Content/GUI/ContentView.cs
@@ -520,8 +520,8 @@ namespace FlaxEditor.Content.GUI
{
int min = _selection.Min(x => x.IndexInParent);
int max = _selection.Max(x => x.IndexInParent);
- min = Mathf.Min(min, item.IndexInParent);
- max = Mathf.Max(max, item.IndexInParent);
+ min = Mathf.Max(Mathf.Min(min, item.IndexInParent), 0);
+ max = Mathf.Min(Mathf.Max(max, item.IndexInParent), _children.Count - 1);
var selection = new List(_selection);
for (int i = min; i <= max; i++)
{
diff --git a/Source/Editor/Content/Import/AudioImportSettings.cs b/Source/Editor/Content/Import/AudioImportSettings.cs
index 2fc0a1795..e0bfb6e20 100644
--- a/Source/Editor/Content/Import/AudioImportSettings.cs
+++ b/Source/Editor/Content/Import/AudioImportSettings.cs
@@ -1,144 +1,52 @@
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
-using System.ComponentModel;
-using System.Reflection;
-using System.Runtime.InteropServices;
+using System.Collections.Generic;
+using FlaxEditor.CustomEditors.Editors;
+using FlaxEditor.Scripting;
using FlaxEngine;
-using FlaxEngine.Interop;
+using FlaxEngine.Tools;
+
+namespace FlaxEngine.Tools
+{
+ partial class AudioTool
+ {
+ partial struct Options
+ {
+ private bool ShowBtiDepth => Format != AudioFormat.Vorbis;
+ }
+ }
+}
+
+namespace FlaxEditor.CustomEditors.Dedicated
+{
+ ///
+ /// Custom editor for .
+ ///
+ [CustomEditor(typeof(FlaxEngine.Tools.AudioTool.Options)), DefaultEditor]
+ public class AudioToolOptionsEditor : GenericEditor
+ {
+ ///
+ protected override List GetItemsForType(ScriptType type)
+ {
+ // Show both fields and properties
+ return GetItemsForType(type, true, true);
+ }
+ }
+}
namespace FlaxEditor.Content.Import
{
///
/// Proxy object to present audio import settings in .
///
+ [HideInEditor]
public class AudioImportSettings
{
///
- /// A custom set of bit depth audio import sizes.
+ /// The settings data.
///
- public enum CustomBitDepth
- {
- ///
- /// The 8.
- ///
- _8 = 8,
-
- ///
- /// The 16.
- ///
- _16 = 16,
-
- ///
- /// The 24.
- ///
- _24 = 24,
-
- ///
- /// The 32.
- ///
- _32 = 32,
- }
-
- ///
- /// Converts the bit depth to enum.
- ///
- /// The bit depth.
- /// The converted enum.
- public static CustomBitDepth ConvertBitDepth(int f)
- {
- FieldInfo[] fields = typeof(CustomBitDepth).GetFields();
- for (int i = 0; i < fields.Length; i++)
- {
- var field = fields[i];
- if (field.Name.Equals("value__"))
- continue;
-
- if (f == (int)field.GetRawConstantValue())
- return (CustomBitDepth)f;
- }
-
- return CustomBitDepth._16;
- }
-
- ///
- /// The audio data format to import the audio clip as.
- ///
- [EditorOrder(10), DefaultValue(AudioFormat.Vorbis), Tooltip("The audio data format to import the audio clip as.")]
- public AudioFormat Format { get; set; } = AudioFormat.Vorbis;
-
- ///
- /// The audio data compression quality. Used only if target format is using compression. Value 0 means the smallest size, value 1 means the best quality.
- ///
- [EditorOrder(15), DefaultValue(0.4f), Limit(0, 1, 0.01f), Tooltip("The audio data compression quality. Used only if target format is using compression. Value 0 means the smallest size, value 1 means the best quality.")]
- public float CompressionQuality { get; set; } = 0.4f;
-
- ///
- /// Disables dynamic audio streaming. The whole clip will be loaded into the memory. Useful for small clips (eg. gunfire sounds).
- ///
- [EditorOrder(20), DefaultValue(false), Tooltip("Disables dynamic audio streaming. The whole clip will be loaded into the memory. Useful for small clips (eg. gunfire sounds).")]
- public bool DisableStreaming { get; set; } = false;
-
- ///
- /// Checks should the clip be played as spatial (3D) audio or as normal audio. 3D audio is stored in Mono format.
- ///
- [EditorOrder(30), DefaultValue(false), EditorDisplay(null, "Is 3D"), Tooltip("Checks should the clip be played as spatial (3D) audio or as normal audio. 3D audio is stored in Mono format.")]
- public bool Is3D { get; set; } = false;
-
- ///
- /// The size of a single sample in bits. The clip will be converted to this bit depth on import.
- ///
- [EditorOrder(40), DefaultValue(CustomBitDepth._16), Tooltip("The size of a single sample in bits. The clip will be converted to this bit depth on import.")]
- public CustomBitDepth BitDepth { get; set; } = CustomBitDepth._16;
-
- [StructLayout(LayoutKind.Sequential)]
- internal struct InternalOptions
- {
- [MarshalAs(UnmanagedType.I1)]
- public AudioFormat Format;
- public byte DisableStreaming;
- public byte Is3D;
- public int BitDepth;
- public float Quality;
- }
-
- internal void ToInternal(out InternalOptions options)
- {
- options = new InternalOptions
- {
- Format = Format,
- DisableStreaming = (byte)(DisableStreaming ? 1 : 0),
- Is3D = (byte)(Is3D ? 1 : 0),
- Quality = CompressionQuality,
- BitDepth = (int)BitDepth,
- };
- }
-
- internal void FromInternal(ref InternalOptions options)
- {
- Format = options.Format;
- DisableStreaming = options.DisableStreaming != 0;
- Is3D = options.Is3D != 0;
- CompressionQuality = options.Quality;
- BitDepth = ConvertBitDepth(options.BitDepth);
- }
-
- ///
- /// Tries the restore the asset import options from the target resource file.
- ///
- /// The options.
- /// The asset path.
- /// True settings has been restored, otherwise false.
- public static bool TryRestore(ref AudioImportSettings options, string assetPath)
- {
- if (AudioImportEntry.Internal_GetAudioImportOptions(assetPath, out var internalOptions))
- {
- // Restore settings
- options.FromInternal(ref internalOptions);
- return true;
- }
-
- return false;
- }
+ [EditorDisplay(null, EditorDisplayAttribute.InlineStyle)]
+ public AudioTool.Options Settings = AudioTool.Options.Default;
}
///
@@ -147,7 +55,7 @@ namespace FlaxEditor.Content.Import
///
public partial class AudioImportEntry : AssetImportEntry
{
- private AudioImportSettings _settings = new AudioImportSettings();
+ private AudioImportSettings _settings = new();
///
/// Initializes a new instance of the class.
@@ -157,7 +65,7 @@ namespace FlaxEditor.Content.Import
: base(ref request)
{
// Try to restore target asset Audio import options (useful for fast reimport)
- AudioImportSettings.TryRestore(ref _settings, ResultUrl);
+ Editor.TryRestoreImportOptions(ref _settings.Settings, ResultUrl);
}
///
@@ -166,27 +74,23 @@ namespace FlaxEditor.Content.Import
///
public override bool TryOverrideSettings(object settings)
{
- if (settings is AudioImportSettings o)
+ if (settings is AudioImportSettings s)
{
- _settings = o;
+ _settings.Settings = s.Settings;
+ return true;
+ }
+ if (settings is AudioTool.Options o)
+ {
+ _settings.Settings = o;
return true;
}
-
return false;
}
///
public override bool Import()
{
- return Editor.Import(SourceUrl, ResultUrl, _settings);
+ return Editor.Import(SourceUrl, ResultUrl, _settings.Settings);
}
-
- #region Internal Calls
-
- [LibraryImport("FlaxEngine", EntryPoint = "AudioImportEntryInternal_GetAudioImportOptions", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(StringMarshaller))]
- [return: MarshalAs(UnmanagedType.U1)]
- internal static partial bool Internal_GetAudioImportOptions(string path, out AudioImportSettings.InternalOptions result);
-
- #endregion
}
}
diff --git a/Source/Editor/Content/Items/ContentItem.cs b/Source/Editor/Content/Items/ContentItem.cs
index 94db3a5b9..6bbdc0a51 100644
--- a/Source/Editor/Content/Items/ContentItem.cs
+++ b/Source/Editor/Content/Items/ContentItem.cs
@@ -486,7 +486,7 @@ namespace FlaxEditor.Content
else
Render2D.FillRectangle(rectangle, Color.Black);
}
-
+
///
/// Draws the item thumbnail.
///
@@ -684,7 +684,7 @@ namespace FlaxEditor.Content
var thumbnailSize = size.X;
thumbnailRect = new Rectangle(0, 0, thumbnailSize, thumbnailSize);
nameAlignment = TextAlignment.Center;
-
+
if (this is ContentFolder)
{
// Small shadow
@@ -692,7 +692,7 @@ namespace FlaxEditor.Content
var color = Color.Black.AlphaMultiplied(0.2f);
Render2D.FillRectangle(shadowRect, color);
Render2D.FillRectangle(clientRect, style.Background.RGBMultiplied(1.25f));
-
+
if (isSelected)
Render2D.FillRectangle(clientRect, Parent.ContainsFocus ? style.BackgroundSelected : style.LightBackground);
else if (IsMouseOver)
@@ -706,14 +706,14 @@ namespace FlaxEditor.Content
var shadowRect = new Rectangle(2, 2, clientRect.Width + 1, clientRect.Height + 1);
var color = Color.Black.AlphaMultiplied(0.2f);
Render2D.FillRectangle(shadowRect, color);
-
+
Render2D.FillRectangle(clientRect, style.Background.RGBMultiplied(1.25f));
Render2D.FillRectangle(TextRectangle, style.LightBackground);
-
+
var accentHeight = 2 * view.ViewScale;
var barRect = new Rectangle(0, thumbnailRect.Height - accentHeight, clientRect.Width, accentHeight);
Render2D.FillRectangle(barRect, Color.DimGray);
-
+
DrawThumbnail(ref thumbnailRect, false);
if (isSelected)
{
@@ -733,18 +733,18 @@ namespace FlaxEditor.Content
var thumbnailSize = size.Y - 2 * DefaultMarginSize;
thumbnailRect = new Rectangle(DefaultMarginSize, DefaultMarginSize, thumbnailSize, thumbnailSize);
nameAlignment = TextAlignment.Near;
-
+
if (isSelected)
Render2D.FillRectangle(clientRect, Parent.ContainsFocus ? style.BackgroundSelected : style.LightBackground);
else if (IsMouseOver)
Render2D.FillRectangle(clientRect, style.BackgroundHighlighted);
-
+
DrawThumbnail(ref thumbnailRect);
break;
}
default: throw new ArgumentOutOfRangeException();
}
-
+
// Draw short name
Render2D.PushClip(ref textRect);
Render2D.DrawText(style.FontMedium, ShowFileExtension || view.ShowFileExtensions ? FileName : ShortName, textRect, style.Foreground, nameAlignment, TextAlignment.Center, TextWrapping.WrapWords, 1f, 0.95f);
diff --git a/Source/Editor/Content/Proxy/BehaviorTreeProxy.cs b/Source/Editor/Content/Proxy/BehaviorTreeProxy.cs
new file mode 100644
index 000000000..33ad0862f
--- /dev/null
+++ b/Source/Editor/Content/Proxy/BehaviorTreeProxy.cs
@@ -0,0 +1,66 @@
+// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
+
+using System;
+using System.IO;
+using FlaxEditor.Content.Thumbnails;
+using FlaxEditor.Windows;
+using FlaxEditor.Windows.Assets;
+using FlaxEngine;
+using FlaxEngine.GUI;
+
+namespace FlaxEditor.Content
+{
+ ///
+ /// A asset proxy object.
+ ///
+ ///
+ [ContentContextMenu("New/AI/Behavior Tree")]
+ public class BehaviorTreeProxy : BinaryAssetProxy
+ {
+ ///
+ public override string Name => "Behavior Tree";
+
+ ///
+ public override bool CanReimport(ContentItem item)
+ {
+ return true;
+ }
+
+ ///
+ public override EditorWindow Open(Editor editor, ContentItem item)
+ {
+ return new BehaviorTreeWindow(editor, item as BinaryAssetItem);
+ }
+
+ ///
+ public override Color AccentColor => Color.FromRGB(0x3256A8);
+
+ ///
+ public override Type AssetType => typeof(BehaviorTree);
+
+ ///
+ public override bool CanCreate(ContentFolder targetLocation)
+ {
+ return targetLocation.CanHaveAssets;
+ }
+
+ ///
+ public override void Create(string outputPath, object arg)
+ {
+ if (Editor.CreateAsset(Editor.NewAssetType.BehaviorTree, outputPath))
+ throw new Exception("Failed to create new asset.");
+ }
+
+ ///
+ public override void OnThumbnailDrawBegin(ThumbnailRequest request, ContainerControl guiRoot, GPUContext context)
+ {
+ guiRoot.AddChild(new Label
+ {
+ Text = Path.GetFileNameWithoutExtension(request.Asset.Path),
+ Offsets = Margin.Zero,
+ AnchorPreset = AnchorPresets.StretchAll,
+ Wrapping = TextWrapping.WrapWords
+ });
+ }
+ }
+}
diff --git a/Source/Editor/Content/Proxy/CubeTextureProxy.cs b/Source/Editor/Content/Proxy/CubeTextureProxy.cs
index 691e4ac53..2dccc9e47 100644
--- a/Source/Editor/Content/Proxy/CubeTextureProxy.cs
+++ b/Source/Editor/Content/Proxy/CubeTextureProxy.cs
@@ -54,12 +54,7 @@ namespace FlaxEditor.Content
///
public override bool CanDrawThumbnail(ThumbnailRequest request)
{
- if (!_preview.HasLoadedAssets)
- return false;
-
- // Check if all mip maps are streamed
- var asset = (CubeTexture)request.Asset;
- return asset.ResidentMipLevels >= Mathf.Max(1, (int)(asset.MipLevels * ThumbnailsModule.MinimumRequiredResourcesQuality));
+ return _preview.HasLoadedAssets && ThumbnailsModule.HasMinimumQuality((CubeTexture)request.Asset);
}
///
diff --git a/Source/Editor/Content/Proxy/MaterialInstanceProxy.cs b/Source/Editor/Content/Proxy/MaterialInstanceProxy.cs
index dcc3e3ec4..331ff81c3 100644
--- a/Source/Editor/Content/Proxy/MaterialInstanceProxy.cs
+++ b/Source/Editor/Content/Proxy/MaterialInstanceProxy.cs
@@ -62,7 +62,7 @@ namespace FlaxEditor.Content
///
public override bool CanDrawThumbnail(ThumbnailRequest request)
{
- return _preview.HasLoadedAssets;
+ return _preview.HasLoadedAssets && ThumbnailsModule.HasMinimumQuality((MaterialInstance)request.Asset);
}
///
diff --git a/Source/Editor/Content/Proxy/MaterialProxy.cs b/Source/Editor/Content/Proxy/MaterialProxy.cs
index cf7c8b77e..a7fcfecc8 100644
--- a/Source/Editor/Content/Proxy/MaterialProxy.cs
+++ b/Source/Editor/Content/Proxy/MaterialProxy.cs
@@ -106,7 +106,7 @@ namespace FlaxEditor.Content
///
public override bool CanDrawThumbnail(ThumbnailRequest request)
{
- return _preview.HasLoadedAssets;
+ return _preview.HasLoadedAssets && ThumbnailsModule.HasMinimumQuality((Material)request.Asset);
}
///
diff --git a/Source/Editor/Content/Proxy/ModelProxy.cs b/Source/Editor/Content/Proxy/ModelProxy.cs
index 845cbc80b..0cf16850d 100644
--- a/Source/Editor/Content/Proxy/ModelProxy.cs
+++ b/Source/Editor/Content/Proxy/ModelProxy.cs
@@ -82,12 +82,7 @@ namespace FlaxEditor.Content
///
public override bool CanDrawThumbnail(ThumbnailRequest request)
{
- if (!_preview.HasLoadedAssets)
- return false;
-
- // Check if asset is streamed enough
- var asset = (Model)request.Asset;
- return asset.LoadedLODs >= Mathf.Max(1, (int)(asset.LODs.Length * ThumbnailsModule.MinimumRequiredResourcesQuality));
+ return _preview.HasLoadedAssets && ThumbnailsModule.HasMinimumQuality((Model)request.Asset);
}
///
diff --git a/Source/Editor/Content/Proxy/SkinnedModelProxy.cs b/Source/Editor/Content/Proxy/SkinnedModelProxy.cs
index f0193d0d0..6e228aa86 100644
--- a/Source/Editor/Content/Proxy/SkinnedModelProxy.cs
+++ b/Source/Editor/Content/Proxy/SkinnedModelProxy.cs
@@ -54,15 +54,7 @@ namespace FlaxEditor.Content
///
public override bool CanDrawThumbnail(ThumbnailRequest request)
{
- if (!_preview.HasLoadedAssets)
- return false;
-
- // Check if asset is streamed enough
- var asset = (SkinnedModel)request.Asset;
- var lods = asset.LODs.Length;
- if (asset.IsLoaded && lods == 0)
- return true; // Skeleton-only model
- return asset.LoadedLODs >= Mathf.Max(1, (int)(lods * ThumbnailsModule.MinimumRequiredResourcesQuality));
+ return _preview.HasLoadedAssets && ThumbnailsModule.HasMinimumQuality((SkinnedModel)request.Asset);
}
///
diff --git a/Source/Editor/Content/Proxy/TextureProxy.cs b/Source/Editor/Content/Proxy/TextureProxy.cs
index 48bfcc9e9..709ca4dbf 100644
--- a/Source/Editor/Content/Proxy/TextureProxy.cs
+++ b/Source/Editor/Content/Proxy/TextureProxy.cs
@@ -57,11 +57,7 @@ namespace FlaxEditor.Content
///
public override bool CanDrawThumbnail(ThumbnailRequest request)
{
- // Check if asset is streamed enough
- var asset = (Texture)request.Asset;
- var mipLevels = asset.MipLevels;
- var minMipLevels = Mathf.Min(mipLevels, 7);
- return asset.ResidentMipLevels >= Mathf.Max(minMipLevels, (int)(mipLevels * ThumbnailsModule.MinimumRequiredResourcesQuality));
+ return ThumbnailsModule.HasMinimumQuality((Texture)request.Asset);
}
///
diff --git a/Source/Editor/Content/Thumbnails/ThumbnailsModule.cs b/Source/Editor/Content/Thumbnails/ThumbnailsModule.cs
index a6996310e..160c783ed 100644
--- a/Source/Editor/Content/Thumbnails/ThumbnailsModule.cs
+++ b/Source/Editor/Content/Thumbnails/ThumbnailsModule.cs
@@ -125,6 +125,74 @@ namespace FlaxEditor.Content.Thumbnails
}
}
+ internal static bool HasMinimumQuality(TextureBase asset)
+ {
+ var mipLevels = asset.MipLevels;
+ var minMipLevels = Mathf.Min(mipLevels, 7);
+ return asset.IsLoaded && asset.ResidentMipLevels >= Mathf.Max(minMipLevels, (int)(mipLevels * MinimumRequiredResourcesQuality));
+ }
+
+ internal static bool HasMinimumQuality(Model asset)
+ {
+ if (!asset.IsLoaded)
+ return false;
+ var lods = asset.LODs.Length;
+ var slots = asset.MaterialSlots;
+ foreach (var slot in slots)
+ {
+ if (slot.Material && !HasMinimumQuality(slot.Material))
+ return false;
+ }
+ return asset.LoadedLODs >= Mathf.Max(1, (int)(lods * MinimumRequiredResourcesQuality));
+ }
+
+ internal static bool HasMinimumQuality(SkinnedModel asset)
+ {
+ var lods = asset.LODs.Length;
+ if (asset.IsLoaded && lods == 0)
+ return true; // Skeleton-only model
+ var slots = asset.MaterialSlots;
+ foreach (var slot in slots)
+ {
+ if (slot.Material && !HasMinimumQuality(slot.Material))
+ return false;
+ }
+ return asset.LoadedLODs >= Mathf.Max(1, (int)(lods * MinimumRequiredResourcesQuality));
+ }
+
+ internal static bool HasMinimumQuality(MaterialBase asset)
+ {
+ if (asset is MaterialInstance asInstance)
+ return HasMinimumQuality(asInstance);
+ return HasMinimumQualityInternal(asset);
+ }
+
+ internal static bool HasMinimumQuality(Material asset)
+ {
+ return HasMinimumQualityInternal(asset);
+ }
+
+ internal static bool HasMinimumQuality(MaterialInstance asset)
+ {
+ if (!HasMinimumQualityInternal(asset))
+ return false;
+ var baseMaterial = asset.BaseMaterial;
+ return baseMaterial == null || HasMinimumQualityInternal(baseMaterial);
+ }
+
+ private static bool HasMinimumQualityInternal(MaterialBase asset)
+ {
+ if (!asset.IsLoaded)
+ return false;
+ var parameters = asset.Parameters;
+ foreach (var parameter in parameters)
+ {
+ if (parameter.Value is TextureBase asTexture && !HasMinimumQuality(asTexture))
+ return false;
+ }
+ return true;
+ }
+
#region IContentItemOwner
///
@@ -368,7 +436,6 @@ namespace FlaxEditor.Content.Thumbnails
// Create atlas
if (PreviewsCache.Create(path))
{
- // Error
Editor.LogError("Failed to create thumbnails atlas.");
return null;
}
@@ -377,7 +444,6 @@ namespace FlaxEditor.Content.Thumbnails
var atlas = FlaxEngine.Content.LoadAsync(path);
if (atlas == null)
{
- // Error
Editor.LogError("Failed to load thumbnails atlas.");
return null;
}
diff --git a/Source/Editor/Cooker/Platform/Mac/MacPlatformTools.cpp b/Source/Editor/Cooker/Platform/Mac/MacPlatformTools.cpp
index b66a96a9b..a1db61dbb 100644
--- a/Source/Editor/Cooker/Platform/Mac/MacPlatformTools.cpp
+++ b/Source/Editor/Cooker/Platform/Mac/MacPlatformTools.cpp
@@ -17,7 +17,7 @@
#include "Editor/ProjectInfo.h"
#include "Editor/Cooker/GameCooker.h"
#include "Editor/Utilities/EditorUtilities.h"
-#include
+#include
using namespace pugi;
IMPLEMENT_SETTINGS_GETTER(MacPlatformSettings, MacPlatform);
@@ -170,17 +170,17 @@ bool MacPlatformTools::OnPostProcess(CookingData& data)
const String plistPath = data.DataOutputPath / TEXT("Info.plist");
{
xml_document doc;
- xml_node plist = doc.child_or_append(PUGIXML_TEXT("plist"));
+ xml_node_extra plist = xml_node_extra(doc).child_or_append(PUGIXML_TEXT("plist"));
plist.append_attribute(PUGIXML_TEXT("version")).set_value(PUGIXML_TEXT("1.0"));
- xml_node dict = plist.child_or_append(PUGIXML_TEXT("dict"));
+ xml_node_extra dict = plist.child_or_append(PUGIXML_TEXT("dict"));
#define ADD_ENTRY(key, value) \
- dict.append_child(PUGIXML_TEXT("key")).set_child_value(PUGIXML_TEXT(key)); \
- dict.append_child(PUGIXML_TEXT("string")).set_child_value(PUGIXML_TEXT(value))
+ dict.append_child_with_value(PUGIXML_TEXT("key"), PUGIXML_TEXT(key)); \
+ dict.append_child_with_value(PUGIXML_TEXT("string"), PUGIXML_TEXT(value))
#define ADD_ENTRY_STR(key, value) \
- dict.append_child(PUGIXML_TEXT("key")).set_child_value(PUGIXML_TEXT(key)); \
+ dict.append_child_with_value(PUGIXML_TEXT("key"), PUGIXML_TEXT(key)); \
{ std::u16string valueStr(value.GetText()); \
- dict.append_child(PUGIXML_TEXT("string")).set_child_value(pugi::string_t(valueStr.begin(), valueStr.end()).c_str()); }
+ dict.append_child_with_value(PUGIXML_TEXT("string"), pugi::string_t(valueStr.begin(), valueStr.end()).c_str()); }
ADD_ENTRY("CFBundleDevelopmentRegion", "English");
ADD_ENTRY("CFBundlePackageType", "APPL");
@@ -194,22 +194,22 @@ bool MacPlatformTools::OnPostProcess(CookingData& data)
ADD_ENTRY_STR("CFBundleVersion", projectVersion);
ADD_ENTRY_STR("NSHumanReadableCopyright", gameSettings->CopyrightNotice);
- dict.append_child(PUGIXML_TEXT("key")).set_child_value(PUGIXML_TEXT("CFBundleSupportedPlatforms"));
- xml_node CFBundleSupportedPlatforms = dict.append_child(PUGIXML_TEXT("array"));
- CFBundleSupportedPlatforms.append_child(PUGIXML_TEXT("string")).set_child_value(PUGIXML_TEXT("MacOSX"));
+ dict.append_child_with_value(PUGIXML_TEXT("key"), PUGIXML_TEXT("CFBundleSupportedPlatforms"));
+ xml_node_extra CFBundleSupportedPlatforms = dict.append_child(PUGIXML_TEXT("array"));
+ CFBundleSupportedPlatforms.append_child_with_value(PUGIXML_TEXT("string"), PUGIXML_TEXT("MacOSX"));
- dict.append_child(PUGIXML_TEXT("key")).set_child_value(PUGIXML_TEXT("LSMinimumSystemVersionByArchitecture"));
- xml_node LSMinimumSystemVersionByArchitecture = dict.append_child(PUGIXML_TEXT("dict"));
+ dict.append_child_with_value(PUGIXML_TEXT("key"), PUGIXML_TEXT("LSMinimumSystemVersionByArchitecture"));
+ xml_node_extra LSMinimumSystemVersionByArchitecture = dict.append_child(PUGIXML_TEXT("dict"));
switch (_arch)
{
case ArchitectureType::x64:
- LSMinimumSystemVersionByArchitecture.append_child(PUGIXML_TEXT("key")).set_child_value(PUGIXML_TEXT("x86_64"));
+ LSMinimumSystemVersionByArchitecture.append_child_with_value(PUGIXML_TEXT("key"), PUGIXML_TEXT("x86_64"));
break;
case ArchitectureType::ARM64:
- LSMinimumSystemVersionByArchitecture.append_child(PUGIXML_TEXT("key")).set_child_value(PUGIXML_TEXT("arm64"));
+ LSMinimumSystemVersionByArchitecture.append_child_with_value(PUGIXML_TEXT("key"), PUGIXML_TEXT("arm64"));
break;
}
- LSMinimumSystemVersionByArchitecture.append_child(PUGIXML_TEXT("string")).set_child_value(PUGIXML_TEXT("10.15"));
+ LSMinimumSystemVersionByArchitecture.append_child_with_value(PUGIXML_TEXT("string"), PUGIXML_TEXT("10.15"));
#undef ADD_ENTRY
#undef ADD_ENTRY_STR
diff --git a/Source/Editor/CustomEditors/CustomEditorPresenter.cs b/Source/Editor/CustomEditors/CustomEditorPresenter.cs
index 73e2ede0f..0c4ac4106 100644
--- a/Source/Editor/CustomEditors/CustomEditorPresenter.cs
+++ b/Source/Editor/CustomEditors/CustomEditorPresenter.cs
@@ -37,6 +37,23 @@ namespace FlaxEditor.CustomEditors
UseDefault = 1 << 2,
}
+ ///
+ /// The interface for Editor context that owns the presenter. Can be or or other window/panel - custom editor scan use it for more specific features.
+ ///
+ public interface IPresenterOwner
+ {
+ ///
+ /// Gets the viewport linked with properties presenter (optional, null if unused).
+ ///
+ public Viewport.EditorViewport PresenterViewport { get; }
+
+ ///
+ /// Selects the scene objects.
+ ///
+ /// The nodes to select
+ public void Select(List nodes);
+ }
+
///
/// Main class for Custom Editors used to present selected objects properties and allow to modify them.
///
@@ -68,8 +85,15 @@ namespace FlaxEditor.CustomEditors
///
public override void Update(float deltaTime)
{
- // Update editors
- _presenter.Update();
+ try
+ {
+ // Update editors
+ _presenter.Update();
+ }
+ catch (Exception ex)
+ {
+ FlaxEditor.Editor.LogWarning(ex);
+ }
base.Update(deltaTime);
}
@@ -254,7 +278,7 @@ namespace FlaxEditor.CustomEditors
///
/// The Editor context that owns this presenter. Can be or or other window/panel - custom editor scan use it for more specific features.
///
- public object Owner;
+ public IPresenterOwner Owner;
///
/// Gets or sets the text to show when no object is selected.
@@ -270,7 +294,24 @@ namespace FlaxEditor.CustomEditors
}
}
+ ///
+ /// Gets or sets the value indicating whether properties are read-only.
+ ///
+ public bool ReadOnly
+ {
+ get => _readOnly;
+ set
+ {
+ if (_readOnly != value)
+ {
+ _readOnly = value;
+ UpdateReadOnly();
+ }
+ }
+ }
+
private bool _buildOnUpdate;
+ private bool _readOnly;
///
/// Initializes a new instance of the class.
@@ -278,7 +319,7 @@ namespace FlaxEditor.CustomEditors
/// The undo. It's optional.
/// The custom text to display when no object is selected. Default is No selection.
/// The owner of the presenter.
- public CustomEditorPresenter(Undo undo, string noSelectionText = null, object owner = null)
+ public CustomEditorPresenter(Undo undo, string noSelectionText = null, IPresenterOwner owner = null)
{
Undo = undo;
Owner = owner;
@@ -364,6 +405,8 @@ namespace FlaxEditor.CustomEditors
// Restore scroll value
if (parentScrollV > -1)
panel.VScrollBar.Value = parentScrollV;
+ if (_readOnly)
+ UpdateReadOnly();
}
///
@@ -374,6 +417,16 @@ namespace FlaxEditor.CustomEditors
_buildOnUpdate = true;
}
+ private void UpdateReadOnly()
+ {
+ // Only scrollbars are enabled
+ foreach (var child in Panel.Children)
+ {
+ if (!(child is ScrollBar))
+ child.Enabled = !_readOnly;
+ }
+ }
+
private void ExpandGroups(LayoutElementsContainer c, bool open)
{
if (c is Elements.GroupElement group)
diff --git a/Source/Editor/CustomEditors/Dedicated/ClothEditor.cs b/Source/Editor/CustomEditors/Dedicated/ClothEditor.cs
new file mode 100644
index 000000000..c6043e8b6
--- /dev/null
+++ b/Source/Editor/CustomEditors/Dedicated/ClothEditor.cs
@@ -0,0 +1,97 @@
+// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
+
+using FlaxEditor.Gizmo;
+using FlaxEditor.Scripting;
+using FlaxEngine;
+using FlaxEngine.GUI;
+using FlaxEngine.Json;
+using FlaxEngine.Tools;
+
+namespace FlaxEditor.CustomEditors.Dedicated
+{
+ ///
+ /// Custom editor for .
+ ///
+ ///
+ [CustomEditor(typeof(Cloth)), DefaultEditor]
+ class ClothEditor : ActorEditor
+ {
+ private ClothPaintingGizmoMode _gizmoMode;
+ private Viewport.Modes.EditorGizmoMode _prevMode;
+
+ ///
+ public override void Initialize(LayoutElementsContainer layout)
+ {
+ base.Initialize(layout);
+
+ if (Values.Count != 1)
+ return;
+
+ // Add gizmo painting mode to the viewport
+ var owner = Presenter.Owner;
+ if (owner == null)
+ return;
+ var gizmoOwner = owner as IGizmoOwner ?? owner.PresenterViewport as IGizmoOwner;
+ if (gizmoOwner == null)
+ return;
+ var gizmos = gizmoOwner.Gizmos;
+ _gizmoMode = new ClothPaintingGizmoMode();
+
+ var projectCache = Editor.Instance.ProjectCache;
+ if (projectCache.TryGetCustomData("ClothGizmoPaintValue", out var cachedPaintValue))
+ _gizmoMode.PaintValue = JsonSerializer.Deserialize(cachedPaintValue);
+ if (projectCache.TryGetCustomData("ClothGizmoContinuousPaint", out var cachedContinuousPaint))
+ _gizmoMode.ContinuousPaint = JsonSerializer.Deserialize(cachedContinuousPaint);
+ if (projectCache.TryGetCustomData("ClothGizmoBrushFalloff", out var cachedBrushFalloff))
+ _gizmoMode.BrushFalloff = JsonSerializer.Deserialize(cachedBrushFalloff);
+ if (projectCache.TryGetCustomData("ClothGizmoBrushSize", out var cachedBrushSize))
+ _gizmoMode.BrushSize = JsonSerializer.Deserialize(cachedBrushSize);
+ if (projectCache.TryGetCustomData("ClothGizmoBrushStrength", out var cachedBrushStrength))
+ _gizmoMode.BrushStrength = JsonSerializer.Deserialize(cachedBrushStrength);
+
+ gizmos.AddMode(_gizmoMode);
+ _prevMode = gizmos.ActiveMode;
+ gizmos.ActiveMode = _gizmoMode;
+ _gizmoMode.Gizmo.SetPaintCloth((Cloth)Values[0]);
+
+ // Insert gizmo mode options to properties editing
+ var paintGroup = layout.Group("Cloth Painting");
+ var paintValue = new ReadOnlyValueContainer(new ScriptType(typeof(ClothPaintingGizmoMode)), _gizmoMode);
+ paintGroup.Object(paintValue);
+ {
+ var grid = paintGroup.CustomContainer();
+ var gridControl = grid.CustomControl;
+ gridControl.ClipChildren = false;
+ gridControl.Height = Button.DefaultHeight;
+ gridControl.SlotsHorizontally = 2;
+ gridControl.SlotsVertically = 1;
+ grid.Button("Fill", "Fills the cloth particles with given paint value.").Button.Clicked += _gizmoMode.Gizmo.Fill;
+ grid.Button("Reset", "Clears the cloth particles paint.").Button.Clicked += _gizmoMode.Gizmo.Reset;
+ }
+ }
+
+ ///
+ protected override void Deinitialize()
+ {
+ // Cleanup gizmos
+ if (_gizmoMode != null)
+ {
+ var gizmos = _gizmoMode.Owner.Gizmos;
+ if (gizmos.ActiveMode == _gizmoMode)
+ gizmos.ActiveMode = _prevMode;
+ gizmos.RemoveMode(_gizmoMode);
+ var projectCache = Editor.Instance.ProjectCache;
+ projectCache.SetCustomData("ClothGizmoPaintValue", JsonSerializer.Serialize(_gizmoMode.PaintValue, typeof(float)));
+ projectCache.SetCustomData("ClothGizmoContinuousPaint", JsonSerializer.Serialize(_gizmoMode.ContinuousPaint, typeof(bool)));
+ projectCache.SetCustomData("ClothGizmoBrushFalloff", JsonSerializer.Serialize(_gizmoMode.BrushFalloff, typeof(float)));
+ projectCache.SetCustomData("ClothGizmoBrushSize", JsonSerializer.Serialize(_gizmoMode.BrushSize, typeof(float)));
+ projectCache.SetCustomData("ClothGizmoBrushStrength", JsonSerializer.Serialize(_gizmoMode.BrushStrength, typeof(float)));
+ _gizmoMode.Dispose();
+ _gizmoMode = null;
+ }
+ _prevMode = null;
+
+ base.Deinitialize();
+ }
+ }
+}
diff --git a/Source/Editor/CustomEditors/Dedicated/MeshReferenceEditor.cs b/Source/Editor/CustomEditors/Dedicated/MeshReferenceEditor.cs
new file mode 100644
index 000000000..71c5f6daf
--- /dev/null
+++ b/Source/Editor/CustomEditors/Dedicated/MeshReferenceEditor.cs
@@ -0,0 +1,336 @@
+// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
+
+using System;
+using System.IO;
+using System.Linq;
+using FlaxEditor.CustomEditors.Editors;
+using FlaxEditor.CustomEditors.Elements;
+using FlaxEditor.GUI;
+using FlaxEditor.Scripting;
+using FlaxEngine;
+using FlaxEngine.GUI;
+
+namespace FlaxEditor.CustomEditors.Dedicated
+{
+ ///
+ /// Custom editor for .
+ ///
+ ///
+ [CustomEditor(typeof(ModelInstanceActor.MeshReference)), DefaultEditor]
+ public class MeshReferenceEditor : CustomEditor
+ {
+ private class MeshRefPickerControl : Control
+ {
+ private ModelInstanceActor.MeshReference _value = new ModelInstanceActor.MeshReference { LODIndex = -1, MeshIndex = -1 };
+ private string _valueName;
+ private Float2 _mousePos;
+
+ public string[][] MeshNames;
+ public event Action ValueChanged;
+
+ public ModelInstanceActor.MeshReference Value
+ {
+ get => _value;
+ set
+ {
+ if (_value.LODIndex == value.LODIndex && _value.MeshIndex == value.MeshIndex)
+ return;
+ _value = value;
+ if (value.LODIndex == -1 || value.MeshIndex == -1)
+ _valueName = null;
+ else if (MeshNames.Length == 1)
+ _valueName = MeshNames[value.LODIndex][value.MeshIndex];
+ else
+ _valueName = $"LOD{value.LODIndex} - {MeshNames[value.LODIndex][value.MeshIndex]}";
+ ValueChanged?.Invoke();
+ }
+ }
+
+ public MeshRefPickerControl()
+ : base(0, 0, 50, 16)
+ {
+ }
+
+ private void ShowDropDownMenu()
+ {
+ // Show context menu with tree structure of LODs and meshes
+ Focus();
+ var cm = new ItemsListContextMenu(200);
+ var meshNames = MeshNames;
+ var actor = _value.Actor;
+ for (int lodIndex = 0; lodIndex < meshNames.Length; lodIndex++)
+ {
+ var item = new ItemsListContextMenu.Item
+ {
+ Name = "LOD" + lodIndex,
+ Tag = new ModelInstanceActor.MeshReference { Actor = actor, LODIndex = lodIndex, MeshIndex = 0 },
+ TintColor = new Color(0.8f, 0.8f, 1.0f, 0.8f),
+ };
+ cm.AddItem(item);
+
+ for (int meshIndex = 0; meshIndex < meshNames[lodIndex].Length; meshIndex++)
+ {
+ item = new ItemsListContextMenu.Item
+ {
+ Name = " " + meshNames[lodIndex][meshIndex],
+ Tag = new ModelInstanceActor.MeshReference { Actor = actor, LODIndex = lodIndex, MeshIndex = meshIndex },
+ };
+ if (_value.LODIndex == lodIndex && _value.MeshIndex == meshIndex)
+ item.BackgroundColor = FlaxEngine.GUI.Style.Current.BackgroundSelected;
+ cm.AddItem(item);
+ }
+ }
+ cm.ItemClicked += item => Value = (ModelInstanceActor.MeshReference)item.Tag;
+ cm.Show(Parent, BottomLeft);
+ }
+
+ ///
+ public override void Draw()
+ {
+ base.Draw();
+
+ // Cache data
+ var style = FlaxEngine.GUI.Style.Current;
+ bool isSelected = _valueName != null;
+ bool isEnabled = EnabledInHierarchy;
+ var frameRect = new Rectangle(0, 0, Width, 16);
+ if (isSelected)
+ frameRect.Width -= 16;
+ frameRect.Width -= 16;
+ var nameRect = new Rectangle(2, 1, frameRect.Width - 4, 14);
+ var button1Rect = new Rectangle(nameRect.Right + 2, 1, 14, 14);
+ var button2Rect = new Rectangle(button1Rect.Right + 2, 1, 14, 14);
+
+ // Draw frame
+ Render2D.DrawRectangle(frameRect, isEnabled && (IsMouseOver || IsNavFocused) ? style.BorderHighlighted : style.BorderNormal);
+
+ // Check if has item selected
+ if (isSelected)
+ {
+ // Draw name
+ Render2D.PushClip(nameRect);
+ Render2D.DrawText(style.FontMedium, _valueName, nameRect, isEnabled ? style.Foreground : style.ForegroundDisabled, TextAlignment.Near, TextAlignment.Center);
+ Render2D.PopClip();
+
+ // Draw deselect button
+ Render2D.DrawSprite(style.Cross, button1Rect, isEnabled && button1Rect.Contains(_mousePos) ? style.Foreground : style.ForegroundGrey);
+ }
+ else
+ {
+ // Draw info
+ Render2D.DrawText(style.FontMedium, "-", nameRect, isEnabled ? Color.OrangeRed : Color.DarkOrange, TextAlignment.Near, TextAlignment.Center);
+ }
+
+ // Draw picker button
+ var pickerRect = isSelected ? button2Rect : button1Rect;
+ Render2D.DrawSprite(style.ArrowDown, pickerRect, isEnabled && pickerRect.Contains(_mousePos) ? style.Foreground : style.ForegroundGrey);
+ }
+
+ ///
+ public override void OnMouseEnter(Float2 location)
+ {
+ _mousePos = location;
+
+ base.OnMouseEnter(location);
+ }
+
+ ///
+ public override void OnMouseLeave()
+ {
+ _mousePos = Float2.Minimum;
+
+ base.OnMouseLeave();
+ }
+
+ ///
+ public override void OnMouseMove(Float2 location)
+ {
+ _mousePos = location;
+
+ base.OnMouseMove(location);
+ }
+
+ ///
+ public override bool OnMouseUp(Float2 location, MouseButton button)
+ {
+ // Cache data
+ bool isSelected = _valueName != null;
+ var frameRect = new Rectangle(0, 0, Width, 16);
+ if (isSelected)
+ frameRect.Width -= 16;
+ frameRect.Width -= 16;
+ var nameRect = new Rectangle(2, 1, frameRect.Width - 4, 14);
+ var button1Rect = new Rectangle(nameRect.Right + 2, 1, 14, 14);
+ var button2Rect = new Rectangle(button1Rect.Right + 2, 1, 14, 14);
+
+ // Deselect
+ if (isSelected && button1Rect.Contains(ref location))
+ Value = new ModelInstanceActor.MeshReference { Actor = null, LODIndex = -1, MeshIndex = -1 };
+
+ // Picker dropdown menu
+ if ((isSelected ? button2Rect : button1Rect).Contains(ref location))
+ ShowDropDownMenu();
+
+ return base.OnMouseUp(location, button);
+ }
+
+ ///
+ public override bool OnMouseDoubleClick(Float2 location, MouseButton button)
+ {
+ Focus();
+
+ // Open model editor window
+ if (_value.Actor is StaticModel staticModel)
+ Editor.Instance.ContentEditing.Open(staticModel.Model);
+ else if (_value.Actor is AnimatedModel animatedModel)
+ Editor.Instance.ContentEditing.Open(animatedModel.SkinnedModel);
+
+ return base.OnMouseDoubleClick(location, button);
+ }
+
+ ///
+ public override void OnSubmit()
+ {
+ base.OnSubmit();
+
+ ShowDropDownMenu();
+ }
+
+ ///
+ public override void OnDestroy()
+ {
+ MeshNames = null;
+ _valueName = null;
+
+ base.OnDestroy();
+ }
+ }
+
+ private ModelInstanceActor _actor;
+ private CustomElement _actorPicker;
+ private CustomElement _meshPicker;
+
+ ///
+ public override DisplayStyle Style => DisplayStyle.Inline;
+
+ ///
+ public override void Initialize(LayoutElementsContainer layout)
+ {
+ // Get the context actor to pick the mesh from it
+ if (GetActor(out var actor))
+ {
+ // TODO: support editing multiple values
+ layout.Label("Different values");
+ return;
+ }
+ _actor = actor;
+
+ var showActorPicker = actor == null || ParentEditor.Values.All(x => x is not Cloth);
+ if (showActorPicker)
+ {
+ // Actor reference picker
+ _actorPicker = layout.Custom();
+ _actorPicker.CustomControl.Type = new ScriptType(typeof(ModelInstanceActor));
+ _actorPicker.CustomControl.ValueChanged += () => SetValue(new ModelInstanceActor.MeshReference { Actor = (ModelInstanceActor)_actorPicker.CustomControl.Value });
+ }
+
+ if (actor != null)
+ {
+ // Get mesh names hierarchy
+ string[][] meshNames;
+ if (actor is StaticModel staticModel)
+ {
+ var model = staticModel.Model;
+ if (model == null || model.WaitForLoaded())
+ return;
+ var materials = model.MaterialSlots;
+ var lods = model.LODs;
+ meshNames = new string[lods.Length][];
+ for (int lodIndex = 0; lodIndex < lods.Length; lodIndex++)
+ {
+ var lodMeshes = lods[lodIndex].Meshes;
+ meshNames[lodIndex] = new string[lodMeshes.Length];
+ for (int meshIndex = 0; meshIndex < lodMeshes.Length; meshIndex++)
+ {
+ var mesh = lodMeshes[meshIndex];
+ var materialName = materials[mesh.MaterialSlotIndex].Name;
+ if (string.IsNullOrEmpty(materialName) && materials[mesh.MaterialSlotIndex].Material)
+ materialName = Path.GetFileNameWithoutExtension(materials[mesh.MaterialSlotIndex].Material.Path);
+ if (string.IsNullOrEmpty(materialName))
+ meshNames[lodIndex][meshIndex] = $"Mesh {meshIndex}";
+ else
+ meshNames[lodIndex][meshIndex] = $"Mesh {meshIndex} ({materialName})";
+ }
+ }
+ }
+ else if (actor is AnimatedModel animatedModel)
+ {
+ var skinnedModel = animatedModel.SkinnedModel;
+ if (skinnedModel == null || skinnedModel.WaitForLoaded())
+ return;
+ var materials = skinnedModel.MaterialSlots;
+ var lods = skinnedModel.LODs;
+ meshNames = new string[lods.Length][];
+ for (int lodIndex = 0; lodIndex < lods.Length; lodIndex++)
+ {
+ var lodMeshes = lods[lodIndex].Meshes;
+ meshNames[lodIndex] = new string[lodMeshes.Length];
+ for (int meshIndex = 0; meshIndex < lodMeshes.Length; meshIndex++)
+ {
+ var mesh = lodMeshes[meshIndex];
+ var materialName = materials[mesh.MaterialSlotIndex].Name;
+ if (string.IsNullOrEmpty(materialName) && materials[mesh.MaterialSlotIndex].Material)
+ materialName = Path.GetFileNameWithoutExtension(materials[mesh.MaterialSlotIndex].Material.Path);
+ if (string.IsNullOrEmpty(materialName))
+ meshNames[lodIndex][meshIndex] = $"Mesh {meshIndex}";
+ else
+ meshNames[lodIndex][meshIndex] = $"Mesh {meshIndex} ({materialName})";
+ }
+ }
+ }
+ else
+ return; // Not supported model type
+
+ // Mesh reference picker
+ _meshPicker = layout.Custom();
+ _meshPicker.CustomControl.MeshNames = meshNames;
+ _meshPicker.CustomControl.Value = (ModelInstanceActor.MeshReference)Values[0];
+ _meshPicker.CustomControl.ValueChanged += () => SetValue(_meshPicker.CustomControl.Value);
+ }
+ }
+
+ ///
+ public override void Refresh()
+ {
+ base.Refresh();
+
+ if (_actorPicker != null)
+ {
+ GetActor(out var actor);
+ _actorPicker.CustomControl.Value = actor;
+ if (actor != _actor)
+ {
+ RebuildLayout();
+ return;
+ }
+ }
+ if (_meshPicker != null)
+ {
+ _meshPicker.CustomControl.Value = (ModelInstanceActor.MeshReference)Values[0];
+ }
+ }
+
+ private bool GetActor(out ModelInstanceActor actor)
+ {
+ actor = null;
+ foreach (ModelInstanceActor.MeshReference value in Values)
+ {
+ if (actor == null)
+ actor = value.Actor;
+ else if (actor != value.Actor)
+ return true;
+ }
+ return false;
+ }
+ }
+}
diff --git a/Source/Editor/CustomEditors/Dedicated/MissingScriptEditor.cs b/Source/Editor/CustomEditors/Dedicated/MissingScriptEditor.cs
index 03d5ddd55..a6c4e6623 100644
--- a/Source/Editor/CustomEditors/Dedicated/MissingScriptEditor.cs
+++ b/Source/Editor/CustomEditors/Dedicated/MissingScriptEditor.cs
@@ -20,7 +20,7 @@ public class MissingScriptEditor : GenericEditor
}
dropPanel.HeaderTextColor = Color.OrangeRed;
-
+
base.Initialize(layout);
}
}
diff --git a/Source/Editor/CustomEditors/Dedicated/RagdollEditor.cs b/Source/Editor/CustomEditors/Dedicated/RagdollEditor.cs
index 5a10b9a52..c4b334b3a 100644
--- a/Source/Editor/CustomEditors/Dedicated/RagdollEditor.cs
+++ b/Source/Editor/CustomEditors/Dedicated/RagdollEditor.cs
@@ -50,7 +50,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
grid.Button("Remove bone").Button.ButtonClicked += OnRemoveBone;
}
- if (Presenter.Owner is Windows.PropertiesWindow || Presenter.Owner is Windows.Assets.PrefabWindow)
+ if (Presenter.Owner != null)
{
// Selection
var grid = editorGroup.CustomContainer();
@@ -309,10 +309,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
if (node != null)
selection.Add(node);
}
- if (Presenter.Owner is Windows.PropertiesWindow propertiesWindow)
- propertiesWindow.Editor.SceneEditing.Select(selection);
- else if (Presenter.Owner is Windows.Assets.PrefabWindow prefabWindow)
- prefabWindow.Select(selection);
+ Presenter.Owner.Select(selection);
}
}
}
diff --git a/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs b/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs
index aaf45bc52..d7bfbbad7 100644
--- a/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs
+++ b/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs
@@ -258,27 +258,15 @@ namespace FlaxEditor.CustomEditors.Dedicated
/// Small image control added per script group that allows to drag and drop a reference to it. Also used to reorder the scripts.
///
///
- internal class ScriptDragIcon : Image
+ internal class DragImage : Image
{
- private ScriptsEditor _editor;
private bool _isMouseDown;
private Float2 _mouseDownPos;
///
- /// Gets the target script.
+ /// Action called when drag event should start.
///
- public Script Script => (Script)Tag;
-
- ///
- /// Initializes a new instance of the class.
- ///
- /// The script editor.
- /// The target script.
- public ScriptDragIcon(ScriptsEditor editor, Script script)
- {
- Tag = script;
- _editor = editor;
- }
+ public Action Drag;
///
public override void OnMouseEnter(Float2 location)
@@ -291,11 +279,10 @@ namespace FlaxEditor.CustomEditors.Dedicated
///
public override void OnMouseLeave()
{
- // Check if start drag drop
if (_isMouseDown)
{
- DoDrag();
_isMouseDown = false;
+ Drag(this);
}
base.OnMouseLeave();
@@ -304,11 +291,10 @@ namespace FlaxEditor.CustomEditors.Dedicated
///
public override void OnMouseMove(Float2 location)
{
- // Check if start drag drop
if (_isMouseDown && Float2.Distance(location, _mouseDownPos) > 10.0f)
{
- DoDrag();
_isMouseDown = false;
+ Drag(this);
}
base.OnMouseMove(location);
@@ -319,8 +305,8 @@ namespace FlaxEditor.CustomEditors.Dedicated
{
if (button == MouseButton.Left)
{
- // Clear flag
_isMouseDown = false;
+ return true;
}
return base.OnMouseUp(location, button);
@@ -331,21 +317,13 @@ namespace FlaxEditor.CustomEditors.Dedicated
{
if (button == MouseButton.Left)
{
- // Set flag
_isMouseDown = true;
_mouseDownPos = location;
+ return true;
}
return base.OnMouseDown(location, button);
}
-
- private void DoDrag()
- {
- var script = Script;
- _editor.OnScriptDragChange(true, script);
- DoDragDrop(DragScripts.GetDragData(script));
- _editor.OnScriptDragChange(false, script);
- }
}
internal class ScriptArrangeBar : Control
@@ -643,7 +621,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
_scriptToggles[i] = scriptToggle;
// Add drag button to the group
- var scriptDrag = new ScriptDragIcon(this, script)
+ var scriptDrag = new DragImage
{
TooltipText = "Script reference",
AutoFocus = true,
@@ -654,6 +632,13 @@ namespace FlaxEditor.CustomEditors.Dedicated
Margin = new Margin(1),
Brush = new SpriteBrush(Editor.Instance.Icons.DragBar12),
Tag = script,
+ Drag = img =>
+ {
+ var s = (Script)img.Tag;
+ OnScriptDragChange(true, s);
+ img.DoDragDrop(DragScripts.GetDragData(s));
+ OnScriptDragChange(false, s);
+ }
};
// Add settings button to the group
diff --git a/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs b/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs
index 03d58ef33..7b9b65c5c 100644
--- a/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs
+++ b/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs
@@ -348,7 +348,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
if (!CanEditTangent())
return;
-
+
var index = _lastPointSelected.Index;
var currentTangentInPosition = _selectedSpline.GetSplineLocalTangent(index, true).Translation;
var currentTangentOutPosition = _selectedSpline.GetSplineLocalTangent(index, false).Translation;
diff --git a/Source/Editor/CustomEditors/Editors/ActorTransformEditor.cs b/Source/Editor/CustomEditors/Editors/ActorTransformEditor.cs
index 4aa02ac78..4c153e759 100644
--- a/Source/Editor/CustomEditors/Editors/ActorTransformEditor.cs
+++ b/Source/Editor/CustomEditors/Editors/ActorTransformEditor.cs
@@ -88,7 +88,7 @@ namespace FlaxEditor.CustomEditors.Editors
LinkValues = Editor.Instance.Windows.PropertiesWin.ScaleLinked;
// Add button with the link icon
-
+
_linkButton = new Button
{
BackgroundBrush = new SpriteBrush(Editor.Instance.Icons.Link32),
diff --git a/Source/Editor/CustomEditors/Editors/BehaviorKnowledgeSelectorEditor.cs b/Source/Editor/CustomEditors/Editors/BehaviorKnowledgeSelectorEditor.cs
new file mode 100644
index 000000000..8ac6a51cb
--- /dev/null
+++ b/Source/Editor/CustomEditors/Editors/BehaviorKnowledgeSelectorEditor.cs
@@ -0,0 +1,196 @@
+// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using FlaxEditor.GUI;
+using FlaxEditor.GUI.Tree;
+using FlaxEditor.Scripting;
+using FlaxEngine;
+using FlaxEngine.GUI;
+using FlaxEngine.Utilities;
+
+namespace FlaxEditor.CustomEditors.Editors
+{
+ ///
+ /// Custom editor for and .
+ ///
+ public sealed class BehaviorKnowledgeSelectorEditor : CustomEditor
+ {
+ private ClickableLabel _label;
+
+ ///
+ public override DisplayStyle Style => DisplayStyle.Inline;
+
+ ///
+ public override void Initialize(LayoutElementsContainer layout)
+ {
+ _label = layout.ClickableLabel(Path).CustomControl;
+ _label.RightClick += ShowPicker;
+ var button = new Button
+ {
+ Size = new Float2(16.0f),
+ Text = "...",
+ TooltipText = "Edit...",
+ Parent = _label,
+ };
+ button.SetAnchorPreset(AnchorPresets.MiddleRight, false, true);
+ button.Clicked += ShowPicker;
+ }
+
+ ///
+ public override void Refresh()
+ {
+ base.Refresh();
+
+ // Update label
+ _label.Text = _label.TooltipText = Path;
+ }
+
+ private string Path
+ {
+ get
+ {
+ var v = Values[0];
+ if (v is BehaviorKnowledgeSelectorAny any)
+ return any.Path;
+ if (v is string str)
+ return str;
+ var pathField = v.GetType().GetField("Path");
+ return pathField.GetValue(v) as string;
+ }
+ set
+ {
+ if (string.Equals(Path, value, StringComparison.Ordinal))
+ return;
+ var v = Values[0];
+ if (v is BehaviorKnowledgeSelectorAny)
+ v = new BehaviorKnowledgeSelectorAny(value);
+ else if (v is string)
+ v = value;
+ else
+ {
+ var pathField = v.GetType().GetField("Path");
+ pathField.SetValue(v, value);
+ }
+ SetValue(v);
+ }
+ }
+
+ private void ShowPicker()
+ {
+ // Get Behavior Knowledge to select from
+ var behaviorTreeWindow = Presenter.Owner as Windows.Assets.BehaviorTreeWindow;
+ var rootNode = behaviorTreeWindow?.RootNode;
+ if (rootNode == null)
+ return;
+ var typed = ScriptType.Null;
+ var valueType = Values[0].GetType();
+ if (valueType.Name == "BehaviorKnowledgeSelector`1")
+ {
+ // Get typed selector type to show only assignable items
+ typed = new ScriptType(valueType.GenericTypeArguments[0]);
+ }
+
+ // Get customization options
+ var attributes = Values.GetAttributes();
+ var attribute = (BehaviorKnowledgeSelectorAttribute)attributes?.FirstOrDefault(x => x is BehaviorKnowledgeSelectorAttribute);
+ bool isGoalSelector = false;
+ if (attribute != null)
+ {
+ isGoalSelector = attribute.IsGoalSelector;
+ }
+
+ // Create menu with tree-like structure and search box
+ var menu = Utilities.Utils.CreateSearchPopup(out var searchBox, out var tree, 0, true);
+ var selected = Path;
+
+ // Empty
+ var noneNode = new TreeNode
+ {
+ Text = "",
+ TooltipText = "Deselect value",
+ Parent = tree,
+ };
+ if (string.IsNullOrEmpty(selected))
+ tree.Select(noneNode);
+
+ if (!isGoalSelector)
+ {
+ // Blackboard
+ SetupPickerTypeItems(tree, typed, selected, "Blackboard", "Blackboard/", rootNode.BlackboardType);
+ }
+
+ // Goals
+ var goalTypes = rootNode.GoalTypes;
+ if (goalTypes?.Length != 0)
+ {
+ var goalsNode = new TreeNode
+ {
+ Text = "Goal",
+ TooltipText = "List of goal types defined in Blackboard Tree",
+ Parent = tree,
+ };
+ foreach (var goalTypeName in goalTypes)
+ {
+ var goalType = TypeUtils.GetType(goalTypeName);
+ if (goalType == null)
+ continue;
+ var goalTypeNode = SetupPickerTypeItems(tree, typed, selected, goalType.Name, "Goal/" + goalTypeName + "/", goalTypeName, !isGoalSelector);
+ goalTypeNode.Parent = goalsNode;
+ }
+ goalsNode.ExpandAll(true);
+ }
+
+ tree.SelectedChanged += delegate(List before, List after)
+ {
+ if (after.Count == 1)
+ {
+ menu.Hide();
+ Path = after[0].Tag as string;
+ }
+ };
+ menu.Show(_label, new Float2(0, _label.Height));
+ }
+
+ private TreeNode SetupPickerTypeItems(Tree tree, ScriptType typed, string selected, string text, string typePath, string typeName, bool addItems = true)
+ {
+ var type = TypeUtils.GetType(typeName);
+ if (type == null)
+ return null;
+ var typeNode = new TreeNode
+ {
+ Text = text,
+ TooltipText = type.TypeName,
+ Tag = typePath, // Ability to select whole item type data (eg. whole blackboard value)
+ Parent = tree,
+ };
+ if (typed && !typed.IsAssignableFrom(type))
+ typeNode.Tag = null;
+ if (string.Equals(selected, (string)typeNode.Tag, StringComparison.Ordinal))
+ tree.Select(typeNode);
+ if (addItems)
+ {
+ var items = GenericEditor.GetItemsForType(type, type.IsClass, true);
+ foreach (var item in items)
+ {
+ if (typed && !typed.IsAssignableFrom(item.Info.ValueType))
+ continue;
+ var itemPath = typePath + item.Info.Name;
+ var node = new TreeNode
+ {
+ Text = item.DisplayName,
+ TooltipText = item.TooltipText,
+ Tag = itemPath,
+ Parent = typeNode,
+ };
+ if (string.Equals(selected, itemPath, StringComparison.Ordinal))
+ tree.Select(node);
+ // TODO: add support for nested items (eg. field from blackboard structure field)
+ }
+ typeNode.Expand(true);
+ }
+ return typeNode;
+ }
+ }
+}
diff --git a/Source/Editor/CustomEditors/Editors/GenericEditor.cs b/Source/Editor/CustomEditors/Editors/GenericEditor.cs
index cba2b5a5a..55ac453a9 100644
--- a/Source/Editor/CustomEditors/Editors/GenericEditor.cs
+++ b/Source/Editor/CustomEditors/Editors/GenericEditor.cs
@@ -26,7 +26,7 @@ namespace FlaxEditor.CustomEditors.Editors
/// Describes object property/field information for custom editors pipeline.
///
///
- protected class ItemInfo : IComparable
+ public class ItemInfo : IComparable
{
private Options.GeneralOptions.MembersOrder _membersOrder;
@@ -248,7 +248,7 @@ namespace FlaxEditor.CustomEditors.Editors
/// True if use type properties.
/// True if use type fields.
/// The items.
- protected List GetItemsForType(ScriptType type, bool useProperties, bool useFields)
+ public static List GetItemsForType(ScriptType type, bool useProperties, bool useFields)
{
var items = new List();
diff --git a/Source/Editor/CustomEditors/Editors/ObjectSwitcherEditor.cs b/Source/Editor/CustomEditors/Editors/ObjectSwitcherEditor.cs
index 4d0c0f662..0369d679d 100644
--- a/Source/Editor/CustomEditors/Editors/ObjectSwitcherEditor.cs
+++ b/Source/Editor/CustomEditors/Editors/ObjectSwitcherEditor.cs
@@ -125,7 +125,7 @@ namespace FlaxEditor.CustomEditors.Editors
}
// Value
- var values = new CustomValueContainer(type, (instance, index) => instance, (instance, index, value) => { });
+ var values = new CustomValueContainer(type, (instance, index) => instance);
values.AddRange(Values);
var editor = CustomEditorsUtil.CreateEditor(type);
var style = editor.Style;
@@ -160,7 +160,6 @@ namespace FlaxEditor.CustomEditors.Editors
var option = _options[comboBox.SelectedIndex];
if (option.Type != null)
value = option.Creator(option.Type);
-
}
SetValue(value);
RebuildLayoutOnRefresh();
diff --git a/Source/Editor/CustomEditors/Editors/TagEditor.cs b/Source/Editor/CustomEditors/Editors/TagEditor.cs
index 911397785..dbd5d124c 100644
--- a/Source/Editor/CustomEditors/Editors/TagEditor.cs
+++ b/Source/Editor/CustomEditors/Editors/TagEditor.cs
@@ -634,7 +634,7 @@ namespace FlaxEditor.CustomEditors.Editors
var textSize = FlaxEngine.GUI.Style.Current.FontMedium.MeasureText(buttonText);
if (textSize.Y > button.Width)
button.Width = textSize.Y + 2;
-
+
button.SetAnchorPreset(AnchorPresets.MiddleRight, false, true);
button.Clicked += ShowPicker;
}
diff --git a/Source/Editor/CustomEditors/Editors/TypeEditor.cs b/Source/Editor/CustomEditors/Editors/TypeEditor.cs
index ab17c4ae1..38800a738 100644
--- a/Source/Editor/CustomEditors/Editors/TypeEditor.cs
+++ b/Source/Editor/CustomEditors/Editors/TypeEditor.cs
@@ -464,6 +464,11 @@ namespace FlaxEditor.CustomEditors.Editors
///
public class TypeNameEditor : TypeEditorBase
{
+ ///
+ /// Prevents spamming log if Value contains missing type to skip research in subsequential Refresh ticks.
+ ///
+ private string _lastTypeNameError;
+
///
public override void Initialize(LayoutElementsContainer layout)
{
@@ -484,8 +489,19 @@ namespace FlaxEditor.CustomEditors.Editors
{
base.Refresh();
- if (!HasDifferentValues && Values[0] is string asTypename)
- _element.CustomControl.Value = TypeUtils.GetType(asTypename);
+ if (!HasDifferentValues && Values[0] is string asTypename &&
+ !string.Equals(asTypename, _lastTypeNameError, StringComparison.Ordinal))
+ {
+ try
+ {
+ _element.CustomControl.Value = TypeUtils.GetType(asTypename);
+ }
+ finally
+ {
+ if (_element.CustomControl.Value == null && asTypename.Length != 0)
+ _lastTypeNameError = asTypename;
+ }
+ }
}
}
}
diff --git a/Source/Editor/CustomEditors/GUI/PropertiesList.cs b/Source/Editor/CustomEditors/GUI/PropertiesList.cs
index 2b2f0a3d3..93aacbd34 100644
--- a/Source/Editor/CustomEditors/GUI/PropertiesList.cs
+++ b/Source/Editor/CustomEditors/GUI/PropertiesList.cs
@@ -175,7 +175,7 @@ namespace FlaxEditor.CustomEditors.GUI
{
// Clear flag
_mouseOverSplitter = false;
-
+
if (_cursorChanged)
{
Cursor = CursorType.Default;
diff --git a/Source/Editor/CustomEditors/Values/CustomValueContainer.cs b/Source/Editor/CustomEditors/Values/CustomValueContainer.cs
index 5be61399b..9a09c4cc2 100644
--- a/Source/Editor/CustomEditors/Values/CustomValueContainer.cs
+++ b/Source/Editor/CustomEditors/Values/CustomValueContainer.cs
@@ -38,15 +38,12 @@ namespace FlaxEditor.CustomEditors
///
/// Type of the value.
/// The value getter.
- /// The value setter.
+ /// The value setter (can be null if value is read-only).
/// The custom type attributes used to override the value editor logic or appearance (eg. instance of ).
- public CustomValueContainer(ScriptType valueType, GetDelegate getter, SetDelegate setter, object[] attributes = null)
+ public CustomValueContainer(ScriptType valueType, GetDelegate getter, SetDelegate setter = null, object[] attributes = null)
: base(ScriptMemberInfo.Null, valueType)
{
- if (getter == null || setter == null)
- throw new ArgumentNullException();
-
- _getter = getter;
+ _getter = getter ?? throw new ArgumentNullException();
_setter = setter;
_attributes = attributes;
}
@@ -57,9 +54,9 @@ namespace FlaxEditor.CustomEditors
/// Type of the value.
/// The initial value.
/// The value getter.
- /// The value setter.
+ /// The value setter (can be null if value is read-only).
/// The custom type attributes used to override the value editor logic or appearance (eg. instance of ).
- public CustomValueContainer(ScriptType valueType, object initialValue, GetDelegate getter, SetDelegate setter, object[] attributes = null)
+ public CustomValueContainer(ScriptType valueType, object initialValue, GetDelegate getter, SetDelegate setter = null, object[] attributes = null)
: this(valueType, getter, setter, attributes)
{
Add(initialValue);
@@ -89,6 +86,8 @@ namespace FlaxEditor.CustomEditors
{
if (instanceValues == null || instanceValues.Count != Count)
throw new ArgumentException();
+ if (_setter == null)
+ return;
for (int i = 0; i < Count; i++)
{
@@ -105,6 +104,8 @@ namespace FlaxEditor.CustomEditors
throw new ArgumentException();
if (values == null || values.Count != Count)
throw new ArgumentException();
+ if (_setter == null)
+ return;
for (int i = 0; i < Count; i++)
{
@@ -120,6 +121,8 @@ namespace FlaxEditor.CustomEditors
{
if (instanceValues == null || instanceValues.Count != Count)
throw new ArgumentException();
+ if (_setter == null)
+ return;
for (int i = 0; i < Count; i++)
{
diff --git a/Source/Editor/Editor.cs b/Source/Editor/Editor.cs
index 06a6f0979..1b46222ea 100644
--- a/Source/Editor/Editor.cs
+++ b/Source/Editor/Editor.cs
@@ -8,7 +8,6 @@ using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.Marshalling;
using FlaxEditor.Content;
-using FlaxEditor.Content.Import;
using FlaxEditor.Content.Settings;
using FlaxEditor.Content.Thumbnails;
using FlaxEditor.Modules;
@@ -154,12 +153,12 @@ namespace FlaxEditor
public ContentFindingModule ContentFinding;
///
- /// The scripts editing
+ /// The scripts editing.
///
public CodeEditingModule CodeEditing;
///
- /// The scripts documentation
+ /// The scripts documentation.
///
public CodeDocsModule CodeDocs;
@@ -179,7 +178,7 @@ namespace FlaxEditor
public ProjectCacheModule ProjectCache;
///
- /// The undo/redo
+ /// The undo/redo.
///
public EditorUndo Undo;
@@ -726,8 +725,8 @@ namespace FlaxEditor
// Cleanup
Undo.Dispose();
- Surface.VisualScriptSurface.NodesCache.Clear();
- Surface.AnimGraphSurface.NodesCache.Clear();
+ foreach (var cache in Surface.VisjectSurface.NodesCache.Caches.ToArray())
+ cache.Clear();
Instance = null;
// Invoke new instance if need to open a project
@@ -797,7 +796,6 @@ namespace FlaxEditor
{
if (projectFilePath == null || !File.Exists(projectFilePath))
{
- // Error
MessageBox.Show("Missing project");
return;
}
@@ -933,21 +931,11 @@ namespace FlaxEditor
/// The .
///
Animation = 11,
- }
- ///
- /// Imports the audio asset file to the target location.
- ///
- /// The source file path.
- /// The result asset file path.
- /// The settings.
- /// True if importing failed, otherwise false.
- public static bool Import(string inputPath, string outputPath, AudioImportSettings settings)
- {
- if (settings == null)
- throw new ArgumentNullException();
- settings.ToInternal(out var internalOptions);
- return Internal_ImportAudio(inputPath, outputPath, ref internalOptions);
+ ///
+ /// The .
+ ///
+ BehaviorTree = 12,
}
///
@@ -1667,10 +1655,6 @@ namespace FlaxEditor
[return: MarshalAs(UnmanagedType.U1)]
internal static partial bool Internal_CloneAssetFile(string dstPath, string srcPath, ref Guid dstId);
- [LibraryImport("FlaxEngine", EntryPoint = "EditorInternal_ImportAudio", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(StringMarshaller))]
- [return: MarshalAs(UnmanagedType.U1)]
- internal static partial bool Internal_ImportAudio(string inputPath, string outputPath, ref AudioImportSettings.InternalOptions options);
-
[LibraryImport("FlaxEngine", EntryPoint = "EditorInternal_GetAudioClipMetadata", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(StringMarshaller))]
internal static partial void Internal_GetAudioClipMetadata(IntPtr obj, out int originalSize, out int importedSize);
diff --git a/Source/Editor/GUI/ContextMenu/ContextMenuBase.cs b/Source/Editor/GUI/ContextMenu/ContextMenuBase.cs
index 7ecd197eb..628af18e1 100644
--- a/Source/Editor/GUI/ContextMenu/ContextMenuBase.cs
+++ b/Source/Editor/GUI/ContextMenu/ContextMenuBase.cs
@@ -154,6 +154,7 @@ namespace FlaxEditor.GUI.ContextMenu
}
// Unlock and perform controls update
+ Location = Float2.Zero;
UnlockChildrenRecursive();
PerformLayout();
@@ -162,7 +163,6 @@ namespace FlaxEditor.GUI.ContextMenu
var dpiSize = Size * dpiScale;
var locationWS = parent.PointToWindow(location);
var locationSS = parentWin.PointToScreen(locationWS);
- Location = Float2.Zero;
var monitorBounds = Platform.GetMonitorBounds(locationSS);
var rightBottomLocationSS = locationSS + dpiSize;
bool isUp = false, isLeft = false;
diff --git a/Source/Editor/GUI/Drag/DragScripts.cs b/Source/Editor/GUI/Drag/DragScripts.cs
index e72d28479..875875ef3 100644
--- a/Source/Editor/GUI/Drag/DragScripts.cs
+++ b/Source/Editor/GUI/Drag/DragScripts.cs
@@ -58,7 +58,6 @@ namespace FlaxEditor.GUI.Drag
{
if (item == null)
throw new ArgumentNullException();
-
return new DragDataText(DragPrefix + item.ID.ToString("N"));
}
@@ -71,11 +70,9 @@ namespace FlaxEditor.GUI.Drag
{
if (items == null)
throw new ArgumentNullException();
-
string text = DragPrefix;
foreach (var item in items)
text += item.ID.ToString("N") + '\n';
-
return new DragDataText(text);
}
@@ -83,9 +80,7 @@ namespace FlaxEditor.GUI.Drag
/// Tries to parse the drag data.
///
/// The data.
- ///
- /// Gathered objects or empty IEnumerable if cannot get any valid.
- ///
+ /// Gathered objects or empty IEnumerable if cannot get any valid.
public override IEnumerable