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/Editor.cs b/Source/Editor/Editor.cs
index 06a6f0979..e0c460d0c 100644
--- a/Source/Editor/Editor.cs
+++ b/Source/Editor/Editor.cs
@@ -935,21 +935,6 @@ namespace FlaxEditor
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);
- }
-
///
/// Serializes the given object to json asset.
///
@@ -1667,10 +1652,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/Timeline/Tracks/CameraCutTrack.cs b/Source/Editor/GUI/Timeline/Tracks/CameraCutTrack.cs
index f6a542faa..5ca9ff7ce 100644
--- a/Source/Editor/GUI/Timeline/Tracks/CameraCutTrack.cs
+++ b/Source/Editor/GUI/Timeline/Tracks/CameraCutTrack.cs
@@ -125,6 +125,8 @@ namespace FlaxEditor.GUI.Timeline.Tracks
fov = cam.FieldOfView;
customAspectRatio = cam.CustomAspectRatio;
view.RenderLayersMask = cam.RenderLayersMask;
+ view.Flags = cam.RenderFlags;
+ view.Mode = cam.RenderMode;
}
// Try to evaluate camera properties based on the animated tracks
diff --git a/Source/Editor/Managed/ManagedEditor.Internal.cpp b/Source/Editor/Managed/ManagedEditor.Internal.cpp
index afcbb740a..d2d2f216a 100644
--- a/Source/Editor/Managed/ManagedEditor.Internal.cpp
+++ b/Source/Editor/Managed/ManagedEditor.Internal.cpp
@@ -50,43 +50,6 @@
Guid ManagedEditor::ObjectID(0x91970b4e, 0x99634f61, 0x84723632, 0x54c776af);
-// Disable warning C4800: 'const byte': forcing value to bool 'true' or 'false' (performance warning)
-#if defined(_MSC_VER)
-#pragma warning( push )
-#pragma warning( disable : 4800)
-#endif
-
-struct InternalAudioOptions
-{
- AudioFormat Format;
- byte DisableStreaming;
- byte Is3D;
- int32 BitDepth;
- float Quality;
-
- static void Convert(InternalAudioOptions* from, ImportAudio::Options* to)
- {
- to->Format = from->Format;
- to->DisableStreaming = from->DisableStreaming;
- to->Is3D = from->Is3D;
- to->BitDepth = from->BitDepth;
- to->Quality = from->Quality;
- }
-
- static void Convert(ImportAudio::Options* from, InternalAudioOptions* to)
- {
- to->Format = from->Format;
- to->DisableStreaming = from->DisableStreaming;
- to->Is3D = from->Is3D;
- to->BitDepth = from->BitDepth;
- to->Quality = from->Quality;
- }
-};
-
-#if defined(_MSC_VER)
-#pragma warning( pop )
-#endif
-
// Pack log messages into a single scratch buffer to reduce dynamic memory allocations
CriticalSection CachedLogDataLocker;
Array CachedLogData;
@@ -295,16 +258,6 @@ DEFINE_INTERNAL_CALL(MString*) EditorInternal_CanImport(MString* extensionObj)
return importer ? MUtils::ToString(importer->ResultExtension) : nullptr;
}
-DEFINE_INTERNAL_CALL(bool) EditorInternal_ImportAudio(MString* inputPathObj, MString* outputPathObj, InternalAudioOptions* optionsObj)
-{
- ImportAudio::Options options;
- InternalAudioOptions::Convert(optionsObj, &options);
- String inputPath, outputPath;
- MUtils::ToString(inputPathObj, inputPath);
- MUtils::ToString(outputPathObj, outputPath);
- return ManagedEditor::Import(inputPath, outputPath, &options);
-}
-
DEFINE_INTERNAL_CALL(void) EditorInternal_GetAudioClipMetadata(AudioClip* clip, int32* originalSize, int32* importedSize)
{
INTERNAL_CALL_CHECK(clip);
@@ -765,24 +718,6 @@ DEFINE_INTERNAL_CALL(MTypeObject*) CustomEditorsUtilInternal_GetCustomEditor(MTy
return CustomEditorsUtil::GetCustomEditor(targetType);
}
-DEFINE_INTERNAL_CALL(bool) AudioImportEntryInternal_GetAudioImportOptions(MString* pathObj, InternalAudioOptions* result)
-{
- String path;
- MUtils::ToString(pathObj, path);
- FileSystem::NormalizePath(path);
-
- ImportAudio::Options options;
- if (ImportAudio::TryGetImportOptions(path, options))
- {
- // Convert into managed storage
- InternalAudioOptions::Convert(&options, result);
-
- return true;
- }
-
- return false;
-}
-
DEFINE_INTERNAL_CALL(MArray*) LayersAndTagsSettingsInternal_GetCurrentLayers(int* layersCount)
{
*layersCount = Math::Max(1, Level::GetNonEmptyLayerNamesCount());
@@ -830,3 +765,15 @@ bool ManagedEditor::TryRestoreImportOptions(ModelTool::Options& options, String
FileSystem::NormalizePath(assetPath);
return ImportModelFile::TryGetImportOptions(assetPath, options);
}
+
+bool ManagedEditor::Import(const String& inputPath, const String& outputPath, const AudioTool::Options& options)
+{
+ return Import(inputPath, outputPath, (void*)&options);
+}
+
+bool ManagedEditor::TryRestoreImportOptions(AudioTool::Options& options, String assetPath)
+{
+ // Get options from model
+ FileSystem::NormalizePath(assetPath);
+ return ImportAudio::TryGetImportOptions(assetPath, options);
+}
diff --git a/Source/Editor/Managed/ManagedEditor.h b/Source/Editor/Managed/ManagedEditor.h
index ddd94b47e..93d1723a3 100644
--- a/Source/Editor/Managed/ManagedEditor.h
+++ b/Source/Editor/Managed/ManagedEditor.h
@@ -7,6 +7,7 @@
#include "Engine/ShadowsOfMordor/Types.h"
#include "Engine/Tools/TextureTool/TextureTool.h"
#include "Engine/Tools/ModelTool/ModelTool.h"
+#include "Engine/Tools/AudioTool/AudioTool.h"
namespace CSG
{
@@ -190,6 +191,25 @@ public:
API_FUNCTION() static bool TryRestoreImportOptions(API_PARAM(Ref) ModelTool::Options& options, String assetPath);
#endif
+#if COMPILE_WITH_AUDIO_TOOL
+ ///
+ /// Imports the audio asset file to the target location.
+ ///
+ /// The source file path.
+ /// The result asset file path.
+ /// The import settings.
+ /// True if importing failed, otherwise false.
+ API_FUNCTION() static bool Import(const String& inputPath, const String& outputPath, const AudioTool::Options& options);
+
+ ///
+ /// Tries the restore the asset import options from the target resource file.
+ ///
+ /// The options.
+ /// The asset path.
+ /// True settings has been restored, otherwise false.
+ API_FUNCTION() static bool TryRestoreImportOptions(API_PARAM(Ref) AudioTool::Options& options, String assetPath);
+#endif
+
private:
void OnEditorAssemblyLoaded(MAssembly* assembly);
diff --git a/Source/Editor/Modules/PrefabsModule.cs b/Source/Editor/Modules/PrefabsModule.cs
index 7b3ffebb3..bb76e125b 100644
--- a/Source/Editor/Modules/PrefabsModule.cs
+++ b/Source/Editor/Modules/PrefabsModule.cs
@@ -37,11 +37,6 @@ namespace FlaxEditor.Modules
///
public event Action PrefabApplied;
- ///
- /// Locally cached actor for prefab creation.
- ///
- private Actor _prefabCreationActor;
-
internal PrefabsModule(Editor editor)
: base(editor)
{
@@ -65,13 +60,14 @@ namespace FlaxEditor.Modules
/// To create prefab manually (from code) use method.
///
/// The scene selection to use.
- public void CreatePrefab(List selection)
+ /// The prefab window that creates it.
+ public void CreatePrefab(List selection, Windows.Assets.PrefabWindow prefabWindow = null)
{
if (selection == null)
selection = Editor.SceneEditing.Selection;
if (selection.Count == 1 && selection[0] is ActorNode actorNode && actorNode.CanCreatePrefab)
{
- CreatePrefab(actorNode.Actor);
+ CreatePrefab(actorNode.Actor, true, prefabWindow);
}
}
@@ -92,7 +88,8 @@ namespace FlaxEditor.Modules
///
/// The root prefab actor.
/// Allow renaming or not
- public void CreatePrefab(Actor actor, bool rename)
+ /// The prefab window that creates it.
+ public void CreatePrefab(Actor actor, bool rename, Windows.Assets.PrefabWindow prefabWindow = null)
{
// Skip in invalid states
if (!Editor.StateMachine.CurrentState.CanEditContent)
@@ -105,42 +102,47 @@ namespace FlaxEditor.Modules
PrefabCreating?.Invoke(actor);
var proxy = Editor.ContentDatabase.GetProxy();
- _prefabCreationActor = actor;
- Editor.Windows.ContentWin.NewItem(proxy, actor, OnPrefabCreated, actor.Name, rename);
+ Editor.Windows.ContentWin.NewItem(proxy, actor, contentItem => OnPrefabCreated(contentItem, actor, prefabWindow), actor.Name, rename);
}
- private void OnPrefabCreated(ContentItem contentItem)
+ private void OnPrefabCreated(ContentItem contentItem, Actor actor, Windows.Assets.PrefabWindow prefabWindow)
{
if (contentItem is PrefabItem prefabItem)
{
PrefabCreated?.Invoke(prefabItem);
}
- // Skip in invalid states
- if (!Editor.StateMachine.CurrentState.CanEditScene)
- return;
+ Undo undo = null;
+ if (prefabWindow != null)
+ {
+ prefabWindow.MarkAsEdited();
+ undo = prefabWindow.Undo;
+ }
+ else
+ {
+ // Skip in invalid states
+ if (!Editor.StateMachine.CurrentState.CanEditScene)
+ return;
+ undo = Editor.Undo;
+ }
// Record undo for prefab creating (backend links the target instance with the prefab)
- if (Editor.Undo.Enabled)
+ if (undo.Enabled)
{
- if (!_prefabCreationActor)
+ if (!actor)
return;
-
var actorsList = new List();
- Utilities.Utils.GetActorsTree(actorsList, _prefabCreationActor);
+ Utilities.Utils.GetActorsTree(actorsList, actor);
var actions = new IUndoAction[actorsList.Count];
for (int i = 0; i < actorsList.Count; i++)
- {
- var action = BreakPrefabLinkAction.Linked(actorsList[i]);
- actions[i] = action;
- }
- Undo.AddAction(new MultiUndoAction(actions));
-
- _prefabCreationActor = null;
+ actions[i] = BreakPrefabLinkAction.Linked(actorsList[i]);
+ undo.AddAction(new MultiUndoAction(actions));
}
- Editor.Instance.Windows.PropertiesWin.Presenter.BuildLayout();
+ Editor.Windows.PropertiesWin.Presenter.BuildLayout();
+ if (prefabWindow != null)
+ prefabWindow.Presenter.BuildLayout();
}
///
diff --git a/Source/Editor/SceneGraph/Actors/AnimatedModelNode.cs b/Source/Editor/SceneGraph/Actors/AnimatedModelNode.cs
index 604907ae4..0bf7f6afc 100644
--- a/Source/Editor/SceneGraph/Actors/AnimatedModelNode.cs
+++ b/Source/Editor/SceneGraph/Actors/AnimatedModelNode.cs
@@ -4,6 +4,7 @@ using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using FlaxEditor.GUI.ContextMenu;
+using FlaxEditor.Windows;
using FlaxEngine;
namespace FlaxEditor.SceneGraph.Actors
@@ -22,9 +23,9 @@ namespace FlaxEditor.SceneGraph.Actors
}
///
- public override void OnContextMenu(ContextMenu contextMenu)
+ public override void OnContextMenu(ContextMenu contextMenu, EditorWindow window)
{
- base.OnContextMenu(contextMenu);
+ base.OnContextMenu(contextMenu, window);
var actor = (AnimatedModel)Actor;
if (actor && actor.SkinnedModel)
diff --git a/Source/Editor/SceneGraph/Actors/CameraNode.cs b/Source/Editor/SceneGraph/Actors/CameraNode.cs
index 400a2e06f..1e01b5c50 100644
--- a/Source/Editor/SceneGraph/Actors/CameraNode.cs
+++ b/Source/Editor/SceneGraph/Actors/CameraNode.cs
@@ -6,6 +6,8 @@ using Real = System.Double;
using Real = System.Single;
#endif
+using FlaxEditor.GUI.ContextMenu;
+using FlaxEditor.Windows;
using FlaxEngine;
namespace FlaxEditor.SceneGraph.Actors
@@ -23,6 +25,25 @@ namespace FlaxEditor.SceneGraph.Actors
{
}
+ ///
+ public override void OnContextMenu(ContextMenu contextMenu, EditorWindow window)
+ {
+ base.OnContextMenu(contextMenu, window);
+ if (window is not SceneTreeWindow win)
+ return;
+ var button = new ContextMenuButton(contextMenu, "Move Camera to View");
+ button.Parent = contextMenu.ItemsContainer;
+ contextMenu.ItemsContainer.Children.Remove(button);
+ contextMenu.ItemsContainer.Children.Insert(4, button);
+ button.Clicked += () =>
+ {
+ var c = Actor as Camera;
+ var viewport = Editor.Instance.Windows.EditWin.Viewport;
+ c.Position = viewport.ViewPosition;
+ c.Orientation = viewport.ViewOrientation;
+ };
+ }
+
///
public override bool RayCastSelf(ref RayCastData ray, out Real distance, out Vector3 normal)
{
diff --git a/Source/Editor/SceneGraph/Actors/SceneNode.cs b/Source/Editor/SceneGraph/Actors/SceneNode.cs
index 0405e4fdf..c2ee3645a 100644
--- a/Source/Editor/SceneGraph/Actors/SceneNode.cs
+++ b/Source/Editor/SceneGraph/Actors/SceneNode.cs
@@ -3,6 +3,7 @@
using System.IO;
using FlaxEditor.GUI.ContextMenu;
using FlaxEditor.SceneGraph.GUI;
+using FlaxEditor.Windows;
using FlaxEngine;
namespace FlaxEditor.SceneGraph.Actors
@@ -65,7 +66,7 @@ namespace FlaxEditor.SceneGraph.Actors
public override SceneNode ParentScene => this;
///
- public override void OnContextMenu(ContextMenu contextMenu)
+ public override void OnContextMenu(ContextMenu contextMenu, EditorWindow window)
{
contextMenu.AddSeparator();
var path = Scene.Path;
@@ -80,7 +81,7 @@ namespace FlaxEditor.SceneGraph.Actors
if (Level.ScenesCount > 1)
contextMenu.AddButton("Unload all but this scene", OnUnloadAllButSelectedScene).LinkTooltip("Unloads all of the active scenes except for the selected scene.").Enabled = Editor.Instance.StateMachine.CurrentState.CanChangeScene;
- base.OnContextMenu(contextMenu);
+ base.OnContextMenu(contextMenu, window);
}
private void OnSelect()
diff --git a/Source/Editor/SceneGraph/Actors/SplineNode.cs b/Source/Editor/SceneGraph/Actors/SplineNode.cs
index 1b35cdc35..3b9e8651d 100644
--- a/Source/Editor/SceneGraph/Actors/SplineNode.cs
+++ b/Source/Editor/SceneGraph/Actors/SplineNode.cs
@@ -9,6 +9,7 @@ using Real = System.Single;
using System;
using FlaxEditor.GUI.ContextMenu;
using FlaxEditor.Modules;
+using FlaxEditor.Windows;
using FlaxEngine;
using FlaxEngine.Json;
using Object = FlaxEngine.Object;
@@ -203,9 +204,9 @@ namespace FlaxEditor.SceneGraph.Actors
}
}
- public override void OnContextMenu(ContextMenu contextMenu)
+ public override void OnContextMenu(ContextMenu contextMenu, EditorWindow window)
{
- ParentNode.OnContextMenu(contextMenu);
+ ParentNode.OnContextMenu(contextMenu, window);
}
public static SceneGraphNode Create(StateData state)
@@ -272,9 +273,9 @@ namespace FlaxEditor.SceneGraph.Actors
DebugDraw.DrawSphere(new BoundingSphere(pos, tangentSize), Color.YellowGreen, 0, false);
}
- public override void OnContextMenu(ContextMenu contextMenu)
+ public override void OnContextMenu(ContextMenu contextMenu, EditorWindow window)
{
- ParentNode.OnContextMenu(contextMenu);
+ ParentNode.OnContextMenu(contextMenu, window);
}
public override void OnDispose()
@@ -354,9 +355,9 @@ namespace FlaxEditor.SceneGraph.Actors
}
///
- public override void OnContextMenu(ContextMenu contextMenu)
+ public override void OnContextMenu(ContextMenu contextMenu, EditorWindow window)
{
- base.OnContextMenu(contextMenu);
+ base.OnContextMenu(contextMenu, window);
contextMenu.AddButton("Add spline model", OnAddSplineModel);
contextMenu.AddButton("Add spline collider", OnAddSplineCollider);
diff --git a/Source/Editor/SceneGraph/Actors/StaticModelNode.cs b/Source/Editor/SceneGraph/Actors/StaticModelNode.cs
index 52176ab8b..8de1a7f22 100644
--- a/Source/Editor/SceneGraph/Actors/StaticModelNode.cs
+++ b/Source/Editor/SceneGraph/Actors/StaticModelNode.cs
@@ -3,6 +3,7 @@
using System;
using FlaxEditor.Content;
using FlaxEditor.GUI.ContextMenu;
+using FlaxEditor.Windows;
using FlaxEngine;
namespace FlaxEditor.SceneGraph.Actors
@@ -21,9 +22,9 @@ namespace FlaxEditor.SceneGraph.Actors
}
///
- public override void OnContextMenu(ContextMenu contextMenu)
+ public override void OnContextMenu(ContextMenu contextMenu, EditorWindow window)
{
- base.OnContextMenu(contextMenu);
+ base.OnContextMenu(contextMenu, window);
contextMenu.AddButton("Add collider", OnAddMeshCollider).Enabled = ((StaticModel)Actor).Model != null;
}
diff --git a/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs b/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs
index c4edda65e..d392e8309 100644
--- a/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs
+++ b/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs
@@ -599,7 +599,7 @@ namespace FlaxEditor.SceneGraph.GUI
// Drag scripts
else if (_dragScripts != null && _dragScripts.HasValidDrag)
{
- foreach(var script in _dragScripts.Objects)
+ foreach (var script in _dragScripts.Objects)
{
var customAction = script.HasPrefabLink ? new ReparentAction(script) : null;
using (new UndoBlock(ActorNode.Root.Undo, script, "Change script parent", customAction))
@@ -616,7 +616,7 @@ namespace FlaxEditor.SceneGraph.GUI
var spawnParent = myActor;
if (DragOverMode == DragItemPositioning.Above || DragOverMode == DragItemPositioning.Below)
spawnParent = newParent;
-
+
for (int i = 0; i < _dragAssets.Objects.Count; i++)
{
var item = _dragAssets.Objects[i];
@@ -720,7 +720,7 @@ namespace FlaxEditor.SceneGraph.GUI
for (var i = 0; i < tree.Selection.Count; i++)
{
var e = tree.Selection[i];
-
+
// Skip if parent is already selected to keep correct parenting
if (tree.Selection.Contains(e.Parent))
continue;
diff --git a/Source/Editor/SceneGraph/SceneGraphNode.cs b/Source/Editor/SceneGraph/SceneGraphNode.cs
index a9c350bb2..672a239dc 100644
--- a/Source/Editor/SceneGraph/SceneGraphNode.cs
+++ b/Source/Editor/SceneGraph/SceneGraphNode.cs
@@ -11,6 +11,7 @@ using System.Collections.Generic;
using System.Linq;
using FlaxEditor.Modules;
using FlaxEditor.SceneGraph.Actors;
+using FlaxEditor.Windows;
using FlaxEngine;
namespace FlaxEditor.SceneGraph
@@ -339,7 +340,7 @@ namespace FlaxEditor.SceneGraph
///
/// Called when scene tree window wants to show the context menu. Allows to add custom options.
///
- public virtual void OnContextMenu(FlaxEditor.GUI.ContextMenu.ContextMenu contextMenu)
+ public virtual void OnContextMenu(FlaxEditor.GUI.ContextMenu.ContextMenu contextMenu, EditorWindow window)
{
}
diff --git a/Source/Editor/Surface/Archetypes/Tools.cs b/Source/Editor/Surface/Archetypes/Tools.cs
index 9f6db7ea4..68bea9051 100644
--- a/Source/Editor/Surface/Archetypes/Tools.cs
+++ b/Source/Editor/Surface/Archetypes/Tools.cs
@@ -787,7 +787,7 @@ namespace FlaxEditor.Surface.Archetypes
}
}
- private class AsNode : SurfaceNode
+ internal class AsNode : SurfaceNode
{
private TypePickerControl _picker;
@@ -838,6 +838,15 @@ namespace FlaxEditor.Surface.Archetypes
box.CurrentType = type ? type : ScriptType.FlaxObject;
}
+ ///
+ /// Sets the type of the picker and the type of the output box
+ ///
+ /// Target Type
+ public void SetPickerValue(ScriptType type)
+ {
+ _picker.Value = type;
+ }
+
///
public override void OnDestroy()
{
@@ -999,6 +1008,15 @@ namespace FlaxEditor.Surface.Archetypes
GetBox(4).CurrentType = type ? type : _picker.Type;
}
+ ///
+ /// Sets the type of the picker and the type of the output box
+ ///
+ /// Target Type
+ public void SetPickerValue(ScriptType type)
+ {
+ _picker.Value = type;
+ }
+
///
public override void OnDestroy()
{
diff --git a/Source/Editor/Surface/ContextMenu/VisjectCM.cs b/Source/Editor/Surface/ContextMenu/VisjectCM.cs
index 342429fc8..aa2069ee2 100644
--- a/Source/Editor/Surface/ContextMenu/VisjectCM.cs
+++ b/Source/Editor/Surface/ContextMenu/VisjectCM.cs
@@ -721,9 +721,7 @@ namespace FlaxEditor.Surface.ContextMenu
SelectedItem = previousSelectedItem;
// Scroll into view (without smoothing)
- _panel1.VScrollBar.SmoothingScale = 0;
- _panel1.ScrollViewTo(SelectedItem);
- _panel1.VScrollBar.SmoothingScale = 1;
+ _panel1.ScrollViewTo(SelectedItem, true);
}
return true;
}
diff --git a/Source/Editor/Surface/Elements/Box.cs b/Source/Editor/Surface/Elements/Box.cs
index a03c8f701..ba2009b2a 100644
--- a/Source/Editor/Surface/Elements/Box.cs
+++ b/Source/Editor/Surface/Elements/Box.cs
@@ -820,8 +820,62 @@ namespace FlaxEditor.Surface.Elements
if (useCaster)
{
// Connect via Caster
- //AddCaster(oB, iB);
- throw new NotImplementedException("AddCaster(..) function");
+ const float casterXOffset = 250;
+ if (Surface.Undo != null && Surface.Undo.Enabled)
+ {
+ bool undoEnabled = Surface.Undo.Enabled;
+ Surface.Undo.Enabled = false;
+ SurfaceNode node = Surface.Context.SpawnNode(7, 22, Float2.Zero); // 22 AsNode, 25 CastNode
+ Surface.Undo.Enabled = undoEnabled;
+ if (node is not Archetypes.Tools.AsNode castNode)
+ throw new Exception("Node is not a casting node!");
+
+ // Set the type of the casting node
+ undoEnabled = castNode.Surface.Undo.Enabled;
+ castNode.Surface.Undo.Enabled = false;
+ castNode.SetPickerValue(iB.CurrentType);
+ castNode.Surface.Undo.Enabled = undoEnabled;
+ if (node.GetBox(0) is not OutputBox castOutputBox || node.GetBox(1) is not InputBox castInputBox)
+ throw new NullReferenceException("Casting failed. Cast node is invalid!");
+
+ // We set the position of the cast node here to set it relative to the target nodes input box
+ undoEnabled = castNode.Surface.Undo.Enabled;
+ castNode.Surface.Undo.Enabled = false;
+ var wantedOffset = iB.ParentNode.Location - new Float2(casterXOffset, -(iB.LocalY - castOutputBox.LocalY));
+ castNode.Location = Surface.Root.PointFromParent(ref wantedOffset);
+ castNode.Surface.Undo.Enabled = undoEnabled;
+
+ var spawnNodeAction = new AddRemoveNodeAction(castNode, true);
+
+ var connectToCastNodeAction = new ConnectBoxesAction(castInputBox, oB, true);
+ castInputBox.CreateConnection(oB);
+ connectToCastNodeAction.End();
+
+ var connectCastToTargetNodeAction = new ConnectBoxesAction(iB, castOutputBox, true);
+ iB.CreateConnection(castOutputBox);
+ connectCastToTargetNodeAction.End();
+
+ Surface.AddBatchedUndoAction(new MultiUndoAction(spawnNodeAction, connectToCastNodeAction, connectCastToTargetNodeAction));
+ }
+ else
+ {
+ SurfaceNode node = Surface.Context.SpawnNode(7, 22, Float2.Zero); // 22 AsNode, 25 CastNode
+ if (node is not Archetypes.Tools.AsNode castNode)
+ throw new Exception("Node is not a casting node!");
+
+ // Set the type of the casting node
+ castNode.SetPickerValue(iB.CurrentType);
+ if (node.GetBox(0) is not OutputBox castOutputBox || node.GetBox(1) is not InputBox castInputBox)
+ throw new NullReferenceException("Casting failed. Cast node is invalid!");
+
+ // We set the position of the cast node here to set it relative to the target nodes input box
+ var wantedOffset = iB.ParentNode.Location - new Float2(casterXOffset, -(iB.LocalY - castOutputBox.LocalY));
+ castNode.Location = Surface.Root.PointFromParent(ref wantedOffset);
+
+ castInputBox.CreateConnection(oB);
+ iB.CreateConnection(castOutputBox);
+ }
+ Surface.MarkAsEdited();
}
else
{
diff --git a/Source/Editor/Viewport/EditorViewport.cs b/Source/Editor/Viewport/EditorViewport.cs
index 8f733ce0c..1d7588c19 100644
--- a/Source/Editor/Viewport/EditorViewport.cs
+++ b/Source/Editor/Viewport/EditorViewport.cs
@@ -2,6 +2,7 @@
using System;
using System.Linq;
+using FlaxEditor.Content.Settings;
using FlaxEditor.GUI.ContextMenu;
using FlaxEditor.GUI.Input;
using FlaxEditor.Options;
@@ -9,6 +10,8 @@ using FlaxEditor.Viewport.Cameras;
using FlaxEditor.Viewport.Widgets;
using FlaxEngine;
using FlaxEngine.GUI;
+using Newtonsoft.Json;
+using JsonSerializer = FlaxEngine.Json.JsonSerializer;
namespace FlaxEditor.Viewport
{
@@ -817,10 +820,63 @@ namespace FlaxEditor.Viewport
}
}
+ // View Layers
+ {
+ var viewLayers = ViewWidgetButtonMenu.AddChildMenu("View Layers").ContextMenu;
+ viewLayers.AddButton("Copy layers", () => Clipboard.Text = JsonSerializer.Serialize(Task.View.RenderLayersMask));
+ viewLayers.AddButton("Paste layers", () =>
+ {
+ try
+ {
+ Task.ViewLayersMask = JsonSerializer.Deserialize(Clipboard.Text);
+ }
+ catch
+ {
+ }
+ });
+ viewLayers.AddButton("Reset layers", () => Task.ViewLayersMask = LayersMask.Default).Icon = Editor.Instance.Icons.Rotate32;
+ viewLayers.AddButton("Disable layers", () => Task.ViewLayersMask = new LayersMask(0)).Icon = Editor.Instance.Icons.Rotate32;
+ viewLayers.AddSeparator();
+ var layers = LayersAndTagsSettings.GetCurrentLayers();
+ if (layers != null && layers.Length > 0)
+ {
+ for (int i = 0; i < layers.Length; i++)
+ {
+ var layer = layers[i];
+ var button = viewLayers.AddButton(layer);
+ button.CloseMenuOnClick = false;
+ button.Tag = 1 << i;
+ }
+ }
+ viewLayers.ButtonClicked += button =>
+ {
+ if (button.Tag != null)
+ {
+ int layerIndex = (int)button.Tag;
+ LayersMask mask = new LayersMask(layerIndex);
+ Task.ViewLayersMask ^= mask;
+ button.Icon = (Task.ViewLayersMask & mask) != 0 ? Style.Current.CheckBoxTick : SpriteHandle.Invalid;
+ }
+ };
+ viewLayers.VisibleChanged += WidgetViewLayersShowHide;
+ }
+
// View Flags
{
var viewFlags = ViewWidgetButtonMenu.AddChildMenu("View Flags").ContextMenu;
+ viewFlags.AddButton("Copy flags", () => Clipboard.Text = JsonSerializer.Serialize(Task.ViewFlags));
+ viewFlags.AddButton("Paste flags", () =>
+ {
+ try
+ {
+ Task.ViewFlags = JsonSerializer.Deserialize(Clipboard.Text);
+ }
+ catch
+ {
+ }
+ });
viewFlags.AddButton("Reset flags", () => Task.ViewFlags = ViewFlags.DefaultEditor).Icon = Editor.Instance.Icons.Rotate32;
+ viewFlags.AddButton("Disable flags", () => Task.ViewFlags = ViewFlags.None).Icon = Editor.Instance.Icons.Rotate32;
viewFlags.AddSeparator();
for (int i = 0; i < EditorViewportViewFlagsValues.Length; i++)
{
@@ -835,7 +891,7 @@ namespace FlaxEditor.Viewport
{
var v = (ViewFlags)button.Tag;
Task.ViewFlags ^= v;
- button.Icon = (Task.View.Flags & v) != 0 ? Style.Current.CheckBoxTick : SpriteHandle.Invalid;
+ button.Icon = (Task.ViewFlags & v) != 0 ? Style.Current.CheckBoxTick : SpriteHandle.Invalid;
}
};
viewFlags.VisibleChanged += WidgetViewFlagsShowHide;
@@ -844,6 +900,18 @@ namespace FlaxEditor.Viewport
// Debug View
{
var debugView = ViewWidgetButtonMenu.AddChildMenu("Debug View").ContextMenu;
+ debugView.AddButton("Copy view", () => Clipboard.Text = JsonSerializer.Serialize(Task.ViewMode));
+ debugView.AddButton("Paste view", () =>
+ {
+ try
+ {
+ Task.ViewMode = JsonSerializer.Deserialize(Clipboard.Text);
+ }
+ catch
+ {
+ }
+ });
+ debugView.AddSeparator();
for (int i = 0; i < EditorViewportViewModeValues.Length; i++)
{
ref var v = ref EditorViewportViewModeValues[i];
@@ -1451,9 +1519,9 @@ namespace FlaxEditor.Viewport
_isVirtualMouseRightDown = false; // Cancel when mouse right or escape is pressed
if (_wasVirtualMouseRightDown)
wasControllingMouse = true;
- if (_isVirtualMouseRightDown)
+ if (_isVirtualMouseRightDown)
_isControllingMouse = _isVirtualMouseRightDown;
-
+
if (wasControllingMouse != _isControllingMouse)
{
if (_isControllingMouse)
@@ -1914,6 +1982,24 @@ namespace FlaxEditor.Viewport
}
}
+ private void WidgetViewLayersShowHide(Control cm)
+ {
+ if (cm.Visible == false)
+ return;
+
+ var ccm = (ContextMenu)cm;
+ var layersMask = Task.ViewLayersMask;
+ foreach (var e in ccm.Items)
+ {
+ if (e is ContextMenuButton b && b != null && b.Tag != null)
+ {
+ int layerIndex = (int)b.Tag;
+ LayersMask mask = new LayersMask(layerIndex);
+ b.Icon = (layersMask & mask) != 0 ? Style.Current.CheckBoxTick : SpriteHandle.Invalid;
+ }
+ }
+ }
+
private float GetGamepadAxis(GamepadAxis axis)
{
var value = FlaxEngine.Input.GetGamepadAxis(InputGamepadIndex.All, axis);
diff --git a/Source/Editor/Windows/Assets/AudioClipWindow.cs b/Source/Editor/Windows/Assets/AudioClipWindow.cs
index f5f244c58..8cf31f416 100644
--- a/Source/Editor/Windows/Assets/AudioClipWindow.cs
+++ b/Source/Editor/Windows/Assets/AudioClipWindow.cs
@@ -116,7 +116,7 @@ namespace FlaxEditor.Windows.Assets
_window = window;
// Try to restore target asset AudioClip import options (useful for fast reimport)
- AudioImportSettings.TryRestore(ref ImportSettings, window.Item.Path);
+ Editor.TryRestoreImportOptions(ref ImportSettings.Settings, window.Item.Path);
// Prepare restore data
PeekState();
@@ -134,6 +134,11 @@ namespace FlaxEditor.Windows.Assets
///
public void Reimport()
{
+ if (_window?._previewSource != null)
+ {
+ _window._previewSource.Stop();
+ _window.UpdateToolstrip();
+ }
Editor.Instance.ContentImporting.Reimport((BinaryAssetItem)_window.Item, ImportSettings, true);
}
diff --git a/Source/Editor/Windows/Assets/PrefabWindow.Hierarchy.cs b/Source/Editor/Windows/Assets/PrefabWindow.Hierarchy.cs
index 5a532e362..70aa1dca3 100644
--- a/Source/Editor/Windows/Assets/PrefabWindow.Hierarchy.cs
+++ b/Source/Editor/Windows/Assets/PrefabWindow.Hierarchy.cs
@@ -233,7 +233,7 @@ namespace FlaxEditor.Windows.Assets
contextMenu.AddSeparator();
- b = contextMenu.AddButton("Create Prefab", () => Editor.Prefabs.CreatePrefab(Selection));
+ b = contextMenu.AddButton("Create Prefab", () => Editor.Prefabs.CreatePrefab(Selection, this));
b.Enabled = isSingleActorSelected &&
(Selection[0] as ActorNode).CanCreatePrefab &&
Editor.Windows.ContentWin.CurrentViewFolder.CanHaveAssets;
@@ -313,7 +313,7 @@ namespace FlaxEditor.Windows.Assets
}
if (showCustomNodeOptions)
{
- Selection[0].OnContextMenu(contextMenu);
+ Selection[0].OnContextMenu(contextMenu, this);
}
ContextMenuShow?.Invoke(contextMenu);
diff --git a/Source/Editor/Windows/Assets/PrefabWindow.cs b/Source/Editor/Windows/Assets/PrefabWindow.cs
index 659bab249..53d619b01 100644
--- a/Source/Editor/Windows/Assets/PrefabWindow.cs
+++ b/Source/Editor/Windows/Assets/PrefabWindow.cs
@@ -53,6 +53,11 @@ namespace FlaxEditor.Windows.Assets
///
public PrefabWindowViewport Viewport => _viewport;
+ ///
+ /// Gets the prefab objects properties editor.
+ ///
+ public CustomEditorPresenter Presenter => _propertiesEditor;
+
///
/// Gets the undo system used by this window for changes tracking.
///
@@ -146,7 +151,7 @@ namespace FlaxEditor.Windows.Assets
Graph = new LocalSceneGraph(new CustomRootNode(this));
_tree = new PrefabTree
{
- Margin = new Margin(0.0f, 0.0f, -16.0f, 0.0f), // Hide root node
+ Margin = new Margin(0.0f, 0.0f, -16.0f, _treePanel.ScrollBarsSize), // Hide root node
IsScrollable = true,
};
_tree.AddChild(Graph.Root.TreeNode);
diff --git a/Source/Editor/Windows/SceneTreeWindow.ContextMenu.cs b/Source/Editor/Windows/SceneTreeWindow.ContextMenu.cs
index 63d110368..a99c3ac7d 100644
--- a/Source/Editor/Windows/SceneTreeWindow.ContextMenu.cs
+++ b/Source/Editor/Windows/SceneTreeWindow.ContextMenu.cs
@@ -215,7 +215,7 @@ namespace FlaxEditor.Windows
}
if (showCustomNodeOptions)
{
- Editor.SceneEditing.Selection[0].OnContextMenu(contextMenu);
+ Editor.SceneEditing.Selection[0].OnContextMenu(contextMenu, this);
}
ContextMenuShow?.Invoke(contextMenu);
diff --git a/Source/Editor/Windows/SceneTreeWindow.cs b/Source/Editor/Windows/SceneTreeWindow.cs
index fba052269..cbaa27371 100644
--- a/Source/Editor/Windows/SceneTreeWindow.cs
+++ b/Source/Editor/Windows/SceneTreeWindow.cs
@@ -74,7 +74,7 @@ namespace FlaxEditor.Windows
root.TreeNode.Expand();
_tree = new Tree(true)
{
- Margin = new Margin(0.0f, 0.0f, -16.0f, 0.0f), // Hide root node
+ Margin = new Margin(0.0f, 0.0f, -16.0f, _sceneTreePanel.ScrollBarsSize), // Hide root node
IsScrollable = true,
};
_tree.AddChild(root.TreeNode);
diff --git a/Source/Editor/Windows/Search/ContentFinder.cs b/Source/Editor/Windows/Search/ContentFinder.cs
index 05e1d6792..e19d212b8 100644
--- a/Source/Editor/Windows/Search/ContentFinder.cs
+++ b/Source/Editor/Windows/Search/ContentFinder.cs
@@ -54,9 +54,7 @@ namespace FlaxEditor.Windows.Search
_selectedItem.BackgroundColor = Style.Current.BackgroundSelected;
if (_matchedItems.Count > VisibleItemCount)
{
- _resultPanel.VScrollBar.SmoothingScale = 0;
- _resultPanel.ScrollViewTo(_selectedItem);
- _resultPanel.VScrollBar.SmoothingScale = 1;
+ _resultPanel.ScrollViewTo(_selectedItem, true);
}
}
}
diff --git a/Source/Editor/Windows/SplashScreen.cpp b/Source/Editor/Windows/SplashScreen.cpp
index d2371a540..92894b537 100644
--- a/Source/Editor/Windows/SplashScreen.cpp
+++ b/Source/Editor/Windows/SplashScreen.cpp
@@ -127,6 +127,14 @@ const Char* SplashScreenQuotes[] =
TEXT("Who is signing all these integers?!"),
TEXT("Flax fact: Flax was called Celelej once."),
TEXT("Changing text overflow setti-"),
+ TEXT("Testing tests"),
+ TEXT("Free hugs"),
+ TEXT("Think outside the box"),
+ TEXT("Let's make something fantastic"),
+ TEXT("Be brave"),
+ TEXT("Drum roll please"),
+ TEXT("Good Luck Have Fun"),
+ TEXT("GG Well Played"),
};
SplashScreen::~SplashScreen()
diff --git a/Source/Engine/Audio/AudioClip.cpp b/Source/Engine/Audio/AudioClip.cpp
index 8750bed03..d04da7274 100644
--- a/Source/Engine/Audio/AudioClip.cpp
+++ b/Source/Engine/Audio/AudioClip.cpp
@@ -348,7 +348,7 @@ Asset::LoadResult AudioClip::load()
#if !BUILD_RELEASE
// Validate buffer start times
- if (Math::NotNearEqual(_buffersStartTimes[_totalChunks], GetLength()))
+ if (!Math::NearEqual(_buffersStartTimes[_totalChunks], GetLength(), 1.0f / 60.0f))
{
LOG(Warning, "Invalid audio buffers data size. Expected length: {0}s", GetLength());
for (int32 i = 0; i < _totalChunks + 1; i++)
@@ -454,14 +454,12 @@ bool AudioClip::WriteBuffer(int32 chunkIndex)
}
break;
case AudioFormat::Raw:
- {
data = Span(chunk->Get(), chunk->Size());
- }
- break;
+ break;
default:
return true;
}
- info.NumSamples = data.Length() / bytesPerSample;
+ info.NumSamples = Math::AlignDown(data.Length() / bytesPerSample, info.NumChannels * bytesPerSample);
// Convert to Mono if used as 3D source and backend doesn't support it
if (Is3D() && info.NumChannels > 1 && EnumHasNoneFlags(AudioBackend::Features(), AudioBackend::FeatureFlags::SpatialMultiChannel))
diff --git a/Source/Engine/Audio/AudioSource.cpp b/Source/Engine/Audio/AudioSource.cpp
index 2cee52a8a..afe5c4b49 100644
--- a/Source/Engine/Audio/AudioSource.cpp
+++ b/Source/Engine/Audio/AudioSource.cpp
@@ -183,7 +183,7 @@ void AudioSource::Stop()
float AudioSource::GetTime() const
{
- if (_state == States::Stopped || SourceIDs.IsEmpty())
+ if (_state == States::Stopped || SourceIDs.IsEmpty() || !Clip->IsLoaded())
return 0.0f;
float time = AudioBackend::Source::GetCurrentBufferTime(this);
@@ -265,6 +265,7 @@ void AudioSource::Cleanup()
void AudioSource::OnClipChanged()
{
Stop();
+ _clipChanged = true;
}
void AudioSource::OnClipLoaded()
@@ -318,6 +319,12 @@ void AudioSource::SetNonStreamingBuffer()
void AudioSource::PlayInternal()
{
+ if (_clipChanged && SourceIDs.HasItems())
+ {
+ // If clip was changed between source setup (OnEnable) and actual playback start then ensure to flush any runtime properties with the audio backend
+ _clipChanged = false;
+ AudioBackend::Source::SpatialSetupChanged(this);
+ }
AudioBackend::Source::Play(this);
_isActuallyPlayingSth = true;
@@ -482,6 +489,7 @@ void AudioSource::OnEnable()
{
_prevPos = GetPosition();
_velocity = Vector3::Zero;
+ _clipChanged = false;
Audio::OnAddSource(this);
GetScene()->Ticking.Update.AddTick(this);
diff --git a/Source/Engine/Audio/AudioSource.h b/Source/Engine/Audio/AudioSource.h
index 3b6f32153..70cdb4180 100644
--- a/Source/Engine/Audio/AudioSource.h
+++ b/Source/Engine/Audio/AudioSource.h
@@ -53,6 +53,7 @@ private:
bool _loop;
bool _playOnStart;
bool _allowSpatialization;
+ bool _clipChanged = false;
bool _isActuallyPlayingSth = false;
bool _needToUpdateStreamingBuffers = false;
diff --git a/Source/Engine/Audio/OpenAL/AudioBackendOAL.cpp b/Source/Engine/Audio/OpenAL/AudioBackendOAL.cpp
index 84642ac1f..c905c7eef 100644
--- a/Source/Engine/Audio/OpenAL/AudioBackendOAL.cpp
+++ b/Source/Engine/Audio/OpenAL/AudioBackendOAL.cpp
@@ -228,11 +228,9 @@ namespace ALC
ALenum GetOpenALBufferFormat(uint32 numChannels, uint32 bitDepth)
{
// TODO: cache enum values in Init()??
-
switch (bitDepth)
{
case 8:
- {
switch (numChannels)
{
case 1:
@@ -247,13 +245,8 @@ ALenum GetOpenALBufferFormat(uint32 numChannels, uint32 bitDepth)
return alGetEnumValue("AL_FORMAT_61CHN8");
case 8:
return alGetEnumValue("AL_FORMAT_71CHN8");
- default:
- CRASH;
- return 0;
}
- }
case 16:
- {
switch (numChannels)
{
case 1:
@@ -268,19 +261,22 @@ ALenum GetOpenALBufferFormat(uint32 numChannels, uint32 bitDepth)
return alGetEnumValue("AL_FORMAT_61CHN16");
case 8:
return alGetEnumValue("AL_FORMAT_71CHN16");
- default:
- CRASH;
- return 0;
}
- }
case 32:
- {
switch (numChannels)
{
case 1:
+#ifdef AL_FORMAT_MONO_FLOAT32
+ return AL_FORMAT_MONO_FLOAT32;
+#else
return alGetEnumValue("AL_FORMAT_MONO_FLOAT32");
+#endif
case 2:
+#ifdef AL_FORMAT_STEREO_FLOAT32
+ return AL_FORMAT_STEREO_FLOAT32;
+#else
return alGetEnumValue("AL_FORMAT_STEREO_FLOAT32");
+#endif
case 4:
return alGetEnumValue("AL_FORMAT_QUAD32");
case 6:
@@ -289,15 +285,9 @@ ALenum GetOpenALBufferFormat(uint32 numChannels, uint32 bitDepth)
return alGetEnumValue("AL_FORMAT_61CHN32");
case 8:
return alGetEnumValue("AL_FORMAT_71CHN32");
- default:
- CRASH;
- return 0;
}
}
- default:
- CRASH;
- return 0;
- }
+ return 0;
}
void AudioBackendOAL::Listener_OnAdd(AudioListener* listener)
@@ -607,7 +597,8 @@ void AudioBackendOAL::Buffer_Write(uint32 bufferId, byte* samples, const AudioDa
{
PROFILE_CPU();
- // TODO: maybe use temporary buffers per thread to reduce dynamic allocations when uploading data to OpenAL?
+ // Pick the format for the audio data (it might not be supported natively)
+ ALenum format = GetOpenALBufferFormat(info.NumChannels, info.BitDepth);
// Mono or stereo
if (info.NumChannels <= 2)
@@ -618,28 +609,23 @@ void AudioBackendOAL::Buffer_Write(uint32 bufferId, byte* samples, const AudioDa
{
const uint32 bufferSize = info.NumSamples * sizeof(float);
float* sampleBufferFloat = (float*)Allocator::Allocate(bufferSize);
-
AudioTool::ConvertToFloat(samples, info.BitDepth, sampleBufferFloat, info.NumSamples);
- const ALenum format = GetOpenALBufferFormat(info.NumChannels, info.BitDepth);
+ format = GetOpenALBufferFormat(info.NumChannels, 32);
alBufferData(bufferId, format, sampleBufferFloat, bufferSize, info.SampleRate);
ALC_CHECK_ERROR(alBufferData);
-
Allocator::Free(sampleBufferFloat);
}
else
{
LOG(Warning, "OpenAL doesn't support bit depth larger than 16. Your audio data will be truncated.");
-
const uint32 bufferSize = info.NumSamples * 2;
byte* sampleBuffer16 = (byte*)Allocator::Allocate(bufferSize);
-
AudioTool::ConvertBitDepth(samples, info.BitDepth, sampleBuffer16, 16, info.NumSamples);
- const ALenum format = GetOpenALBufferFormat(info.NumChannels, 16);
+ format = GetOpenALBufferFormat(info.NumChannels, 16);
alBufferData(bufferId, format, sampleBuffer16, bufferSize, info.SampleRate);
ALC_CHECK_ERROR(alBufferData);
-
Allocator::Free(sampleBuffer16);
}
}
@@ -648,19 +634,15 @@ void AudioBackendOAL::Buffer_Write(uint32 bufferId, byte* samples, const AudioDa
// OpenAL expects unsigned 8-bit data, but engine stores it as signed, so convert
const uint32 bufferSize = info.NumSamples * (info.BitDepth / 8);
byte* sampleBuffer = (byte*)Allocator::Allocate(bufferSize);
-
for (uint32 i = 0; i < info.NumSamples; i++)
sampleBuffer[i] = ((int8*)samples)[i] + 128;
- const ALenum format = GetOpenALBufferFormat(info.NumChannels, 16);
alBufferData(bufferId, format, sampleBuffer, bufferSize, info.SampleRate);
ALC_CHECK_ERROR(alBufferData);
-
Allocator::Free(sampleBuffer);
}
- else
+ else if (format)
{
- const ALenum format = GetOpenALBufferFormat(info.NumChannels, info.BitDepth);
alBufferData(bufferId, format, samples, info.NumSamples * (info.BitDepth / 8), info.SampleRate);
ALC_CHECK_ERROR(alBufferData);
}
@@ -675,10 +657,9 @@ void AudioBackendOAL::Buffer_Write(uint32 bufferId, byte* samples, const AudioDa
{
const uint32 bufferSize = info.NumChannels * sizeof(int32);
byte* sampleBuffer32 = (byte*)Allocator::Allocate(bufferSize);
-
AudioTool::ConvertBitDepth(samples, info.BitDepth, sampleBuffer32, 32, info.NumSamples);
- const ALenum format = GetOpenALBufferFormat(info.NumChannels, 32);
+ format = GetOpenALBufferFormat(info.NumChannels, 32);
alBufferData(bufferId, format, sampleBuffer32, bufferSize, info.SampleRate);
ALC_CHECK_ERROR(alBufferData);
@@ -693,19 +674,23 @@ void AudioBackendOAL::Buffer_Write(uint32 bufferId, byte* samples, const AudioDa
for (uint32 i = 0; i < info.NumSamples; i++)
sampleBuffer[i] = ((int8*)samples)[i] + 128;
- const ALenum format = GetOpenALBufferFormat(info.NumChannels, 16);
+ format = GetOpenALBufferFormat(info.NumChannels, 16);
alBufferData(bufferId, format, sampleBuffer, bufferSize, info.SampleRate);
ALC_CHECK_ERROR(alBufferData);
Allocator::Free(sampleBuffer);
}
- else
+ else if (format)
{
- const ALenum format = GetOpenALBufferFormat(info.NumChannels, info.BitDepth);
alBufferData(bufferId, format, samples, info.NumSamples * (info.BitDepth / 8), info.SampleRate);
ALC_CHECK_ERROR(alBufferData);
}
}
+
+ if (!format)
+ {
+ LOG(Error, "Not suppported audio data format for OpenAL device: BitDepth={}, NumChannels={}", info.BitDepth, info.NumChannels);
+ }
}
const Char* AudioBackendOAL::Base_Name()
diff --git a/Source/Engine/Content/Asset.h b/Source/Engine/Content/Asset.h
index 2d2ba4b3f..be188b2b9 100644
--- a/Source/Engine/Content/Asset.h
+++ b/Source/Engine/Content/Asset.h
@@ -227,7 +227,7 @@ protected:
bool onLoad(LoadAssetTask* task);
void onLoaded();
- void onLoaded_MainThread();
+ virtual void onLoaded_MainThread();
virtual void onUnload_MainThread();
#if USE_EDITOR
virtual void onRename(const StringView& newPath) = 0;
diff --git a/Source/Engine/Content/JsonAsset.cpp b/Source/Engine/Content/JsonAsset.cpp
index b48ef02e5..9977a28e1 100644
--- a/Source/Engine/Content/JsonAsset.cpp
+++ b/Source/Engine/Content/JsonAsset.cpp
@@ -367,6 +367,20 @@ void JsonAsset::unload(bool isReloading)
JsonAssetBase::unload(isReloading);
}
+void JsonAsset::onLoaded_MainThread()
+{
+ JsonAssetBase::onLoaded_MainThread();
+
+ // Special case for Settings assets to flush them after edited and saved in Editor
+ const StringAsANSI<> dataTypeNameAnsi(DataTypeName.Get(), DataTypeName.Length());
+ const auto typeHandle = Scripting::FindScriptingType(StringAnsiView(dataTypeNameAnsi.Get(), DataTypeName.Length()));
+ if (Instance && typeHandle && typeHandle.IsSubclassOf(SettingsBase::TypeInitializer) && _isAfterReload)
+ {
+ _isAfterReload = false;
+ ((SettingsBase*)Instance)->Apply();
+ }
+}
+
bool JsonAsset::CreateInstance()
{
ScopeLock lock(Locker);
@@ -409,13 +423,6 @@ bool JsonAsset::CreateInstance()
}
}
- // Special case for Settings assets to flush them after edited and saved in Editor
- if (typeHandle && typeHandle.IsSubclassOf(SettingsBase::TypeInitializer) && _isAfterReload)
- {
- _isAfterReload = false;
- ((SettingsBase*)Instance)->Apply();
- }
-
return false;
}
diff --git a/Source/Engine/Content/JsonAsset.h b/Source/Engine/Content/JsonAsset.h
index 0b6e3ca86..a8e89cec0 100644
--- a/Source/Engine/Content/JsonAsset.h
+++ b/Source/Engine/Content/JsonAsset.h
@@ -150,6 +150,7 @@ protected:
// [JsonAssetBase]
LoadResult loadAsset() override;
void unload(bool isReloading) override;
+ void onLoaded_MainThread() override;
private:
bool CreateInstance();
diff --git a/Source/Engine/ContentImporters/ImportAudio.cpp b/Source/Engine/ContentImporters/ImportAudio.cpp
index 10915b2d1..803bf58a8 100644
--- a/Source/Engine/ContentImporters/ImportAudio.cpp
+++ b/Source/Engine/ContentImporters/ImportAudio.cpp
@@ -18,32 +18,6 @@
#include "Engine/Tools/AudioTool/OggVorbisDecoder.h"
#include "Engine/Tools/AudioTool/OggVorbisEncoder.h"
#include "Engine/Serialization/JsonWriters.h"
-#include "Engine/Scripting/Enums.h"
-
-String ImportAudio::Options::ToString() const
-{
- return String::Format(TEXT("Format:{}, DisableStreaming:{}, Is3D:{}, Quality:{}, BitDepth:{}"), ScriptingEnum::ToString(Format), DisableStreaming, Is3D, Quality, BitDepth);
-}
-
-void ImportAudio::Options::Serialize(SerializeStream& stream, const void* otherObj)
-{
- SERIALIZE_GET_OTHER_OBJ(ImportAudio::Options);
-
- SERIALIZE(Format);
- SERIALIZE(DisableStreaming);
- SERIALIZE(Is3D);
- SERIALIZE(Quality);
- SERIALIZE(BitDepth);
-}
-
-void ImportAudio::Options::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier)
-{
- DESERIALIZE(Format);
- DESERIALIZE(DisableStreaming);
- DESERIALIZE(Is3D);
- DESERIALIZE(Quality);
- DESERIALIZE(BitDepth);
-}
bool ImportAudio::TryGetImportOptions(const StringView& path, Options& options)
{
@@ -90,6 +64,10 @@ CreateAssetResult ImportAudio::Import(CreateAssetContext& context, AudioDecoder&
}
}
+ // Vorbis uses fixed 16-bit depth
+ if (options.Format == AudioFormat::Vorbis)
+ options.BitDepth = AudioTool::BitDepth::_16;
+
LOG_STR(Info, options.ToString());
// Open the file
@@ -112,16 +90,14 @@ CreateAssetResult ImportAudio::Import(CreateAssetContext& context, AudioDecoder&
sampleBuffer.Link(audioData.Get());
// Convert bit depth if need to
- if (options.BitDepth != static_cast(info.BitDepth))
+ uint32 outputBitDepth = (uint32)options.BitDepth;
+ if (outputBitDepth != info.BitDepth)
{
- const uint32 outBufferSize = info.NumSamples * (options.BitDepth / 8);
+ const uint32 outBufferSize = info.NumSamples * (outputBitDepth / 8);
sampleBuffer.Allocate(outBufferSize);
-
- AudioTool::ConvertBitDepth(audioData.Get(), info.BitDepth, sampleBuffer.Get(), options.BitDepth, info.NumSamples);
-
- info.BitDepth = options.BitDepth;
+ AudioTool::ConvertBitDepth(audioData.Get(), info.BitDepth, sampleBuffer.Get(), outputBitDepth, info.NumSamples);
+ info.BitDepth = outputBitDepth;
bytesPerSample = info.BitDepth / 8;
-
bufferSize = outBufferSize;
}
@@ -149,7 +125,7 @@ CreateAssetResult ImportAudio::Import(CreateAssetContext& context, AudioDecoder&
context.Data.Header.Chunks[chunkIndex]->Data.Copy(dataPtr, dataSize);
#define WRITE_DATA(chunkIndex, dataPtr, dataSize) \
- samplesPerChunk[chunkIndex] = (dataSize) / (options.BitDepth / 8); \
+ samplesPerChunk[chunkIndex] = (dataSize) / (outputBitDepth / 8); \
switch (options.Format) \
{ \
case AudioFormat::Raw: \
@@ -183,8 +159,9 @@ CreateAssetResult ImportAudio::Import(CreateAssetContext& context, AudioDecoder&
else
{
// Split audio data into a several chunks (uniform data spread)
- const int32 MinChunkSize = 1 * 1024 * 1024; // 1 MB
- const int32 chunkSize = Math::Max(MinChunkSize, (int32)Math::AlignUp(bufferSize / ASSET_FILE_DATA_CHUNKS, 256));
+ const int32 minChunkSize = 1 * 1024 * 1024; // 1 MB
+ const int32 dataAlignment = info.NumChannels * bytesPerSample; // Ensure to never split samples in-between (eg. 24-bit that uses 3 bytes)
+ const int32 chunkSize = Math::Max(minChunkSize, (int32)Math::AlignUp(bufferSize / ASSET_FILE_DATA_CHUNKS, dataAlignment));
const int32 chunksCount = Math::CeilToInt((float)bufferSize / chunkSize);
ASSERT(chunksCount > 0 && chunksCount <= ASSET_FILE_DATA_CHUNKS);
diff --git a/Source/Engine/ContentImporters/ImportAudio.h b/Source/Engine/ContentImporters/ImportAudio.h
index fb4ee64e3..3f3eda5ed 100644
--- a/Source/Engine/ContentImporters/ImportAudio.h
+++ b/Source/Engine/ContentImporters/ImportAudio.h
@@ -3,12 +3,12 @@
#pragma once
#include "Types.h"
-#include "Engine/Tools/AudioTool/AudioDecoder.h"
-#include "Engine/Core/ISerializable.h"
-#include "Engine/Audio/Config.h"
#if COMPILE_WITH_ASSETS_IMPORTER
+#include "Engine/Tools/AudioTool/AudioTool.h"
+#include "Engine/Tools/AudioTool/AudioDecoder.h"
+
///
/// Enable/disable caching audio import options
///
@@ -20,23 +20,7 @@
class ImportAudio
{
public:
- ///
- /// Importing audio options
- ///
- struct Options : public ISerializable
- {
- AudioFormat Format = AudioFormat::Vorbis;
- bool DisableStreaming = false;
- bool Is3D = false;
- int32 BitDepth = 16;
- float Quality = 0.4f;
-
- String ToString() const;
-
- // [ISerializable]
- void Serialize(SerializeStream& stream, const void* otherObj) override;
- void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) override;
- };
+ typedef AudioTool::Options Options;
public:
///
diff --git a/Source/Engine/Engine/NativeInterop.cs b/Source/Engine/Engine/NativeInterop.cs
index 500636290..f3bb57665 100644
--- a/Source/Engine/Engine/NativeInterop.cs
+++ b/Source/Engine/Engine/NativeInterop.cs
@@ -685,8 +685,10 @@ namespace FlaxEngine.Interop
T value = default;
if (nativePtr == IntPtr.Zero)
return value;
-
- MarshalHelper.ToManaged(ref value, nativePtr, false);
+ if (typeof(T).IsValueType)
+ value = (T)ManagedHandle.FromIntPtr(nativePtr).Target;
+ else
+ MarshalHelper.ToManaged(ref value, nativePtr, false);
return value;
}
diff --git a/Source/Engine/Graphics/Models/ModelData.Tool.cpp b/Source/Engine/Graphics/Models/ModelData.Tool.cpp
index 89a227eb7..d061d0745 100644
--- a/Source/Engine/Graphics/Models/ModelData.Tool.cpp
+++ b/Source/Engine/Graphics/Models/ModelData.Tool.cpp
@@ -171,6 +171,8 @@ int32 FindVertex(const MeshData& mesh, int32 vertexIndex, int32 startIndex, int3
const Float3 vNormal = mesh.Normals.HasItems() ? mesh.Normals[vertexIndex] : Float3::Zero;
const Float3 vTangent = mesh.Tangents.HasItems() ? mesh.Tangents[vertexIndex] : Float3::Zero;
const Float2 vLightmapUV = mesh.LightmapUVs.HasItems() ? mesh.LightmapUVs[vertexIndex] : Float2::Zero;
+ const Color vColor = mesh.Colors.HasItems() ? mesh.Colors[vertexIndex] : Color::Black; // Assuming Color::Black as a default color
+
const int32 end = startIndex + searchRange;
for (size_t i = 0; i < sparialSortCache.size(); i++)
@@ -184,6 +186,8 @@ int32 FindVertex(const MeshData& mesh, int32 vertexIndex, int32 startIndex, int3
const Float3 vNormal = mesh.Normals.HasItems() ? mesh.Normals[vertexIndex] : Float3::Zero;
const Float3 vTangent = mesh.Tangents.HasItems() ? mesh.Tangents[vertexIndex] : Float3::Zero;
const Float2 vLightmapUV = mesh.LightmapUVs.HasItems() ? mesh.LightmapUVs[vertexIndex] : Float2::Zero;
+ const Color vColor = mesh.Colors.HasItems() ? mesh.Colors[vertexIndex] : Color::Black; // Assuming Color::Black as a default color
+
const int32 end = startIndex + searchRange;
for (int32 v = startIndex; v < end; v++)
@@ -201,6 +205,8 @@ int32 FindVertex(const MeshData& mesh, int32 vertexIndex, int32 startIndex, int3
continue;
if (mesh.LightmapUVs.HasItems() && (vLightmapUV - mesh.LightmapUVs[v]).LengthSquared() > uvEpsSqr)
continue;
+ if (mesh.Colors.HasItems() && vColor != mesh.Colors[v])
+ continue;
// TODO: check more components?
return v;
diff --git a/Source/Engine/Graphics/RenderTask.cs b/Source/Engine/Graphics/RenderTask.cs
index a1f90a9c5..fd42c754c 100644
--- a/Source/Engine/Graphics/RenderTask.cs
+++ b/Source/Engine/Graphics/RenderTask.cs
@@ -39,5 +39,19 @@ namespace FlaxEngine
View = view;
}
}
+
+ ///
+ /// The rendering mask for layers. Used to exclude objects from rendering (via property).
+ ///
+ public LayersMask ViewLayersMask
+ {
+ get => View.RenderLayersMask;
+ set
+ {
+ var view = View;
+ view.RenderLayersMask = value;
+ View = view;
+ }
+ }
}
}
diff --git a/Source/Engine/Graphics/RenderView.cpp b/Source/Engine/Graphics/RenderView.cpp
index 46766c34d..efcbb1513 100644
--- a/Source/Engine/Graphics/RenderView.cpp
+++ b/Source/Engine/Graphics/RenderView.cpp
@@ -176,7 +176,7 @@ void RenderView::SetProjector(float nearPlane, float farPlane, const Float3& pos
CullingFrustum = Frustum;
}
-void RenderView::CopyFrom(Camera* camera, Viewport* viewport)
+void RenderView::CopyFrom(const Camera* camera, const Viewport* viewport)
{
const Vector3 cameraPos = camera->GetPosition();
LargeWorlds::UpdateOrigin(Origin, cameraPos);
@@ -192,6 +192,8 @@ void RenderView::CopyFrom(Camera* camera, Viewport* viewport)
Frustum.GetInvMatrix(IVP);
CullingFrustum = Frustum;
RenderLayersMask = camera->RenderLayersMask;
+ Flags = camera->RenderFlags;
+ Mode = camera->RenderMode;
}
void RenderView::GetWorldMatrix(const Transform& transform, Matrix& world) const
diff --git a/Source/Engine/Graphics/RenderView.cs b/Source/Engine/Graphics/RenderView.cs
index 091661514..b770037a3 100644
--- a/Source/Engine/Graphics/RenderView.cs
+++ b/Source/Engine/Graphics/RenderView.cs
@@ -102,6 +102,8 @@ namespace FlaxEngine
NonJitteredProjection = Projection;
TemporalAAJitter = Float4.Zero;
RenderLayersMask = camera.RenderLayersMask;
+ Flags = camera.RenderFlags;
+ Mode = camera.RenderMode;
UpdateCachedData();
}
diff --git a/Source/Engine/Graphics/RenderView.h b/Source/Engine/Graphics/RenderView.h
index 5cfe06a8d..e72ebaa45 100644
--- a/Source/Engine/Graphics/RenderView.h
+++ b/Source/Engine/Graphics/RenderView.h
@@ -295,10 +295,12 @@ public:
/// Camera's FOV angle (in degrees)
void SetProjector(float nearPlane, float farPlane, const Float3& position, const Float3& direction, const Float3& up, float angle);
- // Copy view data from camera
- // @param camera Camera to copy its data
- // @param camera The custom viewport to use for view/projection matrices override.
- void CopyFrom(Camera* camera, Viewport* viewport = nullptr);
+ ///
+ /// Copies view data from camera to the view.
+ ///
+ /// The camera to copy its data.
+ /// The custom viewport to use for view/projection matrices override.
+ void CopyFrom(const Camera* camera, const Viewport* viewport = nullptr);
public:
FORCE_INLINE DrawPass GetShadowsDrawPassMask(ShadowsCastingMode shadowsMode) const
diff --git a/Source/Engine/Level/Actors/Camera.cpp b/Source/Engine/Level/Actors/Camera.cpp
index f2625ca3b..5c5107803 100644
--- a/Source/Engine/Level/Actors/Camera.cpp
+++ b/Source/Engine/Level/Actors/Camera.cpp
@@ -415,6 +415,8 @@ void Camera::Serialize(SerializeStream& stream, const void* otherObj)
SERIALIZE_MEMBER(Far, _far);
SERIALIZE_MEMBER(OrthoScale, _orthoScale);
SERIALIZE(RenderLayersMask);
+ SERIALIZE(RenderFlags);
+ SERIALIZE(RenderMode);
}
void Camera::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier)
@@ -429,6 +431,8 @@ void Camera::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier
DESERIALIZE_MEMBER(Far, _far);
DESERIALIZE_MEMBER(OrthoScale, _orthoScale);
DESERIALIZE(RenderLayersMask);
+ DESERIALIZE(RenderFlags);
+ DESERIALIZE(RenderMode);
}
void Camera::OnEnable()
diff --git a/Source/Engine/Level/Actors/Camera.h b/Source/Engine/Level/Actors/Camera.h
index 5a0bece33..22d950dd4 100644
--- a/Source/Engine/Level/Actors/Camera.h
+++ b/Source/Engine/Level/Actors/Camera.h
@@ -7,6 +7,7 @@
#include "Engine/Core/Math/Viewport.h"
#include "Engine/Core/Math/Ray.h"
#include "Engine/Core/Types/LayersMask.h"
+#include "Engine/Graphics/Enums.h"
#include "Engine/Scripting/ScriptingObjectReference.h"
#if USE_EDITOR
#include "Engine/Content/AssetReference.h"
@@ -134,6 +135,18 @@ public:
API_FIELD(Attributes="EditorOrder(100), EditorDisplay(\"Camera\")")
LayersMask RenderLayersMask;
+ ///
+ /// Frame rendering flags used to switch between graphics features for this camera.
+ ///
+ API_FIELD(Attributes = "EditorOrder(110), EditorDisplay(\"Camera\")")
+ ViewFlags RenderFlags = ViewFlags::DefaultGame;
+
+ ///
+ /// Describes frame rendering modes for this camera.
+ ///
+ API_FIELD(Attributes = "EditorOrder(120), EditorDisplay(\"Camera\")")
+ ViewMode RenderMode = ViewMode::Default;
+
public:
///
/// Projects the point from 3D world-space to game window coordinates (in screen pixels for default viewport calculated from ).
diff --git a/Source/Engine/Level/Prefabs/PrefabManager.cpp b/Source/Engine/Level/Prefabs/PrefabManager.cpp
index a2b148f28..7d3933175 100644
--- a/Source/Engine/Level/Prefabs/PrefabManager.cpp
+++ b/Source/Engine/Level/Prefabs/PrefabManager.cpp
@@ -321,6 +321,8 @@ bool PrefabManager::CreatePrefab(Actor* targetActor, const StringView& outputPat
// Serialize to json data
ASSERT(!IsCreatingPrefab);
IsCreatingPrefab = true;
+ const Guid targetPrefabId = targetActor->GetPrefabID();
+ const bool hasTargetPrefabId = targetPrefabId.IsValid();
rapidjson_flax::StringBuffer actorsDataBuffer;
{
CompactJsonWriter writerObj(actorsDataBuffer);
@@ -329,7 +331,27 @@ bool PrefabManager::CreatePrefab(Actor* targetActor, const StringView& outputPat
for (int32 i = 0; i < sceneObjects->Count(); i++)
{
SceneObject* obj = sceneObjects->At(i);
+
+ // Detect when creating prefab from object that is already part of prefab then serialize it as unlinked
+ const Guid prefabId = obj->GetPrefabID();
+ const Guid prefabObjectId = obj->GetPrefabObjectID();
+ bool isObjectFromPrefab = targetPrefabId == prefabId && prefabId.IsValid(); // Allow to use other nested prefabs properly (ignore only root object's prefab link)
+ if (isObjectFromPrefab)
+ {
+ //obj->BreakPrefabLink();
+ obj->_prefabID = Guid::Empty;
+ obj->_prefabObjectID = Guid::Empty;
+ }
+
writer.SceneObject(obj);
+
+ // Restore broken link
+ if (hasTargetPrefabId)
+ {
+ //obj->LinkPrefab(prefabId, prefabObjectId);
+ obj->_prefabID = prefabId;
+ obj->_prefabObjectID = prefabObjectId;
+ }
}
writer.EndArray();
}
@@ -395,7 +417,6 @@ bool PrefabManager::CreatePrefab(Actor* targetActor, const StringView& outputPat
{
SceneObject* obj = sceneObjects->At(i);
Guid prefabObjectId;
-
if (objectInstanceIdToPrefabObjectId.TryGet(obj->GetSceneObjectId(), prefabObjectId))
{
obj->LinkPrefab(assetInfo.ID, prefabObjectId);
diff --git a/Source/Engine/Platform/Linux/LinuxPlatform.cpp b/Source/Engine/Platform/Linux/LinuxPlatform.cpp
index ab314ab72..6195215e3 100644
--- a/Source/Engine/Platform/Linux/LinuxPlatform.cpp
+++ b/Source/Engine/Platform/Linux/LinuxPlatform.cpp
@@ -2707,8 +2707,34 @@ Float2 LinuxPlatform::GetDesktopSize()
Rectangle LinuxPlatform::GetMonitorBounds(const Float2& screenPos)
{
- // TODO: do it in a proper way
- return Rectangle(Float2::Zero, GetDesktopSize());
+ if (!xDisplay)
+ return Rectangle::Empty;
+
+ int event, err;
+ const bool ok = X11::XineramaQueryExtension(xDisplay, &event, &err);
+ if (!ok)
+ return Rectangle::Empty;
+
+ int count;
+ int screenIdx = 0;
+ X11::XineramaScreenInfo* xsi = X11::XineramaQueryScreens(xDisplay, &count);
+ if (screenIdx >= count)
+ return Rectangle::Empty;
+ // find the screen for this screenPos
+ for (int i = 0; i < count; i++)
+ {
+ if (screenPos.X >= xsi[i].x_org && screenPos.X < xsi[i].x_org+xsi[i].width
+ && screenPos.Y >= xsi[i].y_org && screenPos.Y < xsi[i].y_org+xsi[i].height)
+ {
+ screenIdx = i;
+ break;
+ }
+ }
+
+ Float2 org((float)xsi[screenIdx].x_org, (float)xsi[screenIdx].y_org);
+ Float2 size((float)xsi[screenIdx].width, (float)xsi[screenIdx].height);
+ X11::XFree(xsi);
+ return Rectangle(org, size);
}
Rectangle LinuxPlatform::GetVirtualDesktopBounds()
diff --git a/Source/Engine/Platform/Linux/LinuxWindow.cpp b/Source/Engine/Platform/Linux/LinuxWindow.cpp
index 7694836cb..e6fe63f9c 100644
--- a/Source/Engine/Platform/Linux/LinuxWindow.cpp
+++ b/Source/Engine/Platform/Linux/LinuxWindow.cpp
@@ -153,7 +153,11 @@ LinuxWindow::LinuxWindow(const CreateWindowSettings& settings)
hints.max_height = (int)settings.MaximumSize.Y;
hints.flags |= USSize;
}
- X11::XSetNormalHints(display, window, &hints);
+ // honor the WM placement except for manual (overriding) placements
+ if (settings.StartPosition == WindowStartPosition::Manual)
+ {
+ X11::XSetNormalHints(display, window, &hints);
+ }
// Ensures the child window is always on top of the parent window
if (settings.Parent)
diff --git a/Source/Engine/Serialization/JsonTools.cpp b/Source/Engine/Serialization/JsonTools.cpp
index c395df4f1..3cbe8f65c 100644
--- a/Source/Engine/Serialization/JsonTools.cpp
+++ b/Source/Engine/Serialization/JsonTools.cpp
@@ -81,7 +81,6 @@ void JsonTools::ChangeIds(Document& doc, const Dictionary& mapping)
::ChangeIds(doc, doc, mapping);
}
-
Float2 JsonTools::GetFloat2(const Value& value)
{
Float2 result;
diff --git a/Source/Engine/Tools/AudioTool/AudioTool.cpp b/Source/Engine/Tools/AudioTool/AudioTool.cpp
index a5a5d4663..3136a5a0d 100644
--- a/Source/Engine/Tools/AudioTool/AudioTool.cpp
+++ b/Source/Engine/Tools/AudioTool/AudioTool.cpp
@@ -1,14 +1,49 @@
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
+#if COMPILE_WITH_AUDIO_TOOL
+
#include "AudioTool.h"
#include "Engine/Core/Core.h"
#include "Engine/Core/Memory/Allocation.h"
+#if USE_EDITOR
+#include "Engine/Serialization/Serialization.h"
+#include "Engine/Scripting/Enums.h"
+#endif
#define CONVERT_TO_MONO_AVG 1
#if !CONVERT_TO_MONO_AVG
#include "Engine/Core/Math/Math.h"
#endif
+#if USE_EDITOR
+
+String AudioTool::Options::ToString() const
+{
+ return String::Format(TEXT("Format:{}, DisableStreaming:{}, Is3D:{}, Quality:{}, BitDepth:{}"), ScriptingEnum::ToString(Format), DisableStreaming, Is3D, Quality, (int32)BitDepth);
+}
+
+void AudioTool::Options::Serialize(SerializeStream& stream, const void* otherObj)
+{
+ SERIALIZE_GET_OTHER_OBJ(AudioTool::Options);
+
+ SERIALIZE(Format);
+ SERIALIZE(DisableStreaming);
+ SERIALIZE(Is3D);
+ SERIALIZE(Quality);
+ SERIALIZE(BitDepth);
+}
+
+void AudioTool::Options::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier)
+{
+ DESERIALIZE(Format);
+ DESERIALIZE(DisableStreaming);
+ DESERIALIZE(Is3D);
+ DESERIALIZE(Quality);
+ DESERIALIZE(BitDepth);
+}
+
+#endif
+
void ConvertToMono8(const int8* input, uint8* output, uint32 numSamples, uint32 numChannels)
{
for (uint32 i = 0; i < numSamples; i++)
@@ -231,8 +266,7 @@ void AudioTool::ConvertToFloat(const byte* input, uint32 inBitDepth, float* outp
for (uint32 i = 0; i < numSamples; i++)
{
const int8 sample = *(int8*)input;
- output[i] = sample / 127.0f;
-
+ output[i] = sample * (1.0f / 127.0f);
input++;
}
}
@@ -241,8 +275,7 @@ void AudioTool::ConvertToFloat(const byte* input, uint32 inBitDepth, float* outp
for (uint32 i = 0; i < numSamples; i++)
{
const int16 sample = *(int16*)input;
- output[i] = sample / 32767.0f;
-
+ output[i] = sample * (1.0f / 32767.0f);
input += 2;
}
}
@@ -251,8 +284,7 @@ void AudioTool::ConvertToFloat(const byte* input, uint32 inBitDepth, float* outp
for (uint32 i = 0; i < numSamples; i++)
{
const int32 sample = Convert24To32Bits(input);
- output[i] = sample / 2147483647.0f;
-
+ output[i] = sample * (1.0f / 2147483647.0f);
input += 3;
}
}
@@ -261,8 +293,7 @@ void AudioTool::ConvertToFloat(const byte* input, uint32 inBitDepth, float* outp
for (uint32 i = 0; i < numSamples; i++)
{
const int32 sample = *(int32*)input;
- output[i] = sample / 2147483647.0f;
-
+ output[i] = sample * (1.0f / 2147483647.0f);
input += 4;
}
}
@@ -278,7 +309,8 @@ void AudioTool::ConvertFromFloat(const float* input, int32* output, uint32 numSa
{
const float sample = *(float*)input;
output[i] = static_cast(sample * 2147483647.0f);
-
input++;
}
}
+
+#endif
diff --git a/Source/Engine/Tools/AudioTool/AudioTool.h b/Source/Engine/Tools/AudioTool/AudioTool.h
index b9525ffa7..d7e28df57 100644
--- a/Source/Engine/Tools/AudioTool/AudioTool.h
+++ b/Source/Engine/Tools/AudioTool/AudioTool.h
@@ -2,16 +2,86 @@
#pragma once
+#if COMPILE_WITH_AUDIO_TOOL
+
#include "Engine/Core/Config.h"
#include "Engine/Core/Types/BaseTypes.h"
+#if USE_EDITOR
+#include "Engine/Core/ISerializable.h"
+#endif
+#include "Engine/Audio/Types.h"
///
/// Audio data importing and processing utilities.
///
-class FLAXENGINE_API AudioTool
+API_CLASS(Namespace="FlaxEngine.Tools", Static) class FLAXENGINE_API AudioTool
{
-public:
+ DECLARE_SCRIPTING_TYPE_MINIMAL(AudioTool);
+#if USE_EDITOR
+
+public:
+ ///
+ /// Declares the imported audio clip bit depth.
+ ///
+ API_ENUM(Attributes="HideInEditor") enum class BitDepth : int32
+ {
+ // 8-bits per sample.
+ _8 = 8,
+ // 16-bits per sample.
+ _16 = 16,
+ // 24-bits per sample.
+ _24 = 24,
+ // 32-bits per sample.
+ _32 = 32,
+ };
+
+ ///
+ /// Audio import options.
+ ///
+ API_STRUCT(Attributes="HideInEditor") struct FLAXENGINE_API Options : public ISerializable
+ {
+ DECLARE_SCRIPTING_TYPE_MINIMAL(Options);
+
+ ///
+ /// The audio data format to import the audio clip as.
+ ///
+ API_FIELD(Attributes="EditorOrder(10)")
+ AudioFormat Format = 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.
+ ///
+ API_FIELD(Attributes="EditorOrder(20), EditorDisplay(null, \"Compression Quality\"), Limit(0, 1, 0.01f)")
+ float Quality = 0.4f;
+
+ ///
+ /// Disables dynamic audio streaming. The whole clip will be loaded into the memory. Useful for small clips (eg. gunfire sounds).
+ ///
+ API_FIELD(Attributes="EditorOrder(30)")
+ bool DisableStreaming = false;
+
+ ///
+ /// Checks should the clip be played as spatial (3D) audio or as normal audio. 3D audio is stored in Mono format.
+ ///
+ API_FIELD(Attributes="EditorOrder(40), EditorDisplay(null, \"Is 3D\")")
+ bool Is3D = false;
+
+ ///
+ /// The size of a single sample in bits. The clip will be converted to this bit depth on import.
+ ///
+ API_FIELD(Attributes="EditorOrder(50), VisibleIf(nameof(ShowBtiDepth))")
+ BitDepth BitDepth = BitDepth::_16;
+
+ String ToString() const;
+
+ // [ISerializable]
+ void Serialize(SerializeStream& stream, const void* otherObj) override;
+ void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) override;
+ };
+#endif
+
+public:
///
/// Converts a set of audio samples using multiple channels into a set of mono samples.
///
@@ -59,3 +129,5 @@ public:
return (input[2] << 24) | (input[1] << 16) | (input[0] << 8);
}
};
+
+#endif
diff --git a/Source/Engine/UI/GUI/Common/Button.cs b/Source/Engine/UI/GUI/Common/Button.cs
index b50f3dd46..02337411b 100644
--- a/Source/Engine/UI/GUI/Common/Button.cs
+++ b/Source/Engine/UI/GUI/Common/Button.cs
@@ -314,6 +314,28 @@ namespace FlaxEngine.GUI
return false;
}
+ ///
+ public override bool OnMouseDoubleClick(Float2 location, MouseButton button)
+ {
+ if (base.OnMouseDoubleClick(location, button))
+ return true;
+
+ if (button == MouseButton.Left && _isPressed)
+ {
+ OnPressEnd();
+ OnClick();
+ return true;
+ }
+
+ if (button == MouseButton.Left && !_isPressed)
+ {
+ OnPressBegin();
+ return true;
+ }
+
+ return false;
+ }
+
///
public override bool OnTouchDown(Float2 location, int pointerId)
{
diff --git a/Source/Engine/UI/GUI/Common/CheckBox.cs b/Source/Engine/UI/GUI/Common/CheckBox.cs
index 2f1ff42a9..2128c0311 100644
--- a/Source/Engine/UI/GUI/Common/CheckBox.cs
+++ b/Source/Engine/UI/GUI/Common/CheckBox.cs
@@ -277,6 +277,27 @@ namespace FlaxEngine.GUI
return base.OnMouseDown(location, button);
}
+ ///
+ public override bool OnMouseDoubleClick(Float2 location, MouseButton button)
+ {
+ if (button == MouseButton.Left && !_isPressed)
+ {
+ OnPressBegin();
+ return true;
+ }
+
+ if (button == MouseButton.Left && _isPressed)
+ {
+ OnPressEnd();
+ if (_box.Contains(ref location))
+ {
+ OnClick();
+ return true;
+ }
+ }
+ return base.OnMouseDoubleClick(location, button);
+ }
+
///
public override bool OnMouseUp(Float2 location, MouseButton button)
{
diff --git a/Source/Engine/UI/GUI/Common/Dropdown.cs b/Source/Engine/UI/GUI/Common/Dropdown.cs
index 6ad962d6d..aea4e173a 100644
--- a/Source/Engine/UI/GUI/Common/Dropdown.cs
+++ b/Source/Engine/UI/GUI/Common/Dropdown.cs
@@ -666,6 +666,30 @@ namespace FlaxEngine.GUI
return false;
}
+ ///
+ public override bool OnMouseDoubleClick(Float2 location, MouseButton button)
+ {
+ if (base.OnMouseDoubleClick(location, button))
+ return true;
+
+ if (_touchDown && button == MouseButton.Left)
+ {
+ _touchDown = false;
+ ShowPopup();
+ return true;
+ }
+
+ if (button == MouseButton.Left)
+ {
+ _touchDown = true;
+ if (!IsPopupOpened)
+ Focus();
+ return true;
+ }
+
+ return false;
+ }
+
///
public override bool OnTouchDown(Float2 location, int pointerId)
{
diff --git a/Source/Engine/UI/GUI/Panels/DropPanel.cs b/Source/Engine/UI/GUI/Panels/DropPanel.cs
index d650d6205..66e7413eb 100644
--- a/Source/Engine/UI/GUI/Panels/DropPanel.cs
+++ b/Source/Engine/UI/GUI/Panels/DropPanel.cs
@@ -501,6 +501,29 @@ namespace FlaxEngine.GUI
return false;
}
+ ///
+ public override bool OnMouseDoubleClick(Float2 location, MouseButton button)
+ {
+ if (base.OnMouseDoubleClick(location, button))
+ return true;
+
+ _mouseOverHeader = HeaderRectangle.Contains(location);
+ if (button == MouseButton.Left && _mouseOverHeader)
+ {
+ _mouseButtonLeftDown = true;
+ return true;
+ }
+
+ if (button == MouseButton.Left && _mouseButtonLeftDown)
+ {
+ _mouseButtonLeftDown = false;
+ if (_mouseOverHeader)
+ Toggle();
+ return true;
+ }
+ return false;
+ }
+
///
public override void OnMouseLeave()
{
diff --git a/Source/Engine/UI/GUI/Panels/ScrollBar.cs b/Source/Engine/UI/GUI/Panels/ScrollBar.cs
index a51a1df40..abde37b37 100644
--- a/Source/Engine/UI/GUI/Panels/ScrollBar.cs
+++ b/Source/Engine/UI/GUI/Panels/ScrollBar.cs
@@ -23,9 +23,9 @@ namespace FlaxEngine.GUI
// Scrolling
- private float _clickChange = 20, _scrollChange = 100;
+ private float _clickChange = 20, _scrollChange = 50;
private float _minimum, _maximum = 100;
- private float _value, _targetValue;
+ private float _startValue, _value, _targetValue;
private readonly Orientation _orientation;
private RootControl.UpdateDelegate _update;
@@ -42,6 +42,7 @@ namespace FlaxEngine.GUI
// Smoothing
private float _thumbOpacity = DefaultMinimumOpacity;
+ private float _scrollAnimationProgress = 0f;
///
/// Gets the orientation.
@@ -59,14 +60,19 @@ namespace FlaxEngine.GUI
public float TrackThickness { get; set; } = 2.0f;
///
- /// Gets or sets the value smoothing scale (0 to not use it).
+ /// The maximum time it takes to animate from current to target scroll position
///
- public float SmoothingScale { get; set; } = 0.6f;
+ public float ScrollAnimationDuration { get; set; } = 0.18f;
///
/// Gets a value indicating whether use scroll value smoothing.
///
- public bool UseSmoothing => !Mathf.IsZero(SmoothingScale);
+ public bool UseSmoothing => EnableSmoothing && !Mathf.IsZero(ScrollAnimationDuration);
+
+ ///
+ /// Enables scroll smoothing
+ ///
+ public bool EnableSmoothing { get; set; } = true;
///
/// Gets or sets the minimum value.
@@ -112,11 +118,15 @@ namespace FlaxEngine.GUI
if (!Mathf.NearEqual(value, _targetValue))
{
_targetValue = value;
+ _startValue = _value;
+ _scrollAnimationProgress = 0f;
// Check if skip smoothing
if (!UseSmoothing)
{
_value = value;
+ _startValue = value;
+ _scrollAnimationProgress = 1f;
OnValueChanged();
}
else
@@ -208,7 +218,8 @@ namespace FlaxEngine.GUI
{
if (!Mathf.NearEqual(_value, _targetValue))
{
- _value = _targetValue;
+ _value = _targetValue = _startValue;
+ _scrollAnimationProgress = 0f;
SetUpdate(ref _update, null);
OnValueChanged();
}
@@ -274,7 +285,8 @@ namespace FlaxEngine.GUI
internal void Reset()
{
- _value = _targetValue = 0;
+ _value = _targetValue = _startValue = 0;
+ _scrollAnimationProgress = 0f;
}
///
@@ -296,22 +308,39 @@ namespace FlaxEngine.GUI
_thumbOpacity = isDeltaSlow ? targetOpacity : Mathf.Lerp(_thumbOpacity, targetOpacity, deltaTime * 10.0f);
bool needUpdate = Mathf.Abs(_thumbOpacity - targetOpacity) > 0.001f;
- // Ensure scroll bar is visible
- if (Visible)
+ // Ensure scroll bar is visible and smoothing is required
+ if (Visible && Mathf.Abs(_targetValue - _value) > 0.01f)
{
- // Value smoothing
- if (Mathf.Abs(_targetValue - _value) > 0.01f)
+ // Interpolate or not if running slow
+ float value;
+ if (!isDeltaSlow && UseSmoothing)
{
- // Interpolate or not if running slow
- float value;
- if (!isDeltaSlow && UseSmoothing)
- value = Mathf.Lerp(_value, _targetValue, deltaTime * 20.0f * SmoothingScale);
- else
- value = _targetValue;
- _value = value;
- OnValueChanged();
- needUpdate = true;
+ // percentage of scroll from 0 to _scrollChange, ex. 0.5 at _scrollChange / 2
+ var minScrollChangeRatio = Mathf.Clamp(Mathf.Abs(_targetValue - _startValue) / _scrollChange, 0, 1);
+
+ // shorten the duration if we scrolled less than _scrollChange
+ var actualDuration = ScrollAnimationDuration * minScrollChangeRatio;
+ var step = deltaTime / actualDuration;
+
+ var progress = _scrollAnimationProgress;
+ progress = Mathf.Clamp(progress + step, 0, 1);
+
+ // https://easings.net/#easeOutSine
+ var easedProgress = Mathf.Sin((progress * Mathf.Pi) / 2);
+ value = Mathf.Lerp(_startValue, _targetValue, easedProgress);
+
+ _scrollAnimationProgress = progress;
}
+ else
+ {
+ value = _targetValue;
+ _startValue = _targetValue;
+ _scrollAnimationProgress = 0f;
+ }
+
+ _value = value;
+ OnValueChanged();
+ needUpdate = true;
}
// End updating if all animations are done
@@ -371,7 +400,7 @@ namespace FlaxEngine.GUI
float mousePosition = _orientation == Orientation.Vertical ? slidePosition.Y : slidePosition.X;
float percentage = (mousePosition - _mouseOffset - _thumbSize / 2) / (TrackSize - _thumbSize);
- Value = _minimum + percentage * (_maximum - _minimum);
+ TargetValue = _minimum + percentage * (_maximum - _minimum);
}
}
@@ -381,7 +410,7 @@ namespace FlaxEngine.GUI
if (ThumbEnabled)
{
// Scroll
- Value = _value - delta * _scrollChange;
+ Value = _targetValue - delta * _scrollChange;
}
return true;
}
diff --git a/Source/Tools/Flax.Build/Build/Assembler.cs b/Source/Tools/Flax.Build/Build/Assembler.cs
index be4990087..cbe92dd8d 100644
--- a/Source/Tools/Flax.Build/Build/Assembler.cs
+++ b/Source/Tools/Flax.Build/Build/Assembler.cs
@@ -154,7 +154,7 @@ namespace Flax.Build
// Run the compilation
using var memoryStream = new MemoryStream();
- CSharpParseOptions parseOptions = CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp9).WithPreprocessorSymbols(PreprocessorSymbols);
+ CSharpParseOptions parseOptions = CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.Latest).WithPreprocessorSymbols(PreprocessorSymbols);
var syntaxTrees = new List();
foreach (var sourceFile in SourceFiles)
{
diff --git a/Source/Tools/Flax.Build/Projects/VisualStudio/VisualStudioProjectGenerator.cs b/Source/Tools/Flax.Build/Projects/VisualStudio/VisualStudioProjectGenerator.cs
index a344eea50..1ba8e6709 100644
--- a/Source/Tools/Flax.Build/Projects/VisualStudio/VisualStudioProjectGenerator.cs
+++ b/Source/Tools/Flax.Build/Projects/VisualStudio/VisualStudioProjectGenerator.cs
@@ -641,6 +641,44 @@ namespace Flax.Build.Projects.VisualStudio
File.WriteAllText(e.Key, profile.ToString(), Encoding.UTF8);
}
}
+
+ // Generate Rider-specific configuration files
+ {
+ StringBuilder dotSettingsFileContent = new StringBuilder();
+ string dotSettingsUserFilePath = solution.Path + ".DotSettings.user";
+
+ // Solution settings (user layer)
+ bool useResharperBuild = false; // This needs to be disabled for custom build steps to run properly
+
+ if (File.Exists(dotSettingsUserFilePath))
+ {
+ foreach (var line in File.ReadAllLines(dotSettingsUserFilePath))
+ {
+ if (line.Contains(@"/UseMsbuildSolutionBuilder/@EntryValue"))
+ {
+ if (!useResharperBuild)
+ {
+ dotSettingsFileContent.Append("\t").Append(@"No");
+ if (line.Contains(""))
+ dotSettingsFileContent.Append("\n");
+ else
+ dotSettingsFileContent.Append("\n");
+ }
+ continue;
+ }
+ dotSettingsFileContent.Append(line).Append("\n");
+ }
+ }
+ else
+ {
+ dotSettingsFileContent.Append(@"").Append("\n");
+ if (!useResharperBuild)
+ dotSettingsFileContent.Append("\t").Append(@"No");
+ dotSettingsFileContent.Append("\n");
+ }
+
+ Utilities.WriteFileIfChanged(dotSettingsUserFilePath, dotSettingsFileContent.ToString());
+ }
}
///