Merge branch 'FlaxEngine:master' into Debug
This commit is contained in:
Binary file not shown.
@@ -2,35 +2,35 @@
|
||||
using System.Collections.Generic;
|
||||
using FlaxEngine;
|
||||
|
||||
namespace %namespace%
|
||||
namespace %namespace%;
|
||||
|
||||
/// <summary>
|
||||
/// %class% Script.
|
||||
/// </summary>
|
||||
public class %class% : Script
|
||||
{
|
||||
/// <summary>
|
||||
/// %class% Script.
|
||||
/// </summary>
|
||||
public class %class% : Script
|
||||
/// <inheritdoc/>
|
||||
public override void OnStart()
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override void OnStart()
|
||||
{
|
||||
// Here you can add code that needs to be called when script is created, just before the first game update
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
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
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void OnEnable()
|
||||
{
|
||||
// Here you can add code that needs to be called when script is enabled (eg. register for events)
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void OnDisable()
|
||||
{
|
||||
// Here you can add code that needs to be called when script is disabled (eg. unregister from events)
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public override void OnDisable()
|
||||
{
|
||||
// Here you can add code that needs to be called when script is disabled (eg. unregister from events)
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void OnUpdate()
|
||||
{
|
||||
// Here you can add code that needs to be called every frame
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public override void OnUpdate()
|
||||
{
|
||||
// Here you can add code that needs to be called every frame
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,16 +11,6 @@ for %%I in (Source\Logo.png) do if %%~zI LSS 2000 (
|
||||
call "Development\Scripts\Windows\GetMSBuildPath.bat"
|
||||
if errorlevel 1 goto Error_NoVisualStudioEnvironment
|
||||
|
||||
if not exist "%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" goto Compile
|
||||
for /f "delims=" %%i in ('"%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere" -latest -products * -requires Microsoft.Component.MSBuild -property installationPath') do (
|
||||
for %%j in (15.0, Current) do (
|
||||
if exist "%%i\MSBuild\%%j\Bin\MSBuild.exe" (
|
||||
set MSBUILD_PATH="%%i\MSBuild\%%j\Bin\MSBuild.exe"
|
||||
goto Compile
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
:Compile
|
||||
md Cache\Intermediate >nul 2>nul
|
||||
dir /s /b Source\Tools\Flax.Build\*.cs >Cache\Intermediate\Flax.Build.Files.txt
|
||||
@@ -44,7 +34,7 @@ goto Exit
|
||||
echo CallBuildTool ERROR: The script is in invalid directory.
|
||||
goto Exit
|
||||
:Error_NoVisualStudioEnvironment
|
||||
echo CallBuildTool ERROR: Missing Visual Studio 2015 or newer.
|
||||
echo CallBuildTool ERROR: Missing Visual Studio 2022 or newer.
|
||||
goto Exit
|
||||
:Error_CompilationFailed
|
||||
echo CallBuildTool ERROR: Failed to compile Flax.Build project.
|
||||
|
||||
@@ -4,66 +4,26 @@ rem Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
|
||||
|
||||
set MSBUILD_PATH=
|
||||
|
||||
rem Look for MSBuild version 17.0 or later
|
||||
if not exist "%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" goto VsWhereNotFound
|
||||
for /f "delims=" %%i in ('"%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere" -latest -products * -requires Microsoft.Component.MSBuild -property installationPath') do (
|
||||
if exist "%%i\MSBuild\15.0\Bin\MSBuild.exe" (
|
||||
set MSBUILD_PATH="%%i\MSBuild\15.0\Bin\MSBuild.exe"
|
||||
goto End
|
||||
)
|
||||
)
|
||||
for /f "delims=" %%i in ('"%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere" -latest -prerelease -products * -requires Microsoft.Component.MSBuild -property installationPath') do (
|
||||
if exist "%%i\MSBuild\15.0\Bin\MSBuild.exe" (
|
||||
set MSBUILD_PATH="%%i\MSBuild\15.0\Bin\MSBuild.exe"
|
||||
goto End
|
||||
)
|
||||
for /f "delims=" %%i in ('"%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere" -version 17.0 -latest -products * -requires Microsoft.Component.MSBuild -property installationPath') do (
|
||||
if exist "%%i\MSBuild\Current\Bin\MSBuild.exe" (
|
||||
set MSBUILD_PATH="%%i\MSBuild\Current\Bin\MSBuild.exe"
|
||||
goto End
|
||||
)
|
||||
)
|
||||
:VsWhereNotFound
|
||||
|
||||
if exist "%ProgramFiles(x86)%\MSBuild\14.0\bin\MSBuild.exe" (
|
||||
set MSBUILD_PATH="%ProgramFiles(x86)%\MSBuild\14.0\bin\MSBuild.exe"
|
||||
goto End
|
||||
rem Look for MSBuild version 17.0 or later in pre-release versions
|
||||
for /f "delims=" %%i in ('"%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere" -version 17.0 -latest -prerelease -products * -requires Microsoft.Component.MSBuild -property installationPath') do (
|
||||
if exist "%%i\MSBuild\Current\Bin\MSBuild.exe" (
|
||||
set MSBUILD_PATH="%%i\MSBuild\Current\Bin\MSBuild.exe"
|
||||
goto End
|
||||
)
|
||||
)
|
||||
|
||||
call :GetInstallPath Microsoft\VisualStudio\SxS\VS7 15.0 MSBuild\15.0\bin\MSBuild.exe
|
||||
if not errorlevel 1 goto End
|
||||
call :GetInstallPath Microsoft\MSBuild\ToolsVersions\14.0 MSBuildToolsPath MSBuild.exe
|
||||
if not errorlevel 1 goto End
|
||||
call :GetInstallPath Microsoft\MSBuild\ToolsVersions\12.0 MSBuildToolsPath MSBuild.exe
|
||||
if not errorlevel 1 goto End
|
||||
call :GetInstallPath Microsoft\MSBuild\ToolsVersions\4.0 MSBuildToolsPath MSBuild.exe
|
||||
if not errorlevel 1 goto End
|
||||
|
||||
echo GetMSBuildPath ERROR: Could not find MSBuild version 17.0 or later.
|
||||
exit /B 1
|
||||
:VsWhereNotFound
|
||||
echo GetMSBuildPath ERROR: vswhere.exe was not found.
|
||||
exit /B 1
|
||||
:End
|
||||
exit /B 0
|
||||
|
||||
:GetInstallPath
|
||||
for /f "tokens=2,*" %%A in ('REG.exe query HKCU\SOFTWARE\%1 /v %2 2^>Nul') do (
|
||||
if exist "%%B%%3" (
|
||||
set MSBUILD_PATH="%%B%3"
|
||||
exit /B 0
|
||||
)
|
||||
)
|
||||
for /f "tokens=2,*" %%A in ('REG.exe query HKLM\SOFTWARE\%1 /v %2 2^>Nul') do (
|
||||
if exist "%%B%3" (
|
||||
set MSBUILD_PATH="%%B%3"
|
||||
exit /B 0
|
||||
)
|
||||
)
|
||||
for /f "tokens=2,*" %%A in ('REG.exe query HKCU\SOFTWARE\Wow6432Node\%1 /v %2 2^>Nul') do (
|
||||
if exist "%%B%%3" (
|
||||
set MSBUILD_PATH="%%B%3"
|
||||
exit /B 0
|
||||
)
|
||||
)
|
||||
for /f "tokens=2,*" %%A in ('REG.exe query HKLM\SOFTWARE\Wow6432Node\%1 /v %2 2^>Nul') do (
|
||||
if exist "%%B%3" (
|
||||
set MSBUILD_PATH="%%B%3"
|
||||
exit /B 0
|
||||
)
|
||||
)
|
||||
exit /B 1
|
||||
exit /B 0
|
||||
+1
-1
@@ -3,7 +3,7 @@
|
||||
"Version": {
|
||||
"Major": 1,
|
||||
"Minor": 6,
|
||||
"Build": 6344
|
||||
"Build": 6345
|
||||
},
|
||||
"Company": "Flax",
|
||||
"Copyright": "Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.",
|
||||
|
||||
@@ -291,6 +291,8 @@
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=lightmaps/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Linearize/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=lods/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Marshallable/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=marshallers/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=mclass/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=memcpy/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=metalness/@EntryIndexedValue">True</s:Boolean>
|
||||
|
||||
@@ -1,15 +1,22 @@
|
||||
@echo off
|
||||
|
||||
rem Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
|
||||
:: Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
|
||||
|
||||
setlocal
|
||||
pushd
|
||||
|
||||
echo Generating Flax Engine project files...
|
||||
|
||||
rem Run Flax.Build to generate Visual Studio solution and project files (also pass the arguments)
|
||||
call "Development\Scripts\Windows\CallBuildTool.bat" -genproject %*
|
||||
:: Change the path to the script root
|
||||
cd /D "%~dp0"
|
||||
|
||||
:: Run Flax.Build to generate Visual Studio solution and project files (also pass the arguments)
|
||||
call "Development\Scripts\Windows\CallBuildTool.bat" -genproject %*
|
||||
if errorlevel 1 goto BuildToolFailed
|
||||
|
||||
:: Build bindings for all editor configurations
|
||||
echo Building C# bindings...
|
||||
Binaries\Tools\Flax.Build.exe -build -BuildBindingsOnly -arch=x64 -platform=Windows --buildTargets=FlaxEditor,FlaxGame
|
||||
|
||||
popd
|
||||
echo Done!
|
||||
exit /B 0
|
||||
|
||||
@@ -10,3 +10,8 @@ cd "`dirname "$0"`"
|
||||
|
||||
# Run Flax.Build to generate project files (also pass the arguments)
|
||||
bash ./Development/Scripts/Mac/CallBuildTool.sh --genproject "$@"
|
||||
|
||||
# Build bindings for all editor configurations
|
||||
echo Building C# bindings...
|
||||
# TODO: Detect the correct architecture here
|
||||
Binaries/Tools/Flax.Build -build -BuildBindingsOnly -arch=ARM64 -platform=Mac --buildTargets=FlaxEditor,FlaxGame
|
||||
|
||||
@@ -10,3 +10,8 @@ cd "`dirname "$0"`"
|
||||
|
||||
# Run Flax.Build to generate project files (also pass the arguments)
|
||||
bash ./Development/Scripts/Linux/CallBuildTool.sh --genproject "$@"
|
||||
|
||||
# Build bindings for all editor configurations
|
||||
echo Building C# bindings...
|
||||
# TODO: Detect the correct architecture here
|
||||
Binaries/Tools/Flax.Build -build -BuildBindingsOnly -arch=x64 -platform=Linux --buildTargets=FlaxEditor,FlaxGame
|
||||
|
||||
@@ -31,19 +31,20 @@ Follow the instructions below to compile and run the engine from source.
|
||||
* Install Visual Studio 2022 or newer
|
||||
* Install Windows 8.1 SDK or newer (via Visual Studio Installer)
|
||||
* Install Microsoft Visual C++ 2015 v140 toolset or newer (via Visual Studio Installer)
|
||||
* Install .Net 7 SDK (via Visual Studio Installer or [from web](https://dotnet.microsoft.com/en-us/download/dotnet/7.0))
|
||||
* Install .NET 7 SDK for **Windows x64** (via Visual Studio Installer or [from web](https://dotnet.microsoft.com/en-us/download/dotnet/7.0))
|
||||
* Install Git with LFS
|
||||
* Clone repo (with LFS)
|
||||
* Run **GenerateProjectFiles.bat**
|
||||
* Open `Flax.sln` and set solution configuration to **Editor.Development** and solution platform to **Win64**
|
||||
* Set Flax (C++) or FlaxEngine (C#) as startup project
|
||||
* Compile Flax project (hit F7 or CTRL+Shift+B)
|
||||
* Optionally set Debug Type to **Managed Only (.NET Core)** to debug C#-only, or **Mixed (.NET Core)** to debug both C++ and C#
|
||||
* Run Flax (hit F5 key)
|
||||
|
||||
## Linux
|
||||
|
||||
* Install Visual Studio Code
|
||||
* Install .Net 7 SDK ([https://dotnet.microsoft.com/en-us/download/dotnet/7.0](https://dotnet.microsoft.com/en-us/download/dotnet/7.0))
|
||||
* Install .NET 7 SDK ([https://dotnet.microsoft.com/en-us/download/dotnet/7.0](https://dotnet.microsoft.com/en-us/download/dotnet/7.0))
|
||||
* Ubuntu: `sudo apt install dotnet-sdk-7.0`
|
||||
* Install Vulkan SDK ([https://vulkan.lunarg.com/](https://vulkan.lunarg.com/))
|
||||
* Ubuntu: `sudo apt install vulkan-sdk`
|
||||
@@ -66,7 +67,7 @@ Follow the instructions below to compile and run the engine from source.
|
||||
## Mac
|
||||
|
||||
* Install XCode
|
||||
* Install .Net 7 SDK ([https://dotnet.microsoft.com/en-us/download/dotnet/7.0](https://dotnet.microsoft.com/en-us/download/dotnet/7.0))
|
||||
* Install .NET 7 SDK ([https://dotnet.microsoft.com/en-us/download/dotnet/7.0](https://dotnet.microsoft.com/en-us/download/dotnet/7.0))
|
||||
* Install Vulkan SDK ([https://vulkan.lunarg.com/](https://vulkan.lunarg.com/))
|
||||
* Clone repo (with LFS)
|
||||
* Run `GenerateProjectFiles.command`
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// Custom editor for <see cref="FlaxEngine.Tools.AudioTool.Options"/>.
|
||||
/// </summary>
|
||||
[CustomEditor(typeof(FlaxEngine.Tools.AudioTool.Options)), DefaultEditor]
|
||||
public class AudioToolOptionsEditor : GenericEditor
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override List<ItemInfo> GetItemsForType(ScriptType type)
|
||||
{
|
||||
// Show both fields and properties
|
||||
return GetItemsForType(type, true, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace FlaxEditor.Content.Import
|
||||
{
|
||||
/// <summary>
|
||||
/// Proxy object to present audio import settings in <see cref="ImportFilesDialog"/>.
|
||||
/// </summary>
|
||||
[HideInEditor]
|
||||
public class AudioImportSettings
|
||||
{
|
||||
/// <summary>
|
||||
/// A custom set of bit depth audio import sizes.
|
||||
/// The settings data.
|
||||
/// </summary>
|
||||
public enum CustomBitDepth
|
||||
{
|
||||
/// <summary>
|
||||
/// The 8.
|
||||
/// </summary>
|
||||
_8 = 8,
|
||||
|
||||
/// <summary>
|
||||
/// The 16.
|
||||
/// </summary>
|
||||
_16 = 16,
|
||||
|
||||
/// <summary>
|
||||
/// The 24.
|
||||
/// </summary>
|
||||
_24 = 24,
|
||||
|
||||
/// <summary>
|
||||
/// The 32.
|
||||
/// </summary>
|
||||
_32 = 32,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the bit depth to enum.
|
||||
/// </summary>
|
||||
/// <param name="f">The bit depth.</param>
|
||||
/// <returns>The converted enum.</returns>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The audio data format to import the audio clip as.
|
||||
/// </summary>
|
||||
[EditorOrder(10), DefaultValue(AudioFormat.Vorbis), Tooltip("The audio data format to import the audio clip as.")]
|
||||
public AudioFormat Format { get; set; } = AudioFormat.Vorbis;
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
[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;
|
||||
|
||||
/// <summary>
|
||||
/// Disables dynamic audio streaming. The whole clip will be loaded into the memory. Useful for small clips (eg. gunfire sounds).
|
||||
/// </summary>
|
||||
[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;
|
||||
|
||||
/// <summary>
|
||||
/// Checks should the clip be played as spatial (3D) audio or as normal audio. 3D audio is stored in Mono format.
|
||||
/// </summary>
|
||||
[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;
|
||||
|
||||
/// <summary>
|
||||
/// The size of a single sample in bits. The clip will be converted to this bit depth on import.
|
||||
/// </summary>
|
||||
[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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries the restore the asset import options from the target resource file.
|
||||
/// </summary>
|
||||
/// <param name="options">The options.</param>
|
||||
/// <param name="assetPath">The asset path.</param>
|
||||
/// <returns>True settings has been restored, otherwise false.</returns>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -147,7 +55,7 @@ namespace FlaxEditor.Content.Import
|
||||
/// <seealso cref="AssetImportEntry" />
|
||||
public partial class AudioImportEntry : AssetImportEntry
|
||||
{
|
||||
private AudioImportSettings _settings = new AudioImportSettings();
|
||||
private AudioImportSettings _settings = new();
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AudioImportEntry"/> 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);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -166,27 +74,23 @@ namespace FlaxEditor.Content.Import
|
||||
/// <inheritdoc />
|
||||
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;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -139,7 +139,7 @@ namespace FlaxEditor.Content.Import
|
||||
var menu = new ContextMenu();
|
||||
menu.AddButton("Rename", OnRenameClicked);
|
||||
menu.AddButton("Don't import", OnDontImportClicked);
|
||||
menu.AddButton("Show in Explorer", OnShowInExplorerClicked);
|
||||
menu.AddButton(Utilities.Constants.ShowInExplorer, OnShowInExplorerClicked);
|
||||
menu.Tag = node;
|
||||
menu.Show(node, location);
|
||||
}
|
||||
|
||||
@@ -486,7 +486,7 @@ namespace FlaxEditor.Content
|
||||
else
|
||||
Render2D.FillRectangle(rectangle, Color.Black);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Draws the item thumbnail.
|
||||
/// </summary>
|
||||
@@ -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);
|
||||
|
||||
@@ -29,7 +29,7 @@ namespace FlaxEditor.Content
|
||||
//_watcher.Changed += OnEvent;
|
||||
_watcher.Created += OnEvent;
|
||||
_watcher.Deleted += OnEvent;
|
||||
//_watcher.Renamed += OnEvent;
|
||||
_watcher.Renamed += OnEvent;
|
||||
}
|
||||
|
||||
private void OnEvent(object sender, FileSystemEventArgs e)
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "MacPlatformTools.h"
|
||||
#include "Engine/Platform/File.h"
|
||||
#include "Engine/Platform/FileSystem.h"
|
||||
#include "Engine/Platform/CreateProcessSettings.h"
|
||||
#include "Engine/Platform/Mac/MacPlatformSettings.h"
|
||||
#include "Engine/Core/Config/GameSettings.h"
|
||||
#include "Engine/Core/Config/BuildSettings.h"
|
||||
@@ -16,7 +17,9 @@
|
||||
#include "Editor/ProjectInfo.h"
|
||||
#include "Editor/Cooker/GameCooker.h"
|
||||
#include "Editor/Utilities/EditorUtilities.h"
|
||||
#include <ThirdParty/pugixml/pugixml.hpp>
|
||||
|
||||
#include "pugixml/pugixml_extra.hpp"
|
||||
|
||||
using namespace pugi;
|
||||
|
||||
IMPLEMENT_SETTINGS_GETTER(MacPlatformSettings, MacPlatform);
|
||||
@@ -124,17 +127,35 @@ bool MacPlatformTools::OnPostProcess(CookingData& data)
|
||||
LOG(Error, "Failed to export application icon.");
|
||||
return true;
|
||||
}
|
||||
bool failed = Platform::RunProcess(TEXT("sips -z 16 16 icon_1024x1024.png --out icon_16x16.png"), tmpFolderPath);
|
||||
failed |= Platform::RunProcess(TEXT("sips -z 32 32 icon_1024x1024.png --out icon_16x16@2x.png"), tmpFolderPath);
|
||||
failed |= Platform::RunProcess(TEXT("sips -z 32 32 icon_1024x1024.png --out icon_32x32.png"), tmpFolderPath);
|
||||
failed |= Platform::RunProcess(TEXT("sips -z 64 64 icon_1024x1024.png --out icon_32x32@2x.png"), tmpFolderPath);
|
||||
failed |= Platform::RunProcess(TEXT("sips -z 128 128 icon_1024x1024.png --out icon_128x128.png"), tmpFolderPath);
|
||||
failed |= Platform::RunProcess(TEXT("sips -z 256 256 icon_1024x1024.png --out icon_128x128@2x.png"), tmpFolderPath);
|
||||
failed |= Platform::RunProcess(TEXT("sips -z 256 256 icon_1024x1024.png --out icon_256x256.png"), tmpFolderPath);
|
||||
failed |= Platform::RunProcess(TEXT("sips -z 512 512 icon_1024x1024.png --out icon_256x256@2x.png"), tmpFolderPath);
|
||||
failed |= Platform::RunProcess(TEXT("sips -z 512 512 icon_1024x1024.png --out icon_512x512.png"), tmpFolderPath);
|
||||
failed |= Platform::RunProcess(TEXT("sips -z 1024 1024 icon_1024x1024.png --out icon_512x512@2x.png"), tmpFolderPath);
|
||||
failed |= Platform::RunProcess(TEXT("iconutil -c icns icon.iconset"), iconFolderPath);
|
||||
CreateProcessSettings procSettings;
|
||||
procSettings.HiddenWindow = true;
|
||||
procSettings.FileName = TEXT("/usr/bin/sips");
|
||||
procSettings.WorkingDirectory = tmpFolderPath;
|
||||
procSettings.Arguments = TEXT("-z 16 16 icon_1024x1024.png --out icon_16x16.png");
|
||||
bool failed = false;
|
||||
failed |= Platform::CreateProcess(procSettings);
|
||||
procSettings.Arguments = TEXT("-z 32 32 icon_1024x1024.png --out icon_16x16@2x.png");
|
||||
failed |= Platform::CreateProcess(procSettings);
|
||||
procSettings.Arguments = TEXT("-z 32 32 icon_1024x1024.png --out icon_32x32.png");
|
||||
failed |= Platform::CreateProcess(procSettings);
|
||||
procSettings.Arguments = TEXT("-z 64 64 icon_1024x1024.png --out icon_32x32@2x.png");
|
||||
failed |= Platform::CreateProcess(procSettings);
|
||||
procSettings.Arguments = TEXT("-z 128 128 icon_1024x1024.png --out icon_128x128.png");
|
||||
failed |= Platform::CreateProcess(procSettings);
|
||||
procSettings.Arguments = TEXT("-z 256 256 icon_1024x1024.png --out icon_128x128@2x.png");
|
||||
failed |= Platform::CreateProcess(procSettings);
|
||||
procSettings.Arguments = TEXT("-z 256 256 icon_1024x1024.png --out icon_256x256.png");
|
||||
failed |= Platform::CreateProcess(procSettings);
|
||||
procSettings.Arguments = TEXT("-z 512 512 icon_1024x1024.png --out icon_256x256@2x.png");
|
||||
failed |= Platform::CreateProcess(procSettings);
|
||||
procSettings.Arguments = TEXT("-z 512 512 icon_1024x1024.png --out icon_512x512.png");
|
||||
failed |= Platform::CreateProcess(procSettings);
|
||||
procSettings.Arguments = TEXT("-z 1024 1024 icon_1024x1024.png --out icon_512x512@2x.png");
|
||||
failed |= Platform::CreateProcess(procSettings);
|
||||
procSettings.FileName = TEXT("/usr/bin/iconutil");
|
||||
procSettings.Arguments = TEXT("-c icns icon.iconset");
|
||||
procSettings.WorkingDirectory = iconFolderPath;
|
||||
failed |= Platform::CreateProcess(procSettings);
|
||||
if (failed)
|
||||
{
|
||||
LOG(Error, "Failed to export application icon.");
|
||||
@@ -151,17 +172,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");
|
||||
@@ -175,22 +196,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
|
||||
@@ -210,16 +231,22 @@ bool MacPlatformTools::OnPostProcess(CookingData& data)
|
||||
return false;
|
||||
GameCooker::PackageFiles();
|
||||
LOG(Info, "Building app package...");
|
||||
const String dmgPath = data.OriginalOutputPath / appName + TEXT(".dmg");
|
||||
const String dmgCommand = String::Format(TEXT("hdiutil create {0}.dmg -volname {0} -fs HFS+ -srcfolder {0}.app"), appName);
|
||||
const int32 result = Platform::RunProcess(dmgCommand, data.OriginalOutputPath);
|
||||
if (result != 0)
|
||||
{
|
||||
data.Error(String::Format(TEXT("Failed to package app (result code: {0}). See log for more info."), result));
|
||||
return true;
|
||||
const String dmgPath = data.OriginalOutputPath / appName + TEXT(".dmg");
|
||||
CreateProcessSettings procSettings;
|
||||
procSettings.HiddenWindow = true;
|
||||
procSettings.WorkingDirectory = data.OriginalOutputPath;
|
||||
procSettings.FileName = TEXT("/usr/bin/hdiutil");
|
||||
procSettings.Arguments = String::Format(TEXT("create {0}.dmg -volname {0} -fs HFS+ -srcfolder {0}.app"), appName);
|
||||
const int32 result = Platform::CreateProcess(procSettings);
|
||||
if (result != 0)
|
||||
{
|
||||
data.Error(String::Format(TEXT("Failed to package app (result code: {0}). See log for more info."), result));
|
||||
return true;
|
||||
}
|
||||
// TODO: sign dmg
|
||||
LOG(Info, "Output application package: {0} (size: {1} MB)", dmgPath, FileSystem::GetFileSize(dmgPath) / 1024 / 1024);
|
||||
}
|
||||
// TODO: sign dmg
|
||||
LOG(Info, "Output application package: {0} (size: {1} MB)", dmgPath, FileSystem::GetFileSize(dmgPath) / 1024 / 1024);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -260,15 +260,20 @@ bool iOSPlatformTools::OnPostProcess(CookingData& data)
|
||||
{
|
||||
LOG(Info, "Building app package...");
|
||||
const Char* configuration = data.Configuration == BuildConfiguration::Release ? TEXT("Release") : TEXT("Debug");
|
||||
String command = String::Format(TEXT("xcodebuild -project FlaxGame.xcodeproj -configuration {} -scheme FlaxGame -archivePath FlaxGame.xcarchive archive"), configuration);
|
||||
int32 result = Platform::RunProcess(command, data.OriginalOutputPath);
|
||||
CreateProcessSettings procSettings;
|
||||
procSettings.HiddenWindow = true;
|
||||
procSettings.WorkingDirectory = data.OriginalOutputPath;
|
||||
procSettings.FileName = TEXT("/usr/bin/xcodebuild");
|
||||
procSettings.Arguments = String::Format(TEXT("-project FlaxGame.xcodeproj -configuration {} -scheme FlaxGame -archivePath FlaxGame.xcarchive archive"), configuration);
|
||||
int32 result = Platform::CreateProcess(procSettings);
|
||||
if (result != 0)
|
||||
{
|
||||
data.Error(String::Format(TEXT("Failed to package app (result code: {0}). See log for more info."), result));
|
||||
return true;
|
||||
}
|
||||
command = TEXT("xcodebuild -exportArchive -archivePath FlaxGame.xcarchive -allowProvisioningUpdates -exportPath . -exportOptionsPlist ExportOptions.plist");
|
||||
result = Platform::RunProcess(command, data.OriginalOutputPath);
|
||||
procSettings.FileName = TEXT("/usr/bin/xcodebuild");
|
||||
procSettings.Arguments = TEXT("-exportArchive -archivePath FlaxGame.xcarchive -allowProvisioningUpdates -exportPath . -exportOptionsPlist ExportOptions.plist");
|
||||
result = Platform::CreateProcess(procSettings);
|
||||
if (result != 0)
|
||||
{
|
||||
data.Error(String::Format(TEXT("Failed to package app (result code: {0}). See log for more info."), result));
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
using FlaxEditor.CustomEditors.Editors;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
|
||||
namespace FlaxEditor.CustomEditors.Dedicated;
|
||||
|
||||
/// <summary>
|
||||
/// The missing script editor.
|
||||
/// </summary>
|
||||
[CustomEditor(typeof(MissingScript)), DefaultEditor]
|
||||
public class MissingScriptEditor : GenericEditor
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override void Initialize(LayoutElementsContainer layout)
|
||||
{
|
||||
if (layout.ContainerControl is not DropPanel dropPanel)
|
||||
{
|
||||
base.Initialize(layout);
|
||||
return;
|
||||
}
|
||||
|
||||
dropPanel.HeaderTextColor = Color.OrangeRed;
|
||||
|
||||
base.Initialize(layout);
|
||||
}
|
||||
}
|
||||
@@ -25,9 +25,20 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
get
|
||||
{
|
||||
// All selected particle effects use the same system
|
||||
var effect = (ParticleEffect)Values[0];
|
||||
var system = effect.ParticleSystem;
|
||||
return system != null && Values.TrueForAll(x => (x as ParticleEffect)?.ParticleSystem == system);
|
||||
var effect = Values[0] as ParticleEffect;
|
||||
var system = effect ? effect.ParticleSystem : null;
|
||||
if (system && Values.TrueForAll(x => x is ParticleEffect fx && fx && fx.ParticleSystem == system))
|
||||
{
|
||||
// All parameters can be accessed
|
||||
var parameters = effect.Parameters;
|
||||
foreach (var parameter in parameters)
|
||||
{
|
||||
if (!parameter)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
private DragHandlers _dragHandlers;
|
||||
private DragScriptItems _dragScripts;
|
||||
private DragAssets _dragAssets;
|
||||
private Button _addScriptsButton;
|
||||
|
||||
/// <summary>
|
||||
/// The parent scripts editor.
|
||||
@@ -40,16 +41,19 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
AutoFocus = false;
|
||||
|
||||
// Add script button
|
||||
float addScriptButtonWidth = 60.0f;
|
||||
var addScriptButton = new Button
|
||||
var buttonText = "Add script";
|
||||
var textSize = Style.Current.FontMedium.MeasureText(buttonText);
|
||||
float addScriptButtonWidth = (textSize.X < 60.0f) ? 60.0f : textSize.X + 4;
|
||||
var buttonHeight = (textSize.Y < 18) ? 18 : textSize.Y + 4;
|
||||
_addScriptsButton = new Button
|
||||
{
|
||||
TooltipText = "Add new scripts to the actor",
|
||||
AnchorPreset = AnchorPresets.MiddleCenter,
|
||||
Text = "Add script",
|
||||
Text = buttonText,
|
||||
Parent = this,
|
||||
Bounds = new Rectangle((Width - addScriptButtonWidth) / 2, 1, addScriptButtonWidth, 18),
|
||||
Bounds = new Rectangle((Width - addScriptButtonWidth) / 2, 1, addScriptButtonWidth, buttonHeight),
|
||||
};
|
||||
addScriptButton.ButtonClicked += OnAddScriptButtonClicked;
|
||||
_addScriptsButton.ButtonClicked += OnAddScriptButtonClicked;
|
||||
}
|
||||
|
||||
private void OnAddScriptButtonClicked(Button button)
|
||||
@@ -82,7 +86,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
var size = Size;
|
||||
|
||||
// Info
|
||||
Render2D.DrawText(style.FontSmall, "Drag scripts here", new Rectangle(2, 22, size.X - 4, size.Y - 4 - 20), style.ForegroundDisabled, TextAlignment.Center, TextAlignment.Center);
|
||||
Render2D.DrawText(style.FontSmall, "Drag scripts here", new Rectangle(2, _addScriptsButton.Height + 4, size.X - 4, size.Y - 4 - 20), style.ForegroundDisabled, TextAlignment.Center, TextAlignment.Center);
|
||||
|
||||
// Check if drag is over
|
||||
if (IsDragOver && _dragHandlers != null && _dragHandlers.HasValidDrag)
|
||||
@@ -576,8 +580,8 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
return;
|
||||
for (int j = 0; j < e.Length; j++)
|
||||
{
|
||||
var t1 = scripts[j]?.TypeName;
|
||||
var t2 = e[j]?.TypeName;
|
||||
var t1 = scripts[j] != null ? scripts[j].TypeName : null;
|
||||
var t2 = e[j] != null ? e[j].TypeName : null;
|
||||
if (t1 != t2)
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
|
||||
|
||||
using FlaxEditor.Actions;
|
||||
using FlaxEditor.SceneGraph.Actors;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
using FlaxEditor.Actions;
|
||||
using FlaxEditor.SceneGraph;
|
||||
using FlaxEditor.SceneGraph.Actors;
|
||||
using FlaxEditor.GUI.Tabs;
|
||||
|
||||
namespace FlaxEditor.CustomEditors.Dedicated
|
||||
{
|
||||
@@ -14,54 +17,853 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
[CustomEditor(typeof(Spline)), DefaultEditor]
|
||||
public class SplineEditor : ActorEditor
|
||||
{
|
||||
/// <summary>
|
||||
/// Storage undo spline data
|
||||
/// </summary>
|
||||
private struct UndoData
|
||||
{
|
||||
public Spline Spline;
|
||||
public BezierCurve<Transform>.Keyframe[] BeforeKeyframes;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Basis for creating tangent manipulation types for bezier curves.
|
||||
/// </summary>
|
||||
private class EditTangentOptionBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Called when user set selected tangent mode.
|
||||
/// </summary>
|
||||
/// <param name="spline">Current spline selected on editor viewport.</param>
|
||||
/// <param name="index">Index of current keyframe selected on spline.</param>
|
||||
public virtual void OnSetMode(Spline spline, int index)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when user select a keyframe (spline point) of current selected spline on editor viewport.
|
||||
/// </summary>
|
||||
/// <param name="spline">Current spline selected on editor viewport.</param>
|
||||
/// <param name="index">Index of current keyframe selected on spline.</param>
|
||||
public virtual void OnSelectKeyframe(Spline spline, int index)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when user select a tangent of current keyframe selected from spline.
|
||||
/// </summary>
|
||||
/// <param name="spline">Current spline selected on editor viewport.</param>
|
||||
/// <param name="index">Index of current keyframe selected on spline.</param>
|
||||
public virtual void OnSelectTangent(Spline spline, int index)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when the tangent in from current keyframe selected from spline is moved on editor viewport.
|
||||
/// </summary>
|
||||
/// <param name="spline">Current spline selected on editor viewport.</param>
|
||||
/// <param name="index">Index of current keyframe selected on spline.</param>
|
||||
public virtual void OnMoveTangentIn(Spline spline, int index)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when the tangent out from current keyframe selected from spline is moved on editor viewport.
|
||||
/// </summary>
|
||||
/// <param name="spline">Current spline selected on editor viewport.</param>
|
||||
/// <param name="index">Current spline selected on editor viewport.</param>
|
||||
public virtual void OnMoveTangentOut(Spline spline, int index)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Edit curve options manipulate the curve as free mode
|
||||
/// </summary>
|
||||
private sealed class FreeTangentMode : EditTangentOptionBase
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override void OnSetMode(Spline spline, int index)
|
||||
{
|
||||
if (IsLinearTangentMode(spline, index) || IsSmoothInTangentMode(spline, index) || IsSmoothOutTangentMode(spline, index))
|
||||
{
|
||||
SetPointSmooth(spline, index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Edit curve options to set tangents to linear
|
||||
/// </summary>
|
||||
private sealed class LinearTangentMode : EditTangentOptionBase
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override void OnSetMode(Spline spline, int index)
|
||||
{
|
||||
SetKeyframeLinear(spline, index);
|
||||
|
||||
// change the selection to tangent parent (a spline point / keyframe)
|
||||
SetSelectSplinePointNode(spline, index);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Edit curve options to align tangents of selected spline
|
||||
/// </summary>
|
||||
private sealed class AlignedTangentMode : EditTangentOptionBase
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override void OnSetMode(Spline spline, int index)
|
||||
{
|
||||
SmoothIfNotAligned(spline, index);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void OnSelectKeyframe(Spline spline, int index)
|
||||
{
|
||||
SmoothIfNotAligned(spline, index);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void OnMoveTangentIn(Spline spline, int index)
|
||||
{
|
||||
SetPointAligned(spline, index, true);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void OnMoveTangentOut(Spline spline, int index)
|
||||
{
|
||||
SetPointAligned(spline, index, false);
|
||||
}
|
||||
|
||||
private void SmoothIfNotAligned(Spline spline, int index)
|
||||
{
|
||||
if (!IsAlignedTangentMode(spline, index))
|
||||
{
|
||||
SetPointSmooth(spline, index);
|
||||
}
|
||||
}
|
||||
|
||||
private void SetPointAligned(Spline spline, int index, bool alignWithIn)
|
||||
{
|
||||
var keyframe = spline.GetSplineKeyframe(index);
|
||||
var referenceTangent = alignWithIn ? keyframe.TangentIn : keyframe.TangentOut;
|
||||
var otherTangent = !alignWithIn ? keyframe.TangentIn : keyframe.TangentOut;
|
||||
|
||||
// inverse of reference tangent
|
||||
otherTangent.Translation = -referenceTangent.Translation.Normalized * otherTangent.Translation.Length;
|
||||
|
||||
if (alignWithIn)
|
||||
keyframe.TangentOut = otherTangent;
|
||||
if (!alignWithIn)
|
||||
keyframe.TangentIn = otherTangent;
|
||||
|
||||
spline.SetSplineKeyframe(index, keyframe);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Edit curve options manipulate the curve setting selected point
|
||||
/// tangent in as smoothed but tangent out as linear
|
||||
/// </summary>
|
||||
private sealed class SmoothInTangentMode : EditTangentOptionBase
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override void OnSetMode(Spline spline, int index)
|
||||
{
|
||||
SetTangentSmoothIn(spline, index);
|
||||
SetSelectTangentIn(spline, index);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Edit curve options manipulate the curve setting selected point
|
||||
/// tangent in as linear but tangent out as smoothed
|
||||
/// </summary>
|
||||
private sealed class SmoothOutTangentMode : EditTangentOptionBase
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override void OnSetMode(Spline spline, int index)
|
||||
{
|
||||
SetTangentSmoothOut(spline, index);
|
||||
SetSelectTangentOut(spline, index);
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class IconTab : Tab
|
||||
{
|
||||
private sealed class IconTabHeader : Tabs.TabHeader
|
||||
{
|
||||
public IconTabHeader(Tabs tabs, Tab tab)
|
||||
: base(tabs, tab)
|
||||
{
|
||||
}
|
||||
|
||||
public override bool OnMouseUp(Float2 location, MouseButton button)
|
||||
{
|
||||
if (EnabledInHierarchy && Tab.Enabled)
|
||||
((IconTab)Tab)._action();
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void Draw()
|
||||
{
|
||||
base.Draw();
|
||||
|
||||
var tab = (IconTab)Tab;
|
||||
var enabled = EnabledInHierarchy && tab.EnabledInHierarchy;
|
||||
var style = FlaxEngine.GUI.Style.Current;
|
||||
var size = Size;
|
||||
var textHeight = 16.0f;
|
||||
var iconSize = size.Y - textHeight;
|
||||
var iconRect = new Rectangle((Width - iconSize) / 2, 0, iconSize, iconSize);
|
||||
if (tab._mirrorIcon)
|
||||
{
|
||||
iconRect.Location.X += iconRect.Size.X;
|
||||
iconRect.Size.X *= -1;
|
||||
}
|
||||
var color = style.Foreground;
|
||||
if (!enabled)
|
||||
color *= 0.6f;
|
||||
Render2D.DrawSprite(tab._customIcon, iconRect, color);
|
||||
Render2D.DrawText(style.FontMedium, tab._customText, new Rectangle(0, iconSize, size.X, textHeight), color, TextAlignment.Center, TextAlignment.Center);
|
||||
}
|
||||
}
|
||||
|
||||
private readonly Action _action;
|
||||
private readonly string _customText;
|
||||
private readonly SpriteHandle _customIcon;
|
||||
private readonly bool _mirrorIcon;
|
||||
|
||||
public IconTab(Action action, string text, SpriteHandle icon, bool mirrorIcon = false)
|
||||
: base(string.Empty, SpriteHandle.Invalid)
|
||||
{
|
||||
_action = action;
|
||||
_customText = text;
|
||||
_customIcon = icon;
|
||||
_mirrorIcon = mirrorIcon;
|
||||
}
|
||||
|
||||
public override Tabs.TabHeader CreateHeader()
|
||||
{
|
||||
return new IconTabHeader((Tabs)Parent, this);
|
||||
}
|
||||
}
|
||||
|
||||
private EditTangentOptionBase _currentTangentMode;
|
||||
|
||||
private Tabs _selectedPointsTabs, _allPointsTabs;
|
||||
private Tab _freeTangentTab;
|
||||
private Tab _linearTangentTab;
|
||||
private Tab _alignedTangentTab;
|
||||
private Tab _smoothInTangentTab;
|
||||
private Tab _smoothOutTangentTab;
|
||||
private Tab _setLinearAllTangentsTab;
|
||||
private Tab _setSmoothAllTangentsTab;
|
||||
|
||||
private bool _tanInChanged;
|
||||
private bool _tanOutChanged;
|
||||
private Vector3 _lastTanInPos;
|
||||
private Vector3 _lastTanOutPos;
|
||||
private Spline _selectedSpline;
|
||||
private SplineNode.SplinePointNode _selectedPoint;
|
||||
private SplineNode.SplinePointNode _lastPointSelected;
|
||||
private SplineNode.SplinePointTangentNode _selectedTangentIn;
|
||||
private SplineNode.SplinePointTangentNode _selectedTangentOut;
|
||||
|
||||
private UndoData[] _selectedSplinesUndoData;
|
||||
|
||||
private bool HasPointSelected => _selectedPoint != null;
|
||||
private bool HasTangentsSelected => _selectedTangentIn != null || _selectedTangentOut != null;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Initialize(LayoutElementsContainer layout)
|
||||
{
|
||||
base.Initialize(layout);
|
||||
|
||||
if (Values.HasDifferentTypes == false)
|
||||
_currentTangentMode = new FreeTangentMode();
|
||||
if (Values.HasDifferentTypes || !(Values[0] is Spline spline))
|
||||
return;
|
||||
_selectedSpline = spline;
|
||||
|
||||
layout.Space(10);
|
||||
var tabSize = 46;
|
||||
var icons = Editor.Instance.Icons;
|
||||
|
||||
layout.Header("Selected spline point");
|
||||
_selectedPointsTabs = new Tabs
|
||||
{
|
||||
layout.Space(10);
|
||||
var grid = layout.CustomContainer<UniformGridPanel>();
|
||||
grid.CustomControl.SlotsHorizontally = 2;
|
||||
grid.CustomControl.SlotsVertically = 1;
|
||||
grid.Button("Set Linear Tangents").Button.Clicked += OnSetTangentsLinear;
|
||||
grid.Button("Set Smooth Tangents").Button.Clicked += OnSetTangentsSmooth;
|
||||
Height = tabSize,
|
||||
TabsSize = new Float2(tabSize),
|
||||
AutoTabsSize = true,
|
||||
Parent = layout.ContainerControl,
|
||||
};
|
||||
_linearTangentTab = _selectedPointsTabs.AddTab(new IconTab(OnSetSelectedLinear, "Linear", icons.SplineLinear64));
|
||||
_freeTangentTab = _selectedPointsTabs.AddTab(new IconTab(OnSetSelectedFree, "Free", icons.SplineFree64));
|
||||
_alignedTangentTab = _selectedPointsTabs.AddTab(new IconTab(OnSetSelectedAligned, "Aligned", icons.SplineAligned64));
|
||||
_smoothInTangentTab = _selectedPointsTabs.AddTab(new IconTab(OnSetSelectedSmoothIn, "Smooth In", icons.SplineSmoothIn64));
|
||||
_smoothOutTangentTab = _selectedPointsTabs.AddTab(new IconTab(OnSetSelectedSmoothOut, "Smooth Out", icons.SplineSmoothIn64, true));
|
||||
_selectedPointsTabs.SelectedTabIndex = -1;
|
||||
|
||||
layout.Header("All spline points");
|
||||
_allPointsTabs = new Tabs
|
||||
{
|
||||
Height = tabSize,
|
||||
TabsSize = new Float2(tabSize),
|
||||
AutoTabsSize = true,
|
||||
Parent = layout.ContainerControl,
|
||||
};
|
||||
_setLinearAllTangentsTab = _allPointsTabs.AddTab(new IconTab(OnSetTangentsLinear, "Set Linear Tangents", icons.SplineLinear64));
|
||||
_setSmoothAllTangentsTab = _allPointsTabs.AddTab(new IconTab(OnSetTangentsSmooth, "Set Smooth Tangents", icons.SplineAligned64));
|
||||
_allPointsTabs.SelectedTabIndex = -1;
|
||||
|
||||
if (_selectedSpline)
|
||||
_selectedSpline.SplineUpdated += OnSplineEdited;
|
||||
|
||||
SetSelectedTangentTypeAsCurrent();
|
||||
UpdateEditTabsSelection();
|
||||
UpdateButtonsEnabled();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void Deinitialize()
|
||||
{
|
||||
if (_selectedSpline)
|
||||
_selectedSpline.SplineUpdated -= OnSplineEdited;
|
||||
}
|
||||
|
||||
private void OnSplineEdited()
|
||||
{
|
||||
UpdateEditTabsSelection();
|
||||
UpdateButtonsEnabled();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void Refresh()
|
||||
{
|
||||
base.Refresh();
|
||||
|
||||
UpdateSelectedPoint();
|
||||
UpdateSelectedTangent();
|
||||
|
||||
if (!CanEditTangent())
|
||||
return;
|
||||
|
||||
var index = _lastPointSelected.Index;
|
||||
var currentTangentInPosition = _selectedSpline.GetSplineLocalTangent(index, true).Translation;
|
||||
var currentTangentOutPosition = _selectedSpline.GetSplineLocalTangent(index, false).Translation;
|
||||
|
||||
if (_selectedTangentIn != null)
|
||||
{
|
||||
_tanInChanged = _lastTanInPos != currentTangentInPosition;
|
||||
_lastTanInPos = currentTangentInPosition;
|
||||
}
|
||||
|
||||
if (_selectedTangentOut != null)
|
||||
{
|
||||
_tanOutChanged = _lastTanOutPos != currentTangentOutPosition;
|
||||
_lastTanOutPos = currentTangentOutPosition;
|
||||
}
|
||||
|
||||
if (_tanInChanged)
|
||||
_currentTangentMode.OnMoveTangentIn(_selectedSpline, index);
|
||||
if (_tanOutChanged)
|
||||
_currentTangentMode.OnMoveTangentOut(_selectedSpline, index);
|
||||
|
||||
currentTangentInPosition = _selectedSpline.GetSplineLocalTangent(index, true).Translation;
|
||||
currentTangentOutPosition = _selectedSpline.GetSplineLocalTangent(index, false).Translation;
|
||||
|
||||
// Update last tangents position after changes
|
||||
if (_selectedSpline)
|
||||
_lastTanInPos = currentTangentInPosition;
|
||||
if (_selectedSpline)
|
||||
_lastTanOutPos = currentTangentOutPosition;
|
||||
_tanInChanged = false;
|
||||
_tanOutChanged = false;
|
||||
}
|
||||
|
||||
private void SetSelectedTangentTypeAsCurrent()
|
||||
{
|
||||
if (_lastPointSelected == null || _selectedPoint == null)
|
||||
return;
|
||||
if (IsLinearTangentMode(_selectedSpline, _lastPointSelected.Index))
|
||||
SetModeLinear();
|
||||
else if (IsAlignedTangentMode(_selectedSpline, _lastPointSelected.Index))
|
||||
SetModeAligned();
|
||||
else if (IsSmoothInTangentMode(_selectedSpline, _lastPointSelected.Index))
|
||||
SetModeSmoothIn();
|
||||
else if (IsSmoothOutTangentMode(_selectedSpline, _lastPointSelected.Index))
|
||||
SetModeSmoothOut();
|
||||
else if (IsFreeTangentMode(_selectedSpline, _lastPointSelected.Index))
|
||||
SetModeFree();
|
||||
}
|
||||
|
||||
private void UpdateEditTabsSelection()
|
||||
{
|
||||
_selectedPointsTabs.Enabled = CanEditTangent();
|
||||
if (!_selectedPointsTabs.Enabled)
|
||||
{
|
||||
_selectedPointsTabs.SelectedTabIndex = -1;
|
||||
return;
|
||||
}
|
||||
|
||||
var isFree = _currentTangentMode is FreeTangentMode;
|
||||
var isLinear = _currentTangentMode is LinearTangentMode;
|
||||
var isAligned = _currentTangentMode is AlignedTangentMode;
|
||||
var isSmoothIn = _currentTangentMode is SmoothInTangentMode;
|
||||
var isSmoothOut = _currentTangentMode is SmoothOutTangentMode;
|
||||
|
||||
if (isFree)
|
||||
_selectedPointsTabs.SelectedTab = _freeTangentTab;
|
||||
else if (isLinear)
|
||||
_selectedPointsTabs.SelectedTab = _linearTangentTab;
|
||||
else if (isAligned)
|
||||
_selectedPointsTabs.SelectedTab = _alignedTangentTab;
|
||||
else if (isSmoothIn)
|
||||
_selectedPointsTabs.SelectedTab = _smoothInTangentTab;
|
||||
else if (isSmoothOut)
|
||||
_selectedPointsTabs.SelectedTab = _smoothOutTangentTab;
|
||||
else
|
||||
_selectedPointsTabs.SelectedTabIndex = -1;
|
||||
}
|
||||
|
||||
private void UpdateButtonsEnabled()
|
||||
{
|
||||
_linearTangentTab.Enabled = CanEditTangent();
|
||||
_freeTangentTab.Enabled = CanEditTangent();
|
||||
_alignedTangentTab.Enabled = CanEditTangent();
|
||||
_smoothInTangentTab.Enabled = CanSetTangentSmoothIn();
|
||||
_smoothOutTangentTab.Enabled = CanSetTangentSmoothOut();
|
||||
_setLinearAllTangentsTab.Enabled = CanSetAllTangentsLinear();
|
||||
_setSmoothAllTangentsTab.Enabled = CanSetAllTangentsSmooth();
|
||||
}
|
||||
|
||||
private bool CanEditTangent()
|
||||
{
|
||||
return !HasDifferentTypes && !HasDifferentValues && (HasPointSelected || HasTangentsSelected);
|
||||
}
|
||||
|
||||
private bool CanSetTangentSmoothIn()
|
||||
{
|
||||
if (!CanEditTangent())
|
||||
return false;
|
||||
return _lastPointSelected.Index != 0;
|
||||
}
|
||||
|
||||
private bool CanSetTangentSmoothOut()
|
||||
{
|
||||
if (!CanEditTangent())
|
||||
return false;
|
||||
return _lastPointSelected.Index < _selectedSpline.SplinePointsCount - 1;
|
||||
}
|
||||
|
||||
private bool CanSetAllTangentsSmooth()
|
||||
{
|
||||
return _selectedSpline != null;
|
||||
}
|
||||
|
||||
private bool CanSetAllTangentsLinear()
|
||||
{
|
||||
return _selectedSpline != null;
|
||||
}
|
||||
|
||||
private void SetModeLinear()
|
||||
{
|
||||
if (_currentTangentMode is LinearTangentMode)
|
||||
return;
|
||||
_currentTangentMode = new LinearTangentMode();
|
||||
_currentTangentMode.OnSetMode(_selectedSpline, _lastPointSelected.Index);
|
||||
}
|
||||
|
||||
private void SetModeFree()
|
||||
{
|
||||
if (_currentTangentMode is FreeTangentMode)
|
||||
return;
|
||||
_currentTangentMode = new FreeTangentMode();
|
||||
_currentTangentMode.OnSetMode(_selectedSpline, _lastPointSelected.Index);
|
||||
}
|
||||
|
||||
private void SetModeAligned()
|
||||
{
|
||||
if (_currentTangentMode is AlignedTangentMode)
|
||||
return;
|
||||
_currentTangentMode = new AlignedTangentMode();
|
||||
_currentTangentMode.OnSetMode(_selectedSpline, _lastPointSelected.Index);
|
||||
}
|
||||
|
||||
private void SetModeSmoothIn()
|
||||
{
|
||||
if (_currentTangentMode is SmoothInTangentMode)
|
||||
return;
|
||||
_currentTangentMode = new SmoothInTangentMode();
|
||||
_currentTangentMode.OnSetMode(_selectedSpline, _lastPointSelected.Index);
|
||||
}
|
||||
|
||||
private void SetModeSmoothOut()
|
||||
{
|
||||
if (_currentTangentMode is SmoothOutTangentMode)
|
||||
return;
|
||||
_currentTangentMode = new SmoothOutTangentMode();
|
||||
_currentTangentMode.OnSetMode(_selectedSpline, _lastPointSelected.Index);
|
||||
}
|
||||
|
||||
private void UpdateSelectedPoint()
|
||||
{
|
||||
// works only if select one spline
|
||||
if (_selectedSpline)
|
||||
{
|
||||
var currentSelected = Editor.Instance.SceneEditing.Selection[0];
|
||||
|
||||
if (currentSelected == _selectedPoint)
|
||||
return;
|
||||
if (currentSelected is SplineNode.SplinePointNode selectedPoint)
|
||||
{
|
||||
_selectedPoint = selectedPoint;
|
||||
_lastPointSelected = _selectedPoint;
|
||||
_currentTangentMode.OnSelectKeyframe(_selectedSpline, _lastPointSelected.Index);
|
||||
}
|
||||
else
|
||||
{
|
||||
_selectedPoint = null;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_selectedPoint = null;
|
||||
}
|
||||
|
||||
SetSelectedTangentTypeAsCurrent();
|
||||
UpdateEditTabsSelection();
|
||||
UpdateButtonsEnabled();
|
||||
}
|
||||
|
||||
private void UpdateSelectedTangent()
|
||||
{
|
||||
// works only if select one spline
|
||||
if (_lastPointSelected == null || Editor.Instance.SceneEditing.SelectionCount != 1)
|
||||
{
|
||||
_selectedTangentIn = null;
|
||||
_selectedTangentOut = null;
|
||||
return;
|
||||
}
|
||||
|
||||
var currentSelected = Editor.Instance.SceneEditing.Selection[0];
|
||||
|
||||
if (currentSelected is not SplineNode.SplinePointTangentNode selectedPoint)
|
||||
{
|
||||
_selectedTangentIn = null;
|
||||
_selectedTangentOut = null;
|
||||
return;
|
||||
}
|
||||
|
||||
if (currentSelected == _selectedTangentIn)
|
||||
return;
|
||||
if (currentSelected == _selectedTangentOut)
|
||||
return;
|
||||
|
||||
var index = _lastPointSelected.Index;
|
||||
|
||||
if (currentSelected.Transform == _selectedSpline.GetSplineTangent(index, true))
|
||||
{
|
||||
_selectedTangentIn = selectedPoint;
|
||||
_selectedTangentOut = null;
|
||||
_currentTangentMode.OnSelectTangent(_selectedSpline, index);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (currentSelected.Transform == _selectedSpline.GetSplineTangent(index, false))
|
||||
{
|
||||
_selectedTangentOut = selectedPoint;
|
||||
_selectedTangentIn = null;
|
||||
_currentTangentMode.OnSelectTangent(_selectedSpline, index);
|
||||
return;
|
||||
}
|
||||
|
||||
_selectedTangentIn = null;
|
||||
_selectedTangentOut = null;
|
||||
}
|
||||
|
||||
private void StartEditSpline()
|
||||
{
|
||||
if (Presenter.Undo != null && Presenter.Undo.Enabled)
|
||||
{
|
||||
// Capture 'before' state for undo
|
||||
var splines = new List<UndoData>();
|
||||
for (int i = 0; i < Values.Count; i++)
|
||||
{
|
||||
if (Values[i] is Spline spline)
|
||||
{
|
||||
splines.Add(new UndoData
|
||||
{
|
||||
Spline = spline,
|
||||
BeforeKeyframes = spline.SplineKeyframes.Clone() as BezierCurve<Transform>.Keyframe[]
|
||||
});
|
||||
}
|
||||
}
|
||||
_selectedSplinesUndoData = splines.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
private void EndEditSpline()
|
||||
{
|
||||
// Update buttons state
|
||||
UpdateEditTabsSelection();
|
||||
|
||||
if (Presenter.Undo != null && Presenter.Undo.Enabled)
|
||||
{
|
||||
// Add undo
|
||||
foreach (var splineUndoData in _selectedSplinesUndoData)
|
||||
{
|
||||
Presenter.Undo.AddAction(new EditSplineAction(_selectedSpline, splineUndoData.BeforeKeyframes));
|
||||
SplineNode.OnSplineEdited(splineUndoData.Spline);
|
||||
Editor.Instance.Scene.MarkSceneEdited(splineUndoData.Spline.Scene);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnSetSelectedLinear()
|
||||
{
|
||||
StartEditSpline();
|
||||
SetModeLinear();
|
||||
EndEditSpline();
|
||||
}
|
||||
|
||||
private void OnSetSelectedFree()
|
||||
{
|
||||
StartEditSpline();
|
||||
SetModeFree();
|
||||
EndEditSpline();
|
||||
}
|
||||
|
||||
private void OnSetSelectedAligned()
|
||||
{
|
||||
StartEditSpline();
|
||||
SetModeAligned();
|
||||
EndEditSpline();
|
||||
}
|
||||
|
||||
private void OnSetSelectedSmoothIn()
|
||||
{
|
||||
StartEditSpline();
|
||||
SetModeSmoothIn();
|
||||
EndEditSpline();
|
||||
}
|
||||
|
||||
private void OnSetSelectedSmoothOut()
|
||||
{
|
||||
StartEditSpline();
|
||||
SetModeSmoothOut();
|
||||
EndEditSpline();
|
||||
}
|
||||
|
||||
private void OnSetTangentsLinear()
|
||||
{
|
||||
var enableUndo = Presenter.Undo != null && Presenter.Undo.Enabled;
|
||||
for (int i = 0; i < Values.Count; i++)
|
||||
{
|
||||
if (Values[i] is Spline spline)
|
||||
{
|
||||
var before = enableUndo ? (BezierCurve<Transform>.Keyframe[])spline.SplineKeyframes.Clone() : null;
|
||||
spline.SetTangentsLinear();
|
||||
if (enableUndo)
|
||||
Presenter.Undo.AddAction(new EditSplineAction(spline, before));
|
||||
SplineNode.OnSplineEdited(spline);
|
||||
Editor.Instance.Scene.MarkSceneEdited(spline.Scene);
|
||||
}
|
||||
}
|
||||
StartEditSpline();
|
||||
_selectedSpline.SetTangentsLinear();
|
||||
_selectedSpline.UpdateSpline();
|
||||
EndEditSpline();
|
||||
}
|
||||
|
||||
private void OnSetTangentsSmooth()
|
||||
{
|
||||
var enableUndo = Presenter.Undo != null && Presenter.Undo.Enabled;
|
||||
for (int i = 0; i < Values.Count; i++)
|
||||
StartEditSpline();
|
||||
_selectedSpline.SetTangentsSmooth();
|
||||
_selectedSpline.UpdateSpline();
|
||||
EndEditSpline();
|
||||
}
|
||||
|
||||
private static bool IsFreeTangentMode(Spline spline, int index)
|
||||
{
|
||||
if (IsLinearTangentMode(spline, index) ||
|
||||
IsAlignedTangentMode(spline, index) ||
|
||||
IsSmoothInTangentMode(spline, index) ||
|
||||
IsSmoothOutTangentMode(spline, index))
|
||||
{
|
||||
if (Values[i] is Spline spline)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool IsLinearTangentMode(Spline spline, int index)
|
||||
{
|
||||
var keyframe = spline.GetSplineKeyframe(index);
|
||||
return keyframe.TangentIn.Translation.Length == 0 && keyframe.TangentOut.Translation.Length == 0;
|
||||
}
|
||||
|
||||
private static bool IsAlignedTangentMode(Spline spline, int index)
|
||||
{
|
||||
var keyframe = spline.GetSplineKeyframe(index);
|
||||
var tangentIn = keyframe.TangentIn.Translation;
|
||||
var tangentOut = keyframe.TangentOut.Translation;
|
||||
if (tangentIn.Length == 0 || tangentOut.Length == 0)
|
||||
return false;
|
||||
|
||||
var angleBetweenTwoTangents = Vector3.Dot(tangentIn.Normalized, tangentOut.Normalized);
|
||||
if (angleBetweenTwoTangents < -0.99f)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool IsSmoothInTangentMode(Spline spline, int index)
|
||||
{
|
||||
var keyframe = spline.GetSplineKeyframe(index);
|
||||
return keyframe.TangentIn.Translation.Length > 0 && keyframe.TangentOut.Translation.Length == 0;
|
||||
}
|
||||
|
||||
private static bool IsSmoothOutTangentMode(Spline spline, int index)
|
||||
{
|
||||
var keyframe = spline.GetSplineKeyframe(index);
|
||||
return keyframe.TangentOut.Translation.Length > 0 && keyframe.TangentIn.Translation.Length == 0;
|
||||
}
|
||||
|
||||
private static void SetKeyframeLinear(Spline spline, int index)
|
||||
{
|
||||
var keyframe = spline.GetSplineKeyframe(index);
|
||||
keyframe.TangentIn.Translation = Vector3.Zero;
|
||||
keyframe.TangentOut.Translation = Vector3.Zero;
|
||||
|
||||
var lastSplineIndex = spline.SplinePointsCount - 1;
|
||||
if (index == lastSplineIndex && spline.IsLoop)
|
||||
{
|
||||
var lastPoint = spline.GetSplineKeyframe(lastSplineIndex);
|
||||
lastPoint.TangentIn.Translation = Vector3.Zero;
|
||||
lastPoint.TangentOut.Translation = Vector3.Zero;
|
||||
spline.SetSplineKeyframe(lastSplineIndex, lastPoint);
|
||||
}
|
||||
|
||||
spline.SetSplineKeyframe(index, keyframe);
|
||||
spline.UpdateSpline();
|
||||
}
|
||||
|
||||
private static void SetTangentSmoothIn(Spline spline, int index)
|
||||
{
|
||||
var keyframe = spline.GetSplineKeyframe(index);
|
||||
|
||||
// Auto smooth tangent if's linear
|
||||
if (keyframe.TangentIn.Translation.Length == 0)
|
||||
{
|
||||
var smoothRange = SplineNode.NodeSizeByDistance(spline.GetSplineTangent(index, false).Translation, 10f);
|
||||
var previousKeyframe = spline.GetSplineKeyframe(index - 1);
|
||||
var tangentDirection = keyframe.Value.WorldToLocalVector(previousKeyframe.Value.Translation - keyframe.Value.Translation);
|
||||
tangentDirection = tangentDirection.Normalized * smoothRange;
|
||||
keyframe.TangentIn.Translation = tangentDirection;
|
||||
}
|
||||
|
||||
keyframe.TangentOut.Translation = Vector3.Zero;
|
||||
spline.SetSplineKeyframe(index, keyframe);
|
||||
spline.UpdateSpline();
|
||||
}
|
||||
|
||||
private static void SetTangentSmoothOut(Spline spline, int index)
|
||||
{
|
||||
var keyframe = spline.GetSplineKeyframe(index);
|
||||
|
||||
// Auto smooth tangent if's linear
|
||||
if (keyframe.TangentOut.Translation.Length == 0)
|
||||
{
|
||||
var smoothRange = SplineNode.NodeSizeByDistance(spline.GetSplineTangent(index, false).Translation, 10f);
|
||||
var nextKeyframe = spline.GetSplineKeyframe(index + 1);
|
||||
var tangentDirection = keyframe.Value.WorldToLocalVector(nextKeyframe.Value.Translation - keyframe.Value.Translation);
|
||||
tangentDirection = tangentDirection.Normalized * smoothRange;
|
||||
keyframe.TangentOut.Translation = tangentDirection;
|
||||
}
|
||||
|
||||
keyframe.TangentIn.Translation = Vector3.Zero;
|
||||
|
||||
spline.SetSplineKeyframe(index, keyframe);
|
||||
spline.UpdateSpline();
|
||||
}
|
||||
|
||||
private static void SetPointSmooth(Spline spline, int index)
|
||||
{
|
||||
var keyframe = spline.GetSplineKeyframe(index);
|
||||
var tangentInSize = keyframe.TangentIn.Translation.Length;
|
||||
var tangentOutSize = keyframe.TangentOut.Translation.Length;
|
||||
|
||||
var isLastKeyframe = index >= spline.SplinePointsCount - 1;
|
||||
var isFirstKeyframe = index <= 0;
|
||||
var smoothRange = SplineNode.NodeSizeByDistance(spline.GetSplinePoint(index), 10f);
|
||||
|
||||
// Force smooth it's linear point
|
||||
if (tangentInSize == 0f)
|
||||
tangentInSize = smoothRange;
|
||||
if (tangentOutSize == 0f)
|
||||
tangentOutSize = smoothRange;
|
||||
|
||||
// Try get next / last keyframe
|
||||
var nextKeyframe = !isLastKeyframe ? spline.GetSplineKeyframe(index + 1) : keyframe;
|
||||
var previousKeyframe = !isFirstKeyframe ? spline.GetSplineKeyframe(index - 1) : keyframe;
|
||||
|
||||
// calc form from Spline.cpp -> SetTangentsSmooth
|
||||
// get tangent direction
|
||||
var tangentDirection = (keyframe.Value.Translation - previousKeyframe.Value.Translation + nextKeyframe.Value.Translation - keyframe.Value.Translation).Normalized;
|
||||
|
||||
keyframe.TangentIn.Translation = -tangentDirection;
|
||||
keyframe.TangentOut.Translation = tangentDirection;
|
||||
|
||||
keyframe.TangentIn.Translation *= tangentInSize;
|
||||
keyframe.TangentOut.Translation *= tangentOutSize;
|
||||
|
||||
spline.SetSplineKeyframe(index, keyframe);
|
||||
spline.UpdateSpline();
|
||||
}
|
||||
|
||||
private static SplineNode.SplinePointNode GetSplinePointNode(Spline spline, int index)
|
||||
{
|
||||
return (SplineNode.SplinePointNode)SceneGraphFactory.FindNode(spline.ID).ChildNodes[index];
|
||||
}
|
||||
|
||||
private static SplineNode.SplinePointTangentNode GetSplineTangentInNode(Spline spline, int index)
|
||||
{
|
||||
var point = GetSplinePointNode(spline, index);
|
||||
var tangentIn = spline.GetSplineTangent(index, true);
|
||||
var tangentNodes = point.ChildNodes;
|
||||
|
||||
// find tangent in node comparing all child nodes position
|
||||
for (int i = 0; i < tangentNodes.Count; i++)
|
||||
{
|
||||
if (tangentNodes[i].Transform.Translation == tangentIn.Translation)
|
||||
{
|
||||
var before = enableUndo ? (BezierCurve<Transform>.Keyframe[])spline.SplineKeyframes.Clone() : null;
|
||||
spline.SetTangentsSmooth();
|
||||
if (enableUndo)
|
||||
Presenter.Undo.AddAction(new EditSplineAction(spline, before));
|
||||
SplineNode.OnSplineEdited(spline);
|
||||
Editor.Instance.Scene.MarkSceneEdited(spline.Scene);
|
||||
return (SplineNode.SplinePointTangentNode)tangentNodes[i];
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static SplineNode.SplinePointTangentNode GetSplineTangentOutNode(Spline spline, int index)
|
||||
{
|
||||
var point = GetSplinePointNode(spline, index);
|
||||
var tangentOut = spline.GetSplineTangent(index, false);
|
||||
var tangentNodes = point.ChildNodes;
|
||||
|
||||
// find tangent out node comparing all child nodes position
|
||||
for (int i = 0; i < tangentNodes.Count; i++)
|
||||
{
|
||||
if (tangentNodes[i].Transform.Translation == tangentOut.Translation)
|
||||
{
|
||||
return (SplineNode.SplinePointTangentNode)tangentNodes[i];
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static void SetSelectSplinePointNode(Spline spline, int index)
|
||||
{
|
||||
Editor.Instance.SceneEditing.Select(GetSplinePointNode(spline, index));
|
||||
}
|
||||
|
||||
private static void SetSelectTangentIn(Spline spline, int index)
|
||||
{
|
||||
Editor.Instance.SceneEditing.Select(GetSplineTangentInNode(spline, index));
|
||||
}
|
||||
|
||||
private static void SetSelectTangentOut(Spline spline, int index)
|
||||
{
|
||||
Editor.Instance.SceneEditing.Select(GetSplineTangentOutNode(spline, index));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -422,12 +422,14 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
|
||||
// Set control type button
|
||||
var space = layout.Space(20);
|
||||
float setTypeButtonWidth = 60.0f;
|
||||
var buttonText = "Set Type";
|
||||
var textSize = FlaxEngine.GUI.Style.Current.FontMedium.MeasureText(buttonText);
|
||||
float setTypeButtonWidth = (textSize.X < 60.0f) ? 60.0f : textSize.X + 4;
|
||||
var setTypeButton = new Button
|
||||
{
|
||||
TooltipText = "Sets the control to the given type",
|
||||
AnchorPreset = AnchorPresets.MiddleCenter,
|
||||
Text = "Set Type",
|
||||
Text = buttonText,
|
||||
Parent = space.Spacer,
|
||||
Bounds = new Rectangle((space.Spacer.Width - setTypeButtonWidth) / 2, 1, setTypeButtonWidth, 18),
|
||||
};
|
||||
|
||||
@@ -88,20 +88,20 @@ 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),
|
||||
Parent = LinkedLabel,
|
||||
Width = 18,
|
||||
Height = 18,
|
||||
AnchorPreset = AnchorPresets.TopLeft,
|
||||
AnchorPreset = AnchorPresets.MiddleLeft,
|
||||
};
|
||||
_linkButton.Clicked += ToggleLink;
|
||||
ToggleEnabled();
|
||||
SetLinkStyle();
|
||||
var x = LinkedLabel.Text.Value.Length * 7 + 5;
|
||||
_linkButton.LocalX += x;
|
||||
_linkButton.LocalY += 1;
|
||||
var textSize = FlaxEngine.GUI.Style.Current.FontMedium.MeasureText(LinkedLabel.Text.Value);
|
||||
_linkButton.LocalX += textSize.X + 10;
|
||||
LinkedLabel.SetupContextMenu += (label, menu, editor) =>
|
||||
{
|
||||
menu.AddSeparator();
|
||||
|
||||
@@ -34,7 +34,9 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
}
|
||||
else
|
||||
{
|
||||
element.CheckBox.Checked = (bool)Values[0];
|
||||
var value = (bool?)Values[0];
|
||||
if (value != null)
|
||||
element.CheckBox.Checked = value.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,146 @@
|
||||
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using FlaxEditor.CustomEditors.GUI;
|
||||
using FlaxEditor.GUI;
|
||||
using FlaxEditor.GUI.ContextMenu;
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.CustomEditors.Editors
|
||||
{
|
||||
/// <summary>
|
||||
/// Default implementation of the inspector used to edit input event properties.
|
||||
/// </summary>
|
||||
[CustomEditor(typeof(InputEvent)), DefaultEditor]
|
||||
public class InputEventEditor : CustomEditor
|
||||
{
|
||||
private ComboBox _comboBox;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DisplayStyle Style => DisplayStyle.Inline;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Initialize(LayoutElementsContainer layout)
|
||||
{
|
||||
LinkedLabel.SetupContextMenu += OnSetupContextMenu;
|
||||
var comboBoxElement = layout.ComboBox();
|
||||
_comboBox = comboBoxElement.ComboBox;
|
||||
var names = new List<string>();
|
||||
foreach (var mapping in Input.ActionMappings)
|
||||
{
|
||||
if (!names.Contains(mapping.Name))
|
||||
names.Add(mapping.Name);
|
||||
}
|
||||
_comboBox.Items = names;
|
||||
if (Values[0] is InputEvent inputEvent && names.Contains(inputEvent.Name))
|
||||
_comboBox.SelectedItem = inputEvent.Name;
|
||||
_comboBox.SelectedIndexChanged += OnSelectedIndexChanged;
|
||||
}
|
||||
|
||||
private void OnSetupContextMenu(PropertyNameLabel label, ContextMenu menu, CustomEditor linkededitor)
|
||||
{
|
||||
var button = menu.AddButton("Set to null");
|
||||
button.Clicked += () => _comboBox.SelectedItem = null;
|
||||
}
|
||||
|
||||
private void OnSelectedIndexChanged(ComboBox comboBox)
|
||||
{
|
||||
SetValue(comboBox.SelectedItem == null ? null : new InputEvent(comboBox.SelectedItem));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Refresh()
|
||||
{
|
||||
base.Refresh();
|
||||
|
||||
if (HasDifferentValues)
|
||||
{
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Values[0] is InputEvent inputEvent && _comboBox.Items.Contains(inputEvent.Name))
|
||||
_comboBox.SelectedItem = inputEvent.Name;
|
||||
else
|
||||
_comboBox.SelectedItem = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Deinitialize()
|
||||
{
|
||||
if (LinkedLabel != null)
|
||||
LinkedLabel.SetupContextMenu -= OnSetupContextMenu;
|
||||
if (_comboBox != null)
|
||||
_comboBox.SelectedIndexChanged -= OnSelectedIndexChanged;
|
||||
_comboBox = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Default implementation of the inspector used to edit input axis properties.
|
||||
/// </summary>
|
||||
[CustomEditor(typeof(InputAxis)), DefaultEditor]
|
||||
public class InputAxisEditor : CustomEditor
|
||||
{
|
||||
private ComboBox _comboBox;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DisplayStyle Style => DisplayStyle.Inline;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Initialize(LayoutElementsContainer layout)
|
||||
{
|
||||
LinkedLabel.SetupContextMenu += OnSetupContextMenu;
|
||||
var comboBoxElement = layout.ComboBox();
|
||||
_comboBox = comboBoxElement.ComboBox;
|
||||
var names = new List<string>();
|
||||
foreach (var mapping in Input.AxisMappings)
|
||||
{
|
||||
if (!names.Contains(mapping.Name))
|
||||
names.Add(mapping.Name);
|
||||
}
|
||||
_comboBox.Items = names;
|
||||
if (Values[0] is InputAxis inputAxis && names.Contains(inputAxis.Name))
|
||||
_comboBox.SelectedItem = inputAxis.Name;
|
||||
_comboBox.SelectedIndexChanged += OnSelectedIndexChanged;
|
||||
}
|
||||
|
||||
private void OnSetupContextMenu(PropertyNameLabel label, ContextMenu menu, CustomEditor linkededitor)
|
||||
{
|
||||
var button = menu.AddButton("Set to null");
|
||||
button.Clicked += () => _comboBox.SelectedItem = null;
|
||||
}
|
||||
|
||||
private void OnSelectedIndexChanged(ComboBox comboBox)
|
||||
{
|
||||
SetValue(comboBox.SelectedItem == null ? null : new InputAxis(comboBox.SelectedItem));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Refresh()
|
||||
{
|
||||
base.Refresh();
|
||||
|
||||
if (HasDifferentValues)
|
||||
{
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Values[0] is InputAxis inputAxis && _comboBox.Items.Contains(inputAxis.Name))
|
||||
_comboBox.SelectedItem = inputAxis.Name;
|
||||
else
|
||||
_comboBox.SelectedItem = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Deinitialize()
|
||||
{
|
||||
if (LinkedLabel != null)
|
||||
LinkedLabel.SetupContextMenu -= OnSetupContextMenu;
|
||||
if (_comboBox != null)
|
||||
_comboBox.SelectedIndexChanged -= OnSelectedIndexChanged;
|
||||
_comboBox = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
@@ -623,13 +623,18 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
{
|
||||
_label = layout.ClickableLabel(GetText(out _)).CustomControl;
|
||||
_label.RightClick += ShowPicker;
|
||||
var buttonText = "...";
|
||||
var button = new Button
|
||||
{
|
||||
Size = new Float2(16.0f),
|
||||
Text = "...",
|
||||
Text = buttonText,
|
||||
TooltipText = "Edit...",
|
||||
Parent = _label,
|
||||
};
|
||||
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;
|
||||
}
|
||||
@@ -674,9 +679,9 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
}
|
||||
set
|
||||
{
|
||||
if (Values[0] is Tag[])
|
||||
if (Values[0] is Tag[] || Values.Type.Type == typeof(Tag[]))
|
||||
SetValue(value);
|
||||
if (Values[0] is List<Tag>)
|
||||
else if (Values[0] is List<Tag> || Values.Type.Type == typeof(List<Tag>))
|
||||
SetValue(new List<Tag>(value));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -175,7 +175,7 @@ namespace FlaxEditor.CustomEditors.GUI
|
||||
{
|
||||
// Clear flag
|
||||
_mouseOverSplitter = false;
|
||||
|
||||
|
||||
if (_cursorChanged)
|
||||
{
|
||||
Cursor = CursorType.Default;
|
||||
|
||||
+65
-19
@@ -3,6 +3,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.InteropServices.Marshalling;
|
||||
@@ -13,6 +14,7 @@ using FlaxEditor.Content.Thumbnails;
|
||||
using FlaxEditor.Modules;
|
||||
using FlaxEditor.Modules.SourceCodeEditing;
|
||||
using FlaxEditor.Options;
|
||||
using FlaxEditor.SceneGraph.Actors;
|
||||
using FlaxEditor.States;
|
||||
using FlaxEditor.Windows;
|
||||
using FlaxEditor.Windows.Assets;
|
||||
@@ -933,21 +935,6 @@ namespace FlaxEditor
|
||||
Animation = 11,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Imports the audio asset file to the target location.
|
||||
/// </summary>
|
||||
/// <param name="inputPath">The source file path.</param>
|
||||
/// <param name="outputPath">The result asset file path.</param>
|
||||
/// <param name="settings">The settings.</param>
|
||||
/// <returns>True if importing failed, otherwise false.</returns>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Serializes the given object to json asset.
|
||||
/// </summary>
|
||||
@@ -1274,6 +1261,69 @@ namespace FlaxEditor
|
||||
Scene.MarkSceneEdited(scenes);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Bakes all environmental probes in the scene.
|
||||
/// </summary>
|
||||
public void BakeAllEnvProbes()
|
||||
{
|
||||
Scene.ExecuteOnGraph(node =>
|
||||
{
|
||||
if (node is EnvironmentProbeNode envProbeNode && envProbeNode.IsActive)
|
||||
{
|
||||
((EnvironmentProbe)envProbeNode.Actor).Bake();
|
||||
node.ParentScene.IsEdited = true;
|
||||
}
|
||||
else if (node is SkyLightNode skyLightNode && skyLightNode.IsActive && skyLightNode.Actor is SkyLight skyLight && skyLight.Mode == SkyLight.Modes.CaptureScene)
|
||||
{
|
||||
skyLight.Bake();
|
||||
node.ParentScene.IsEdited = true;
|
||||
}
|
||||
|
||||
return node.IsActive;
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds CSG for all open scenes.
|
||||
/// </summary>
|
||||
public void BuildCSG()
|
||||
{
|
||||
var scenes = Level.Scenes;
|
||||
scenes.ToList().ForEach(x => x.BuildCSG(0));
|
||||
Scene.MarkSceneEdited(scenes);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds Nav mesh for all open scenes.
|
||||
/// </summary>
|
||||
public void BuildNavMesh()
|
||||
{
|
||||
var scenes = Level.Scenes;
|
||||
scenes.ToList().ForEach(x => Navigation.BuildNavMesh(x, 0));
|
||||
Scene.MarkSceneEdited(scenes);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds SDF for all static models in the scene.
|
||||
/// </summary>
|
||||
public void BuildAllMeshesSDF()
|
||||
{
|
||||
// TODO: async maybe with progress reporting?
|
||||
Scene.ExecuteOnGraph(node =>
|
||||
{
|
||||
if (node is StaticModelNode staticModelNode && staticModelNode.Actor is StaticModel staticModel)
|
||||
{
|
||||
if (staticModel.DrawModes.HasFlag(DrawPass.GlobalSDF) && staticModel.Model != null && !staticModel.Model.IsVirtual && staticModel.Model.SDF.Texture == null)
|
||||
{
|
||||
Log("Generating SDF for " + staticModel.Model);
|
||||
if (!staticModel.Model.GenerateSDF())
|
||||
staticModel.Model.Save();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Internal Calls
|
||||
@@ -1602,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);
|
||||
|
||||
|
||||
@@ -36,7 +36,9 @@ namespace FlaxEditor
|
||||
|
||||
public static void OnEditorOptionsChanged(Options.EditorOptions options)
|
||||
{
|
||||
var param = _highlightMaterial?.GetParameter("Color");
|
||||
if (!_highlightMaterial)
|
||||
return;
|
||||
var param = _highlightMaterial.GetParameter("Color");
|
||||
if (param != null)
|
||||
param.Value = options.Visual.HighlightColor;
|
||||
}
|
||||
|
||||
@@ -97,6 +97,10 @@ namespace FlaxEditor
|
||||
public SpriteHandle Build64;
|
||||
public SpriteHandle Add64;
|
||||
public SpriteHandle ShipIt64;
|
||||
public SpriteHandle SplineFree64;
|
||||
public SpriteHandle SplineLinear64;
|
||||
public SpriteHandle SplineAligned64;
|
||||
public SpriteHandle SplineSmoothIn64;
|
||||
|
||||
// 96px
|
||||
public SpriteHandle Toolbox96;
|
||||
|
||||
@@ -545,7 +545,7 @@ namespace FlaxEditor.GUI
|
||||
Render2D.DrawRectangle(clientRect.MakeExpanded(-2.0f), borderColor);
|
||||
|
||||
// Check if has selected item
|
||||
if (_selectedIndices.Count > 0)
|
||||
if (_selectedIndices != null && _selectedIndices.Count > 0)
|
||||
{
|
||||
string text = _selectedIndices.Count == 1 ? _items[_selectedIndices[0]] : "Multiple Values";
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using FlaxEditor.Options;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
|
||||
@@ -269,6 +270,24 @@ namespace FlaxEditor.GUI.ContextMenu
|
||||
return item;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the button.
|
||||
/// </summary>
|
||||
/// <param name="text">The text.</param>
|
||||
/// <param name="binding">The input binding.</param>
|
||||
/// <param name="clicked">On button clicked event.</param>
|
||||
/// <returns>Created context menu item control.</returns>
|
||||
public ContextMenuButton AddButton(string text, InputBinding binding, Action clicked)
|
||||
{
|
||||
var item = new ContextMenuButton(this, text, binding.ToString())
|
||||
{
|
||||
Parent = _panel
|
||||
};
|
||||
item.Clicked += clicked;
|
||||
SortButtons();
|
||||
return item;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the child menu (with that name).
|
||||
/// </summary>
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -290,7 +290,11 @@ namespace FlaxEditor.GUI.Dialogs
|
||||
OnCancel();
|
||||
return true;
|
||||
case KeyboardKeys.Tab:
|
||||
Root?.Navigate(NavDirection.Next);
|
||||
if (Root != null)
|
||||
{
|
||||
bool shiftDown = Root.GetKey(KeyboardKeys.Shift);
|
||||
Root.Navigate(shiftDown ? NavDirection.Previous : NavDirection.Next);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
@@ -476,6 +476,7 @@ namespace FlaxEditor.GUI.Docking
|
||||
settings.ShowInTaskbar = false;
|
||||
settings.ActivateWhenFirstShown = false;
|
||||
settings.IsTopmost = true;
|
||||
settings.ShowAfterFirstPaint = false;
|
||||
|
||||
win = Platform.CreateWindow(ref settings);
|
||||
|
||||
|
||||
@@ -292,7 +292,8 @@ namespace FlaxEditor.GUI
|
||||
return true;
|
||||
|
||||
#if PLATFORM_WINDOWS
|
||||
if (_useCustomWindowSystem)
|
||||
var child = GetChildAtRecursive(location);
|
||||
if (_useCustomWindowSystem && child is not Button && child is not MainMenuButton)
|
||||
{
|
||||
if (_window.IsMaximized)
|
||||
_window.Restore();
|
||||
|
||||
@@ -37,6 +37,9 @@ namespace FlaxEditor.GUI
|
||||
: base(0, 0, 100, height)
|
||||
{
|
||||
Depth = -1;
|
||||
|
||||
if (Height < Style.Current.FontMedium.Height)
|
||||
Height = Style.Current.FontMedium.Height + 4;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -77,5 +77,14 @@ namespace FlaxEditor.GUI.Tabs
|
||||
{
|
||||
Deselected?.Invoke(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Factory method for tabs header control (UI for the tabs strip to represent this tab).
|
||||
/// </summary>
|
||||
/// <returns>The tab header control.</returns>
|
||||
public virtual Tabs.TabHeader CreateHeader()
|
||||
{
|
||||
return new Tabs.TabHeader((Tabs)Parent, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,11 +24,6 @@ namespace FlaxEditor.GUI.Tabs
|
||||
/// </summary>
|
||||
protected Tabs Tabs;
|
||||
|
||||
/// <summary>
|
||||
/// The index of the tab.
|
||||
/// </summary>
|
||||
protected int Index;
|
||||
|
||||
/// <summary>
|
||||
/// The tab.
|
||||
/// </summary>
|
||||
@@ -38,21 +33,22 @@ namespace FlaxEditor.GUI.Tabs
|
||||
/// Initializes a new instance of the <see cref="TabHeader"/> class.
|
||||
/// </summary>
|
||||
/// <param name="tabs">The tabs.</param>
|
||||
/// <param name="index">The tab index.</param>
|
||||
/// <param name="tab">The tab.</param>
|
||||
public TabHeader(Tabs tabs, int index, Tab tab)
|
||||
public TabHeader(Tabs tabs, Tab tab)
|
||||
: base(Float2.Zero, tabs._tabsSize)
|
||||
{
|
||||
Tabs = tabs;
|
||||
Index = index;
|
||||
Tab = tab;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool OnMouseUp(Float2 location, MouseButton button)
|
||||
{
|
||||
Tabs.SelectedTabIndex = Index;
|
||||
Tabs.Focus();
|
||||
if (EnabledInHierarchy && Tab.Enabled)
|
||||
{
|
||||
Tabs.SelectedTab = Tab;
|
||||
Tabs.Focus();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -61,19 +57,20 @@ namespace FlaxEditor.GUI.Tabs
|
||||
{
|
||||
base.Draw();
|
||||
|
||||
// Cache data
|
||||
var style = Style.Current;
|
||||
var enabled = EnabledInHierarchy && Tab.EnabledInHierarchy;
|
||||
var tabRect = new Rectangle(Float2.Zero, Size);
|
||||
bool isTabSelected = Tabs._selectedIndex == Index;
|
||||
bool isMouseOverTab = IsMouseOver;
|
||||
var textOffset = Tabs._orientation == Orientation.Horizontal ? 0 : 8;
|
||||
|
||||
// Draw bar
|
||||
if (isTabSelected)
|
||||
if (Tabs.SelectedTab == Tab)
|
||||
{
|
||||
var color = style.BackgroundSelected;
|
||||
if (!enabled)
|
||||
color *= 0.6f;
|
||||
if (Tabs._orientation == Orientation.Horizontal)
|
||||
{
|
||||
Render2D.FillRectangle(tabRect, style.BackgroundSelected);
|
||||
Render2D.FillRectangle(tabRect, color);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -84,10 +81,10 @@ namespace FlaxEditor.GUI.Tabs
|
||||
fillRect.Size.X -= lefEdgeWidth;
|
||||
fillRect.Location.X += lefEdgeWidth;
|
||||
Render2D.FillRectangle(fillRect, style.Background);
|
||||
Render2D.FillRectangle(leftEdgeRect, style.BackgroundSelected);
|
||||
Render2D.FillRectangle(leftEdgeRect, color);
|
||||
}
|
||||
}
|
||||
else if (isMouseOverTab)
|
||||
else if (IsMouseOver && enabled)
|
||||
{
|
||||
Render2D.FillRectangle(tabRect, style.BackgroundHighlighted);
|
||||
}
|
||||
@@ -131,20 +128,15 @@ namespace FlaxEditor.GUI.Tabs
|
||||
{
|
||||
base.PerformLayoutBeforeChildren();
|
||||
|
||||
// Cache data
|
||||
var tabsSize = Tabs._tabsSize;
|
||||
var clientSize = GetClientArea();
|
||||
tabsSize = Float2.Min(tabsSize, clientSize.Size);
|
||||
var tabRect = new Rectangle(Float2.Zero, tabsSize);
|
||||
var tabStripOffset = Tabs._orientation == Orientation.Horizontal ? new Float2(tabsSize.X, 0) : new Float2(0, tabsSize.Y);
|
||||
|
||||
// Arrange tab header controls
|
||||
var pos = Float2.Zero;
|
||||
var sizeMask = Tabs._orientation == Orientation.Horizontal ? Float2.UnitX : Float2.UnitY;
|
||||
for (int i = 0; i < Children.Count; i++)
|
||||
{
|
||||
if (Children[i] is TabHeader tabHeader)
|
||||
{
|
||||
tabHeader.Bounds = tabRect;
|
||||
tabRect.Offset(tabStripOffset);
|
||||
tabHeader.Location = pos;
|
||||
pos += tabHeader.Size * sizeMask;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -160,6 +152,11 @@ namespace FlaxEditor.GUI.Tabs
|
||||
/// </summary>
|
||||
protected Float2 _tabsSize;
|
||||
|
||||
/// <summary>
|
||||
/// Automatic tab size based on the fill axis.
|
||||
/// </summary>
|
||||
protected bool _autoTabsSizeAuto;
|
||||
|
||||
/// <summary>
|
||||
/// The orientation.
|
||||
/// </summary>
|
||||
@@ -174,15 +171,31 @@ namespace FlaxEditor.GUI.Tabs
|
||||
set
|
||||
{
|
||||
_tabsSize = value;
|
||||
for (int i = 0; i < TabsPanel.ChildrenCount; i++)
|
||||
if (!_autoTabsSizeAuto)
|
||||
{
|
||||
if (TabsPanel.Children[i] is TabHeader tabHeader)
|
||||
tabHeader.Size = value;
|
||||
for (int i = 0; i < TabsPanel.ChildrenCount; i++)
|
||||
{
|
||||
if (TabsPanel.Children[i] is TabHeader tabHeader)
|
||||
tabHeader.Size = value;
|
||||
}
|
||||
}
|
||||
PerformLayout();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enables automatic tabs size to fill the space.
|
||||
/// </summary>
|
||||
public bool AutoTabsSize
|
||||
{
|
||||
get => _autoTabsSizeAuto;
|
||||
set
|
||||
{
|
||||
_autoTabsSizeAuto = value;
|
||||
PerformLayout();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the orientation.
|
||||
/// </summary>
|
||||
@@ -339,14 +352,10 @@ namespace FlaxEditor.GUI.Tabs
|
||||
|
||||
// Update tabs headers
|
||||
TabsPanel.DisposeChildren();
|
||||
int index = 0;
|
||||
for (int i = 0; i < Children.Count; i++)
|
||||
{
|
||||
if (Children[i] is Tab tab)
|
||||
{
|
||||
var tabHeader = new TabHeader(this, index++, tab);
|
||||
TabsPanel.AddChild(tabHeader);
|
||||
}
|
||||
TabsPanel.AddChild(tab.CreateHeader());
|
||||
}
|
||||
|
||||
TabsPanel.IsLayoutLocked = wasLocked;
|
||||
@@ -371,23 +380,46 @@ namespace FlaxEditor.GUI.Tabs
|
||||
/// <inheritdoc />
|
||||
protected override void PerformLayoutBeforeChildren()
|
||||
{
|
||||
var tabsSize = _tabsSize;
|
||||
if (_autoTabsSizeAuto)
|
||||
{
|
||||
// Horizontal is default for Toolbox so tabs go to the right
|
||||
int tabsCount = 0;
|
||||
for (int i = 0; i < TabsPanel.ChildrenCount; i++)
|
||||
{
|
||||
if (TabsPanel.Children[i] is TabHeader)
|
||||
tabsCount++;
|
||||
}
|
||||
if (tabsCount == 0)
|
||||
tabsCount = 1;
|
||||
if (_orientation == Orientation.Horizontal)
|
||||
tabsSize.X = Width / tabsCount;
|
||||
else
|
||||
tabsSize.Y = Height / tabsCount;
|
||||
for (int i = 0; i < TabsPanel.ChildrenCount; i++)
|
||||
{
|
||||
if (TabsPanel.Children[i] is TabHeader tabHeader)
|
||||
tabHeader.Size = tabsSize;
|
||||
}
|
||||
}
|
||||
|
||||
// Fit the tabs panel
|
||||
TabsPanel.Size = _orientation == Orientation.Horizontal ? new Float2(Width, _tabsSize.Y) : new Float2(_tabsSize.X, Height);
|
||||
TabsPanel.Size = _orientation == Orientation.Horizontal
|
||||
? new Float2(Width, tabsSize.Y)
|
||||
: new Float2(tabsSize.X, Height);
|
||||
|
||||
// Hide all pages except selected one
|
||||
var clientArea = _orientation == Orientation.Horizontal
|
||||
? new Rectangle(0, _tabsSize.Y, Width, Height - _tabsSize.Y)
|
||||
: new Rectangle(_tabsSize.X, 0, Width - _tabsSize.X, Height);
|
||||
for (int i = 0; i < Children.Count; i++)
|
||||
{
|
||||
if (Children[i] is Tab tab)
|
||||
{
|
||||
// Check if is selected or not
|
||||
if (i - 1 == _selectedIndex)
|
||||
{
|
||||
// Show and fit size
|
||||
tab.Visible = true;
|
||||
tab.Bounds = clientArea;
|
||||
tab.Bounds = _orientation == Orientation.Horizontal
|
||||
? new Rectangle(0, tabsSize.Y, Width, Height - tabsSize.Y)
|
||||
: new Rectangle(tabsSize.X, 0, Width - tabsSize.X, Height);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -33,7 +33,7 @@ namespace FlaxEditor.GUI.Timeline
|
||||
/// </summary>
|
||||
public SceneAnimationPlayer Player
|
||||
{
|
||||
get => _player;
|
||||
get => _player != null ? _player : null;
|
||||
set
|
||||
{
|
||||
if (_player == value)
|
||||
@@ -268,7 +268,7 @@ namespace FlaxEditor.GUI.Timeline
|
||||
/// <inheritdoc />
|
||||
public override void OnSeek(int frame)
|
||||
{
|
||||
if (_player?.Animation)
|
||||
if (_player != null && _player.Animation)
|
||||
{
|
||||
_player.Animation.WaitForLoaded();
|
||||
_player.Time = frame / _player.Animation.FramesPerSecond;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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<byte> 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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
/// <summary>
|
||||
/// Imports the audio asset file to the target location.
|
||||
/// </summary>
|
||||
/// <param name="inputPath">The source file path.</param>
|
||||
/// <param name="outputPath">The result asset file path.</param>
|
||||
/// <param name="options">The import settings.</param>
|
||||
/// <returns>True if importing failed, otherwise false.</returns>
|
||||
API_FUNCTION() static bool Import(const String& inputPath, const String& outputPath, const AudioTool::Options& options);
|
||||
|
||||
/// <summary>
|
||||
/// Tries the restore the asset import options from the target resource file.
|
||||
/// </summary>
|
||||
/// <param name="options">The options.</param>
|
||||
/// <param name="assetPath">The asset path.</param>
|
||||
/// <returns>True settings has been restored, otherwise false.</returns>
|
||||
API_FUNCTION() static bool TryRestoreImportOptions(API_PARAM(Ref) AudioTool::Options& options, String assetPath);
|
||||
#endif
|
||||
|
||||
private:
|
||||
void OnEditorAssemblyLoaded(MAssembly* assembly);
|
||||
|
||||
|
||||
@@ -643,7 +643,8 @@ namespace FlaxEditor.Modules
|
||||
/// Deletes the specified item.
|
||||
/// </summary>
|
||||
/// <param name="item">The item.</param>
|
||||
public void Delete(ContentItem item)
|
||||
/// <param name="deletedByUser">If the file was deleted by the user and not outside the editor.</param>
|
||||
public void Delete(ContentItem item, bool deletedByUser = false)
|
||||
{
|
||||
if (item == null)
|
||||
throw new ArgumentNullException();
|
||||
@@ -667,12 +668,12 @@ namespace FlaxEditor.Modules
|
||||
var children = folder.Children.ToArray();
|
||||
for (int i = 0; i < children.Length; i++)
|
||||
{
|
||||
Delete(children[i]);
|
||||
Delete(children[i], deletedByUser);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove directory
|
||||
if (Directory.Exists(path))
|
||||
if (deletedByUser && Directory.Exists(path))
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -701,7 +702,7 @@ namespace FlaxEditor.Modules
|
||||
// Delete asset by using content pool
|
||||
FlaxEngine.Content.DeleteAsset(path);
|
||||
}
|
||||
else
|
||||
else if (deletedByUser)
|
||||
{
|
||||
// Delete file
|
||||
if (File.Exists(path))
|
||||
@@ -847,7 +848,7 @@ namespace FlaxEditor.Modules
|
||||
Editor.Log(string.Format($"Content item \'{child.Path}\' has been removed"));
|
||||
|
||||
// Destroy it
|
||||
Delete(child);
|
||||
Delete(child, false);
|
||||
|
||||
i--;
|
||||
}
|
||||
@@ -930,6 +931,11 @@ namespace FlaxEditor.Modules
|
||||
// Check if node already has that element (skip during init when we want to walk project dir very fast)
|
||||
if (_isDuringFastSetup || !parent.Folder.ContainsChild(path))
|
||||
{
|
||||
#if PLATFORM_MAC
|
||||
if (path.EndsWith(".DS_Store", StringComparison.Ordinal))
|
||||
continue;
|
||||
#endif
|
||||
|
||||
// Create file item
|
||||
ContentItem item;
|
||||
if (path.EndsWith(".cs"))
|
||||
@@ -971,6 +977,11 @@ namespace FlaxEditor.Modules
|
||||
// Check if node already has that element (skip during init when we want to walk project dir very fast)
|
||||
if (_isDuringFastSetup || !parent.Folder.ContainsChild(path))
|
||||
{
|
||||
#if PLATFORM_MAC
|
||||
if (path.EndsWith(".DS_Store", StringComparison.Ordinal))
|
||||
continue;
|
||||
#endif
|
||||
|
||||
// Create file item
|
||||
ContentItem item = null;
|
||||
if (FlaxEngine.Content.GetAssetInfo(path, out var assetInfo))
|
||||
@@ -1196,6 +1207,7 @@ namespace FlaxEditor.Modules
|
||||
{
|
||||
case WatcherChangeTypes.Created:
|
||||
case WatcherChangeTypes.Deleted:
|
||||
case WatcherChangeTypes.Renamed:
|
||||
{
|
||||
lock (_dirtyNodes)
|
||||
{
|
||||
|
||||
@@ -90,6 +90,12 @@ namespace FlaxEditor.Modules
|
||||
hint = "Too long name.";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (item.IsFolder && shortName.EndsWith("."))
|
||||
{
|
||||
hint = "Name cannot end with '.'";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Find invalid characters
|
||||
if (Utilities.Utils.HasInvalidPathChar(shortName))
|
||||
@@ -120,7 +126,7 @@ namespace FlaxEditor.Modules
|
||||
// Cache data
|
||||
string sourcePath = item.Path;
|
||||
string sourceFolder = System.IO.Path.GetDirectoryName(sourcePath);
|
||||
string extension = System.IO.Path.GetExtension(sourcePath);
|
||||
string extension = item.IsFolder ? "" : System.IO.Path.GetExtension(sourcePath);
|
||||
string destinationPath = StringUtils.CombinePaths(sourceFolder, shortName + extension);
|
||||
|
||||
if (item.IsFolder)
|
||||
|
||||
@@ -37,11 +37,6 @@ namespace FlaxEditor.Modules
|
||||
/// </summary>
|
||||
public event Action<Prefab, Actor> PrefabApplied;
|
||||
|
||||
/// <summary>
|
||||
/// Locally cached actor for prefab creation.
|
||||
/// </summary>
|
||||
private Actor _prefabCreationActor;
|
||||
|
||||
internal PrefabsModule(Editor editor)
|
||||
: base(editor)
|
||||
{
|
||||
@@ -65,13 +60,14 @@ namespace FlaxEditor.Modules
|
||||
/// To create prefab manually (from code) use <see cref="PrefabManager.CreatePrefab"/> method.
|
||||
/// </remarks>
|
||||
/// <param name="selection">The scene selection to use.</param>
|
||||
public void CreatePrefab(List<SceneGraphNode> selection)
|
||||
/// <param name="prefabWindow">The prefab window that creates it.</param>
|
||||
public void CreatePrefab(List<SceneGraphNode> 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
|
||||
/// </summary>
|
||||
/// <param name="actor">The root prefab actor.</param>
|
||||
/// <param name="rename">Allow renaming or not</param>
|
||||
public void CreatePrefab(Actor actor, bool rename)
|
||||
/// <param name="prefabWindow">The prefab window that creates it.</param>
|
||||
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<Prefab>();
|
||||
_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<Actor>();
|
||||
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();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -129,6 +129,7 @@ namespace FlaxEditor.Modules
|
||||
else
|
||||
{
|
||||
Editor.UI.UpdateProgress(string.Empty, 0);
|
||||
Editor.UI.UpdateStatusBar();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -42,8 +42,10 @@ namespace FlaxEditor.Modules
|
||||
|
||||
private ContextMenuButton _menuFileSaveScenes;
|
||||
private ContextMenuButton _menuFileCloseScenes;
|
||||
private ContextMenuButton _menuFileOpenScriptsProject;
|
||||
private ContextMenuButton _menuFileGenerateScriptsProjectFiles;
|
||||
private ContextMenuButton _menuSaveAll;
|
||||
private ContextMenuButton _menuFileRecompileScripts;
|
||||
private ContextMenuButton _menuFileSaveAll;
|
||||
private ContextMenuButton _menuEditUndo;
|
||||
private ContextMenuButton _menuEditRedo;
|
||||
private ContextMenuButton _menuEditCut;
|
||||
@@ -62,15 +64,19 @@ namespace FlaxEditor.Modules
|
||||
private ContextMenuButton _menuGamePlayCurrentScenes;
|
||||
private ContextMenuButton _menuGameStop;
|
||||
private ContextMenuButton _menuGamePause;
|
||||
private ContextMenuButton _menuGameCookAndRun;
|
||||
private ContextMenuButton _menuGameRunCookedGame;
|
||||
private ContextMenuButton _menuToolsBuildScenes;
|
||||
private ContextMenuButton _menuToolsBakeLightmaps;
|
||||
private ContextMenuButton _menuToolsClearLightmaps;
|
||||
private ContextMenuButton _menuToolsBakeAllEnvProbes;
|
||||
private ContextMenuButton _menuToolsBuildCSGMesh;
|
||||
private ContextMenuButton _menuToolsBuildNavMesh;
|
||||
private ContextMenuButton _menuToolsBuildAllMesgesSDF;
|
||||
private ContextMenuButton _menuToolsBuildAllMeshesSDF;
|
||||
private ContextMenuButton _menuToolsCancelBuilding;
|
||||
private ContextMenuButton _menuToolsProfilerWindow;
|
||||
private ContextMenuButton _menuToolsSetTheCurrentSceneViewAsDefault;
|
||||
private ContextMenuButton _menuToolsTakeScreenshot;
|
||||
private ContextMenuChildMenu _menuWindowApplyWindowLayout;
|
||||
|
||||
private ToolStripButton _toolStripSaveAll;
|
||||
@@ -279,7 +285,7 @@ namespace FlaxEditor.Modules
|
||||
|
||||
Color color;
|
||||
if (Editor.StateMachine.IsPlayMode)
|
||||
color = Color.OrangeRed;
|
||||
color = Style.Current.Statusbar.PlayMode;
|
||||
else
|
||||
color = Style.Current.BackgroundSelected;
|
||||
|
||||
@@ -293,6 +299,11 @@ namespace FlaxEditor.Modules
|
||||
else
|
||||
text = "Ready";
|
||||
|
||||
if(ProgressVisible)
|
||||
{
|
||||
color = Style.Current.Statusbar.Loading;
|
||||
}
|
||||
|
||||
StatusBar.Text = text;
|
||||
StatusBar.StatusColor = color;
|
||||
_contentStats = contentStats;
|
||||
@@ -338,7 +349,7 @@ namespace FlaxEditor.Modules
|
||||
internal void ProgressFailed(string message)
|
||||
{
|
||||
_progressFailed = true;
|
||||
StatusBar.StatusColor = Color.Red;
|
||||
StatusBar.StatusColor = Style.Current.Statusbar.Failed;
|
||||
StatusBar.Text = message;
|
||||
_outputLogButton.Visible = true;
|
||||
}
|
||||
@@ -391,6 +402,10 @@ namespace FlaxEditor.Modules
|
||||
{
|
||||
UpdateStatusBar();
|
||||
}
|
||||
else if(ProgressVisible)
|
||||
{
|
||||
UpdateStatusBar();
|
||||
}
|
||||
}
|
||||
|
||||
private class CustomWindowBorderControl : Control
|
||||
@@ -510,13 +525,13 @@ namespace FlaxEditor.Modules
|
||||
MenuFile = MainMenu.AddButton("File");
|
||||
var cm = MenuFile.ContextMenu;
|
||||
cm.VisibleChanged += OnMenuFileShowHide;
|
||||
_menuSaveAll = cm.AddButton("Save All", inputOptions.Save.ToString(), Editor.SaveAll);
|
||||
_menuFileSaveScenes = cm.AddButton("Save scenes", Editor.Scene.SaveScenes);
|
||||
_menuFileCloseScenes = cm.AddButton("Close scenes", Editor.Scene.CloseAllScenes);
|
||||
_menuFileSaveAll = cm.AddButton("Save All", inputOptions.Save, Editor.SaveAll);
|
||||
_menuFileSaveScenes = cm.AddButton("Save scenes", inputOptions.SaveScenes, Editor.Scene.SaveScenes);
|
||||
_menuFileCloseScenes = cm.AddButton("Close scenes", inputOptions.CloseScenes, Editor.Scene.CloseAllScenes);
|
||||
cm.AddSeparator();
|
||||
cm.AddButton("Open scripts project", Editor.CodeEditing.OpenSolution);
|
||||
_menuFileGenerateScriptsProjectFiles = cm.AddButton("Generate scripts project files", Editor.ProgressReporting.GenerateScriptsProjectFiles.RunAsync);
|
||||
cm.AddButton("Recompile scripts", ScriptsBuilder.Compile);
|
||||
_menuFileOpenScriptsProject = cm.AddButton("Open scripts project", inputOptions.OpenScriptsProject, Editor.CodeEditing.OpenSolution);
|
||||
_menuFileGenerateScriptsProjectFiles = cm.AddButton("Generate scripts project files", inputOptions.GenerateScriptsProject, Editor.ProgressReporting.GenerateScriptsProjectFiles.RunAsync);
|
||||
_menuFileRecompileScripts = cm.AddButton("Recompile scripts", inputOptions.RecompileScripts, ScriptsBuilder.Compile);
|
||||
cm.AddSeparator();
|
||||
cm.AddButton("Open project...", OpenProject);
|
||||
cm.AddSeparator();
|
||||
@@ -526,27 +541,34 @@ namespace FlaxEditor.Modules
|
||||
MenuEdit = MainMenu.AddButton("Edit");
|
||||
cm = MenuEdit.ContextMenu;
|
||||
cm.VisibleChanged += OnMenuEditShowHide;
|
||||
_menuEditUndo = cm.AddButton(string.Empty, inputOptions.Undo.ToString(), Editor.PerformUndo);
|
||||
_menuEditRedo = cm.AddButton(string.Empty, inputOptions.Redo.ToString(), Editor.PerformRedo);
|
||||
_menuEditUndo = cm.AddButton(string.Empty, inputOptions.Undo, Editor.PerformUndo);
|
||||
_menuEditRedo = cm.AddButton(string.Empty, inputOptions.Redo, Editor.PerformRedo);
|
||||
cm.AddSeparator();
|
||||
_menuEditCut = cm.AddButton("Cut", inputOptions.Cut.ToString(), Editor.SceneEditing.Cut);
|
||||
_menuEditCopy = cm.AddButton("Copy", inputOptions.Copy.ToString(), Editor.SceneEditing.Copy);
|
||||
_menuEditPaste = cm.AddButton("Paste", inputOptions.Paste.ToString(), Editor.SceneEditing.Paste);
|
||||
_menuEditCut = cm.AddButton("Cut", inputOptions.Cut, Editor.SceneEditing.Cut);
|
||||
_menuEditCopy = cm.AddButton("Copy", inputOptions.Copy, Editor.SceneEditing.Copy);
|
||||
_menuEditPaste = cm.AddButton("Paste", inputOptions.Paste, Editor.SceneEditing.Paste);
|
||||
cm.AddSeparator();
|
||||
_menuEditDelete = cm.AddButton("Delete", inputOptions.Delete.ToString(), Editor.SceneEditing.Delete);
|
||||
_menuEditDuplicate = cm.AddButton("Duplicate", inputOptions.Duplicate.ToString(), Editor.SceneEditing.Duplicate);
|
||||
_menuEditDelete = cm.AddButton("Delete", inputOptions.Delete, Editor.SceneEditing.Delete);
|
||||
_menuEditDuplicate = cm.AddButton("Duplicate", inputOptions.Duplicate, Editor.SceneEditing.Duplicate);
|
||||
cm.AddSeparator();
|
||||
_menuEditSelectAll = cm.AddButton("Select all", inputOptions.SelectAll.ToString(), Editor.SceneEditing.SelectAllScenes);
|
||||
_menuEditFind = cm.AddButton("Find", inputOptions.Search.ToString(), Editor.Windows.SceneWin.Search);
|
||||
_menuEditSelectAll = cm.AddButton("Select all", inputOptions.SelectAll, Editor.SceneEditing.SelectAllScenes);
|
||||
_menuEditFind = cm.AddButton("Find", inputOptions.Search, Editor.Windows.SceneWin.Search);
|
||||
cm.AddSeparator();
|
||||
cm.AddButton("Game Settings", () =>
|
||||
{
|
||||
var item = Editor.ContentDatabase.Find(GameSettings.GameSettingsAssetPath);
|
||||
if(item != null)
|
||||
Editor.ContentEditing.Open(item);
|
||||
});
|
||||
|
||||
// Scene
|
||||
MenuScene = MainMenu.AddButton("Scene");
|
||||
cm = MenuScene.ContextMenu;
|
||||
cm.VisibleChanged += OnMenuSceneShowHide;
|
||||
_menuSceneMoveActorToViewport = cm.AddButton("Move actor to viewport", MoveActorToViewport);
|
||||
_menuSceneAlignActorWithViewport = cm.AddButton("Align actor with viewport", AlignActorWithViewport);
|
||||
_menuSceneAlignViewportWithActor = cm.AddButton("Align viewport with actor", AlignViewportWithActor);
|
||||
_menuScenePilotActor = cm.AddButton("Pilot actor", PilotActor);
|
||||
_menuSceneMoveActorToViewport = cm.AddButton("Move actor to viewport", inputOptions.MoveActorToViewport, MoveActorToViewport);
|
||||
_menuSceneAlignActorWithViewport = cm.AddButton("Align actor with viewport", inputOptions.AlignActorWithViewport, AlignActorWithViewport);
|
||||
_menuSceneAlignViewportWithActor = cm.AddButton("Align viewport with actor", inputOptions.AlignViewportWithActor, AlignViewportWithActor);
|
||||
_menuScenePilotActor = cm.AddButton("Pilot actor", inputOptions.PilotActor, PilotActor);
|
||||
cm.AddSeparator();
|
||||
_menuSceneCreateTerrain = cm.AddButton("Create terrain", CreateTerrain);
|
||||
|
||||
@@ -555,39 +577,41 @@ namespace FlaxEditor.Modules
|
||||
cm = MenuGame.ContextMenu;
|
||||
cm.VisibleChanged += OnMenuGameShowHide;
|
||||
|
||||
_menuGamePlayGame = cm.AddButton("Play Game", Editor.Simulation.RequestPlayGameOrStopPlay);
|
||||
_menuGamePlayCurrentScenes = cm.AddButton("Play Current Scenes", Editor.Simulation.RequestPlayScenesOrStopPlay);
|
||||
_menuGameStop = cm.AddButton("Stop Game", Editor.Simulation.RequestStopPlay);
|
||||
_menuGamePause = cm.AddButton("Pause", inputOptions.Pause.ToString(), Editor.Simulation.RequestPausePlay);
|
||||
_menuGamePlayGame = cm.AddButton("Play Game", inputOptions.Play, Editor.Simulation.RequestPlayGameOrStopPlay);
|
||||
_menuGamePlayCurrentScenes = cm.AddButton("Play Current Scenes", inputOptions.PlayCurrentScenes, Editor.Simulation.RequestPlayScenesOrStopPlay);
|
||||
_menuGameStop = cm.AddButton("Stop Game", inputOptions.Play, Editor.Simulation.RequestStopPlay);
|
||||
_menuGamePause = cm.AddButton("Pause", inputOptions.Pause, Editor.Simulation.RequestPausePlay);
|
||||
|
||||
cm.AddSeparator();
|
||||
var numberOfClientsMenu = cm.AddChildMenu("Number of game clients");
|
||||
_numberOfClientsGroup.AddItemsToContextMenu(numberOfClientsMenu.ContextMenu);
|
||||
|
||||
cm.AddSeparator();
|
||||
cm.AddButton("Cook & Run", Editor.Windows.GameCookerWin.BuildAndRun).LinkTooltip("Runs Game Cooker to build the game for this platform and runs the game after.");
|
||||
cm.AddButton("Run cooked game", Editor.Windows.GameCookerWin.RunCooked).LinkTooltip("Runs the game build from the last cooking output. Use Cook&Play or Game Cooker first.");
|
||||
_menuGameCookAndRun = cm.AddButton("Cook & Run", inputOptions.CookAndRun, Editor.Windows.GameCookerWin.BuildAndRun);
|
||||
_menuGameCookAndRun.LinkTooltip("Runs Game Cooker to build the game for this platform and runs the game after.");
|
||||
_menuGameRunCookedGame = cm.AddButton("Run cooked game", inputOptions.RunCookedGame, Editor.Windows.GameCookerWin.RunCooked);
|
||||
_menuGameRunCookedGame.LinkTooltip("Runs the game build from the last cooking output. Use 'Cook & Run' or Game Cooker first.");
|
||||
|
||||
// Tools
|
||||
MenuTools = MainMenu.AddButton("Tools");
|
||||
cm = MenuTools.ContextMenu;
|
||||
cm.VisibleChanged += OnMenuToolsShowHide;
|
||||
_menuToolsBuildScenes = cm.AddButton("Build scenes data", "Ctrl+F10", Editor.BuildScenesOrCancel);
|
||||
_menuToolsBuildScenes = cm.AddButton("Build scenes data", inputOptions.BuildScenesData, Editor.BuildScenesOrCancel);
|
||||
cm.AddSeparator();
|
||||
_menuToolsBakeLightmaps = cm.AddButton("Bake lightmaps", Editor.BakeLightmapsOrCancel);
|
||||
_menuToolsClearLightmaps = cm.AddButton("Clear lightmaps data", Editor.ClearLightmaps);
|
||||
_menuToolsBakeAllEnvProbes = cm.AddButton("Bake all env probes", BakeAllEnvProbes);
|
||||
_menuToolsBuildCSGMesh = cm.AddButton("Build CSG mesh", BuildCSG);
|
||||
_menuToolsBuildNavMesh = cm.AddButton("Build Nav Mesh", BuildNavMesh);
|
||||
_menuToolsBuildAllMesgesSDF = cm.AddButton("Build all meshes SDF", BuildAllMeshesSDF);
|
||||
_menuToolsBakeLightmaps = cm.AddButton("Bake lightmaps", inputOptions.BakeLightmaps, Editor.BakeLightmapsOrCancel);
|
||||
_menuToolsClearLightmaps = cm.AddButton("Clear lightmaps data", inputOptions.ClearLightmaps, Editor.ClearLightmaps);
|
||||
_menuToolsBakeAllEnvProbes = cm.AddButton("Bake all env probes", inputOptions.BakeEnvProbes, Editor.BakeAllEnvProbes);
|
||||
_menuToolsBuildCSGMesh = cm.AddButton("Build CSG mesh", inputOptions.BuildCSG, Editor.BuildCSG);
|
||||
_menuToolsBuildNavMesh = cm.AddButton("Build Nav Mesh", inputOptions.BuildNav, Editor.BuildNavMesh);
|
||||
_menuToolsBuildAllMeshesSDF = cm.AddButton("Build all meshes SDF", inputOptions.BuildSDF, Editor.BuildAllMeshesSDF);
|
||||
cm.AddSeparator();
|
||||
cm.AddButton("Game Cooker", Editor.Windows.GameCookerWin.FocusOrShow);
|
||||
_menuToolsCancelBuilding = cm.AddButton("Cancel building game", () => GameCooker.Cancel());
|
||||
cm.AddSeparator();
|
||||
cm.AddButton("Profiler", Editor.Windows.ProfilerWin.FocusOrShow);
|
||||
_menuToolsProfilerWindow = cm.AddButton("Profiler", inputOptions.ProfilerWindow, () => Editor.Windows.ProfilerWin.FocusOrShow());
|
||||
cm.AddSeparator();
|
||||
_menuToolsSetTheCurrentSceneViewAsDefault = cm.AddButton("Set current scene view as project default", SetTheCurrentSceneViewAsDefault);
|
||||
cm.AddButton("Take screenshot", "F12", Editor.Windows.TakeScreenshot);
|
||||
_menuToolsTakeScreenshot = cm.AddButton("Take screenshot", inputOptions.TakeScreenshot, Editor.Windows.TakeScreenshot);
|
||||
cm.AddSeparator();
|
||||
cm.AddButton("Plugins", () => Editor.Windows.PluginsWin.Show());
|
||||
cm.AddButton("Options", () => Editor.Windows.EditorOptionsWin.Show());
|
||||
@@ -606,7 +630,7 @@ namespace FlaxEditor.Modules
|
||||
cm.AddButton("Output Log", Editor.Windows.OutputLogWin.FocusOrShow);
|
||||
cm.AddButton("Graphics Quality", Editor.Windows.GraphicsQualityWin.FocusOrShow);
|
||||
cm.AddButton("Game Cooker", Editor.Windows.GameCookerWin.FocusOrShow);
|
||||
cm.AddButton("Profiler", Editor.Windows.ProfilerWin.FocusOrShow);
|
||||
cm.AddButton("Profiler", inputOptions.ProfilerWindow, Editor.Windows.ProfilerWin.FocusOrShow);
|
||||
cm.AddButton("Content Search", Editor.ContentFinding.ShowSearch);
|
||||
cm.AddButton("Visual Script Debugger", Editor.Windows.VisualScriptDebuggerWin.FocusOrShow);
|
||||
cm.AddSeparator();
|
||||
@@ -633,7 +657,12 @@ namespace FlaxEditor.Modules
|
||||
{
|
||||
var inputOptions = options.Input;
|
||||
|
||||
_menuSaveAll.ShortKeys = inputOptions.Save.ToString();
|
||||
_menuFileSaveAll.ShortKeys = inputOptions.Save.ToString();
|
||||
_menuFileSaveScenes.ShortKeys = inputOptions.SaveScenes.ToString();
|
||||
_menuFileCloseScenes.ShortKeys = inputOptions.CloseScenes.ToString();
|
||||
_menuFileOpenScriptsProject.ShortKeys = inputOptions.OpenScriptsProject.ToString();
|
||||
_menuFileGenerateScriptsProjectFiles.ShortKeys = inputOptions.GenerateScriptsProject.ToString();
|
||||
_menuFileRecompileScripts.ShortKeys = inputOptions.RecompileScripts.ToString();
|
||||
_menuEditUndo.ShortKeys = inputOptions.Undo.ToString();
|
||||
_menuEditRedo.ShortKeys = inputOptions.Redo.ToString();
|
||||
_menuEditCut.ShortKeys = inputOptions.Cut.ToString();
|
||||
@@ -642,8 +671,21 @@ namespace FlaxEditor.Modules
|
||||
_menuEditDuplicate.ShortKeys = inputOptions.Duplicate.ToString();
|
||||
_menuEditSelectAll.ShortKeys = inputOptions.SelectAll.ToString();
|
||||
_menuEditFind.ShortKeys = inputOptions.Search.ToString();
|
||||
_menuGamePlayCurrentScenes.ShortKeys = inputOptions.Play.ToString();
|
||||
_menuGamePlayGame.ShortKeys = inputOptions.Play.ToString();
|
||||
_menuGamePlayCurrentScenes.ShortKeys = inputOptions.PlayCurrentScenes.ToString();
|
||||
_menuGamePause.ShortKeys = inputOptions.Pause.ToString();
|
||||
_menuGameStop.ShortKeys = inputOptions.Play.ToString();
|
||||
_menuGameCookAndRun.ShortKeys = inputOptions.CookAndRun.ToString();
|
||||
_menuGameRunCookedGame.ShortKeys = inputOptions.RunCookedGame.ToString();
|
||||
_menuToolsBuildScenes.ShortKeys = inputOptions.BuildScenesData.ToString();
|
||||
_menuToolsBakeLightmaps.ShortKeys = inputOptions.BakeLightmaps.ToString();
|
||||
_menuToolsClearLightmaps.ShortKeys = inputOptions.ClearLightmaps.ToString();
|
||||
_menuToolsBakeAllEnvProbes.ShortKeys = inputOptions.BakeEnvProbes.ToString();
|
||||
_menuToolsBuildCSGMesh.ShortKeys = inputOptions.BuildCSG.ToString();
|
||||
_menuToolsBuildNavMesh.ShortKeys = inputOptions.BuildNav.ToString();
|
||||
_menuToolsBuildAllMeshesSDF.ShortKeys = inputOptions.BuildSDF.ToString();
|
||||
_menuToolsProfilerWindow.ShortKeys = inputOptions.ProfilerWindow.ToString();
|
||||
_menuToolsTakeScreenshot.ShortKeys = inputOptions.TakeScreenshot.ToString();
|
||||
|
||||
MainMenuShortcutKeysUpdated?.Invoke();
|
||||
}
|
||||
@@ -668,10 +710,10 @@ namespace FlaxEditor.Modules
|
||||
ToolStrip.AddSeparator();
|
||||
|
||||
// Cook scenes
|
||||
_toolStripBuildScenes = (ToolStripButton)ToolStrip.AddButton(Editor.Icons.Build64, Editor.BuildScenesOrCancel).LinkTooltip("Build scenes data - CSG, navmesh, static lighting, env probes - configurable via Build Actions in editor options (Ctrl+F10)");
|
||||
_toolStripBuildScenes = (ToolStripButton)ToolStrip.AddButton(Editor.Icons.Build64, Editor.BuildScenesOrCancel).LinkTooltip($"Build scenes data - CSG, navmesh, static lighting, env probes - configurable via Build Actions in editor options ({inputOptions.BuildScenesData})");
|
||||
|
||||
// Cook and run
|
||||
_toolStripCook = (ToolStripButton)ToolStrip.AddButton(Editor.Icons.ShipIt64, Editor.Windows.GameCookerWin.BuildAndRun).LinkTooltip("Cook & Run - build game for the current platform and run it locally");
|
||||
_toolStripCook = (ToolStripButton)ToolStrip.AddButton(Editor.Icons.ShipIt64, Editor.Windows.GameCookerWin.BuildAndRun).LinkTooltip($"Cook & Run - build game for the current platform and run it locally ({inputOptions.Play})");
|
||||
_toolStripCook.ContextMenu = new ContextMenu();
|
||||
_toolStripCook.ContextMenu.AddButton("Run cooked game", Editor.Windows.GameCookerWin.RunCooked);
|
||||
_toolStripCook.ContextMenu.AddSeparator();
|
||||
@@ -692,7 +734,7 @@ namespace FlaxEditor.Modules
|
||||
playActionGroup.SelectedChanged = SetPlayAction;
|
||||
Editor.Options.OptionsChanged += options => { playActionGroup.Selected = options.Interface.PlayButtonAction; };
|
||||
|
||||
_toolStripPause = (ToolStripButton)ToolStrip.AddButton(Editor.Icons.Pause64, Editor.Simulation.RequestResumeOrPause).LinkTooltip($"Pause/Resume game({inputOptions.Pause})");
|
||||
_toolStripPause = (ToolStripButton)ToolStrip.AddButton(Editor.Icons.Pause64, Editor.Simulation.RequestResumeOrPause).LinkTooltip($"Pause/Resume game ({inputOptions.Pause})");
|
||||
_toolStripStep = (ToolStripButton)ToolStrip.AddButton(Editor.Icons.Skip64, Editor.Simulation.RequestPlayOneFrame).LinkTooltip("Step one frame in game");
|
||||
|
||||
UpdateToolstrip();
|
||||
@@ -752,7 +794,7 @@ namespace FlaxEditor.Modules
|
||||
HorizontalAlignment = TextAlignment.Far,
|
||||
AnchorPreset = AnchorPresets.HorizontalStretchMiddle,
|
||||
Parent = progressPanel,
|
||||
Offsets = new Margin(progressBarRightMargin, progressBarWidth + progressBarLeftMargin + progressBarRightMargin, 0, 0),
|
||||
Offsets = new Margin(progressBarRightMargin, progressBarWidth + progressBarLeftMargin + progressBarRightMargin, 0, 0)
|
||||
};
|
||||
|
||||
UpdateStatusBar();
|
||||
@@ -872,7 +914,7 @@ namespace FlaxEditor.Modules
|
||||
_menuToolsBakeLightmaps.Text = isBakingLightmaps ? "Cancel baking lightmaps" : "Bake lightmaps";
|
||||
_menuToolsClearLightmaps.Enabled = canEdit;
|
||||
_menuToolsBakeAllEnvProbes.Enabled = canEdit;
|
||||
_menuToolsBuildAllMesgesSDF.Enabled = canEdit && !isBakingLightmaps;
|
||||
_menuToolsBuildAllMeshesSDF.Enabled = canEdit && !isBakingLightmaps;
|
||||
_menuToolsBuildCSGMesh.Enabled = canEdit;
|
||||
_menuToolsBuildNavMesh.Enabled = canEdit;
|
||||
_menuToolsCancelBuilding.Enabled = GameCooker.IsRunning;
|
||||
@@ -911,7 +953,7 @@ namespace FlaxEditor.Modules
|
||||
Editor.Windows.LoadLayout((string)button.Tag);
|
||||
}
|
||||
|
||||
private void AlignViewportWithActor()
|
||||
internal void AlignViewportWithActor()
|
||||
{
|
||||
var selection = Editor.SceneEditing;
|
||||
if (selection.HasSthSelected && selection.Selection[0] is ActorNode node)
|
||||
@@ -922,7 +964,7 @@ namespace FlaxEditor.Modules
|
||||
}
|
||||
}
|
||||
|
||||
private void MoveActorToViewport()
|
||||
internal void MoveActorToViewport()
|
||||
{
|
||||
var selection = Editor.SceneEditing;
|
||||
if (selection.HasSthSelected && selection.Selection[0] is ActorNode node)
|
||||
@@ -936,7 +978,7 @@ namespace FlaxEditor.Modules
|
||||
}
|
||||
}
|
||||
|
||||
private void AlignActorWithViewport()
|
||||
internal void AlignActorWithViewport()
|
||||
{
|
||||
var selection = Editor.SceneEditing;
|
||||
if (selection.HasSthSelected && selection.Selection[0] is ActorNode node)
|
||||
@@ -972,57 +1014,6 @@ namespace FlaxEditor.Modules
|
||||
new Tools.Terrain.CreateTerrainDialog().Show(Editor.Windows.MainWindow);
|
||||
}
|
||||
|
||||
private void BakeAllEnvProbes()
|
||||
{
|
||||
Editor.Scene.ExecuteOnGraph(node =>
|
||||
{
|
||||
if (node is EnvironmentProbeNode envProbeNode && envProbeNode.IsActive)
|
||||
{
|
||||
((EnvironmentProbe)envProbeNode.Actor).Bake();
|
||||
node.ParentScene.IsEdited = true;
|
||||
}
|
||||
else if (node is SkyLightNode skyLightNode && skyLightNode.IsActive && skyLightNode.Actor is SkyLight skyLight && skyLight.Mode == SkyLight.Modes.CaptureScene)
|
||||
{
|
||||
skyLight.Bake();
|
||||
node.ParentScene.IsEdited = true;
|
||||
}
|
||||
|
||||
return node.IsActive;
|
||||
});
|
||||
}
|
||||
|
||||
private void BuildCSG()
|
||||
{
|
||||
var scenes = Level.Scenes;
|
||||
scenes.ToList().ForEach(x => x.BuildCSG(0));
|
||||
Editor.Scene.MarkSceneEdited(scenes);
|
||||
}
|
||||
|
||||
private void BuildNavMesh()
|
||||
{
|
||||
var scenes = Level.Scenes;
|
||||
scenes.ToList().ForEach(x => Navigation.BuildNavMesh(x, 0));
|
||||
Editor.Scene.MarkSceneEdited(scenes);
|
||||
}
|
||||
|
||||
private void BuildAllMeshesSDF()
|
||||
{
|
||||
// TODO: async maybe with progress reporting?
|
||||
Editor.Scene.ExecuteOnGraph(node =>
|
||||
{
|
||||
if (node is StaticModelNode staticModelNode && staticModelNode.Actor is StaticModel staticModel)
|
||||
{
|
||||
if (staticModel.DrawModes.HasFlag(DrawPass.GlobalSDF) && staticModel.Model != null && !staticModel.Model.IsVirtual && staticModel.Model.SDF.Texture == null)
|
||||
{
|
||||
Editor.Log("Generating SDF for " + staticModel.Model);
|
||||
if (!staticModel.Model.GenerateSDF())
|
||||
staticModel.Model.Save();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
private void SetTheCurrentSceneViewAsDefault()
|
||||
{
|
||||
var projectInfo = Editor.GameProject;
|
||||
|
||||
@@ -721,7 +721,7 @@ namespace FlaxEditor.Modules
|
||||
// Create main window
|
||||
var settings = CreateWindowSettings.Default;
|
||||
settings.Title = "Flax Editor";
|
||||
settings.Size = Platform.DesktopSize;
|
||||
settings.Size = Platform.DesktopSize * 0.75f;
|
||||
settings.StartPosition = WindowStartPosition.CenterScreen;
|
||||
settings.ShowAfterFirstPaint = true;
|
||||
|
||||
|
||||
@@ -106,7 +106,7 @@ namespace FlaxEditor.Options
|
||||
[DefaultValue(60.0f), Limit(0, 666)]
|
||||
[EditorDisplay("General", "Editor FPS"), EditorOrder(110), Tooltip("Limit for the editor draw/update frames per second rate (FPS). Use higher values if you need more responsive interface or lower values to use less device power. Value 0 disables any limits.")]
|
||||
public float EditorFPS { get; set; } = 60.0f;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets The FPS of the editor when the editor window is not focused. Usually set to lower then the editor FPS.
|
||||
/// </summary>
|
||||
@@ -203,7 +203,7 @@ namespace FlaxEditor.Options
|
||||
[DefaultValue(5), Limit(1)]
|
||||
[EditorDisplay("Auto Save", "Auto Save Frequency"), EditorOrder(801), Tooltip("The interval between auto saves (in minutes)")]
|
||||
public int AutoSaveFrequency { get; set; } = 5;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating the time before the auto save that the popup shows (in seconds).
|
||||
/// </summary>
|
||||
|
||||
@@ -134,6 +134,66 @@ namespace FlaxEditor.Options
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool ProcessModifiers(Control control)
|
||||
{
|
||||
return ProcessModifiers(control.Root.GetKey);
|
||||
}
|
||||
|
||||
private bool ProcessModifiers(Window window)
|
||||
{
|
||||
return ProcessModifiers(window.GetKey);
|
||||
}
|
||||
|
||||
private bool ProcessModifiers(Func<KeyboardKeys, bool> getKeyFunc)
|
||||
{
|
||||
bool ctrlPressed = getKeyFunc(KeyboardKeys.Control);
|
||||
bool shiftPressed = getKeyFunc(KeyboardKeys.Shift);
|
||||
bool altPressed = getKeyFunc(KeyboardKeys.Alt);
|
||||
|
||||
bool mod1 = false;
|
||||
if (Modifier1 == KeyboardKeys.None)
|
||||
mod1 = true;
|
||||
else if (Modifier1 == KeyboardKeys.Control)
|
||||
{
|
||||
mod1 = ctrlPressed;
|
||||
ctrlPressed = false;
|
||||
}
|
||||
else if (Modifier1 == KeyboardKeys.Shift)
|
||||
{
|
||||
mod1 = shiftPressed;
|
||||
shiftPressed = false;
|
||||
}
|
||||
else if (Modifier1 == KeyboardKeys.Alt)
|
||||
{
|
||||
mod1 = altPressed;
|
||||
altPressed = false;
|
||||
}
|
||||
|
||||
bool mod2 = false;
|
||||
if (Modifier2 == KeyboardKeys.None)
|
||||
mod2 = true;
|
||||
else if (Modifier2 == KeyboardKeys.Control)
|
||||
{
|
||||
mod2 = ctrlPressed;
|
||||
ctrlPressed = false;
|
||||
}
|
||||
else if (Modifier2 == KeyboardKeys.Shift)
|
||||
{
|
||||
mod2 = shiftPressed;
|
||||
shiftPressed = false;
|
||||
}
|
||||
else if (Modifier2 == KeyboardKeys.Alt)
|
||||
{
|
||||
mod2 = altPressed;
|
||||
altPressed = false;
|
||||
}
|
||||
|
||||
// Check if any unhandled modifiers are not pressed
|
||||
if (mod1 && mod2)
|
||||
return !ctrlPressed && !shiftPressed && !altPressed;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Processes this input binding to check if state matches.
|
||||
/// </summary>
|
||||
@@ -142,19 +202,7 @@ namespace FlaxEditor.Options
|
||||
public bool Process(Control control)
|
||||
{
|
||||
var root = control.Root;
|
||||
|
||||
if (root.GetKey(Key))
|
||||
{
|
||||
if (Modifier1 == KeyboardKeys.None || root.GetKey(Modifier1))
|
||||
{
|
||||
if (Modifier2 == KeyboardKeys.None || root.GetKey(Modifier2))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
return root.GetKey(Key) && ProcessModifiers(control);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -165,20 +213,20 @@ namespace FlaxEditor.Options
|
||||
/// <returns>True if input has been processed, otherwise false.</returns>
|
||||
public bool Process(Control control, KeyboardKeys key)
|
||||
{
|
||||
var root = control.Root;
|
||||
if (key != Key)
|
||||
return false;
|
||||
|
||||
if (key == Key)
|
||||
{
|
||||
if (Modifier1 == KeyboardKeys.None || root.GetKey(Modifier1))
|
||||
{
|
||||
if (Modifier2 == KeyboardKeys.None || root.GetKey(Modifier2))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return ProcessModifiers(control);
|
||||
}
|
||||
|
||||
return false;
|
||||
/// <summary>
|
||||
/// Processes this input binding to check if state matches.
|
||||
/// </summary>
|
||||
/// <param name="window">The input providing window.</param>
|
||||
/// <returns>True if input has been processed, otherwise false.</returns>
|
||||
public bool Process(Window window)
|
||||
{
|
||||
return window.GetKey(Key) && ProcessModifiers(window);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -78,6 +78,30 @@ namespace FlaxEditor.Options
|
||||
|
||||
#endregion
|
||||
|
||||
#region File
|
||||
|
||||
[DefaultValue(typeof(InputBinding), "None")]
|
||||
[EditorDisplay("File"), EditorOrder(300)]
|
||||
public InputBinding SaveScenes = new InputBinding(KeyboardKeys.None);
|
||||
|
||||
[DefaultValue(typeof(InputBinding), "None")]
|
||||
[EditorDisplay("File"), EditorOrder(310)]
|
||||
public InputBinding CloseScenes = new InputBinding(KeyboardKeys.None);
|
||||
|
||||
[DefaultValue(typeof(InputBinding), "None")]
|
||||
[EditorDisplay("File"), EditorOrder(320)]
|
||||
public InputBinding OpenScriptsProject = new InputBinding(KeyboardKeys.None);
|
||||
|
||||
[DefaultValue(typeof(InputBinding), "None")]
|
||||
[EditorDisplay("File"), EditorOrder(330)]
|
||||
public InputBinding GenerateScriptsProject = new InputBinding(KeyboardKeys.None);
|
||||
|
||||
[DefaultValue(typeof(InputBinding), "None")]
|
||||
[EditorDisplay("File"), EditorOrder(340)]
|
||||
public InputBinding RecompileScripts = new InputBinding(KeyboardKeys.None);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Scene
|
||||
|
||||
[DefaultValue(typeof(InputBinding), "End")]
|
||||
@@ -85,35 +109,115 @@ namespace FlaxEditor.Options
|
||||
public InputBinding SnapToGround = new InputBinding(KeyboardKeys.End);
|
||||
|
||||
[DefaultValue(typeof(InputBinding), "F5")]
|
||||
[EditorDisplay("Scene"), EditorOrder(510)]
|
||||
[EditorDisplay("Scene", "Play/Stop"), EditorOrder(510)]
|
||||
public InputBinding Play = new InputBinding(KeyboardKeys.F5);
|
||||
|
||||
[DefaultValue(typeof(InputBinding), "None")]
|
||||
[EditorDisplay("Scene", "Play Current Scenes/Stop"), EditorOrder(520)]
|
||||
public InputBinding PlayCurrentScenes = new InputBinding(KeyboardKeys.None);
|
||||
|
||||
[DefaultValue(typeof(InputBinding), "F6")]
|
||||
[EditorDisplay("Scene"), EditorOrder(520)]
|
||||
[EditorDisplay("Scene"), EditorOrder(530)]
|
||||
public InputBinding Pause = new InputBinding(KeyboardKeys.F6);
|
||||
|
||||
[DefaultValue(typeof(InputBinding), "F11")]
|
||||
[EditorDisplay("Scene"), EditorOrder(530)]
|
||||
[EditorDisplay("Scene"), EditorOrder(540)]
|
||||
public InputBinding StepFrame = new InputBinding(KeyboardKeys.F11);
|
||||
|
||||
[DefaultValue(typeof(InputBinding), "None")]
|
||||
[EditorDisplay("Scene", "Cook & Run"), EditorOrder(550)]
|
||||
public InputBinding CookAndRun = new InputBinding(KeyboardKeys.None);
|
||||
|
||||
[DefaultValue(typeof(InputBinding), "None")]
|
||||
[EditorDisplay("Scene", "Run cooked game"), EditorOrder(560)]
|
||||
public InputBinding RunCookedGame = new InputBinding(KeyboardKeys.None);
|
||||
|
||||
[DefaultValue(typeof(InputBinding), "None")]
|
||||
[EditorDisplay("Scene", "Move actor to viewport"), EditorOrder(570)]
|
||||
public InputBinding MoveActorToViewport = new InputBinding(KeyboardKeys.None);
|
||||
|
||||
[DefaultValue(typeof(InputBinding), "None")]
|
||||
[EditorDisplay("Scene", "Align actor with viewport"), EditorOrder(571)]
|
||||
public InputBinding AlignActorWithViewport = new InputBinding(KeyboardKeys.None);
|
||||
|
||||
[DefaultValue(typeof(InputBinding), "None")]
|
||||
[EditorDisplay("Scene", "Align viewport with actor"), EditorOrder(572)]
|
||||
public InputBinding AlignViewportWithActor = new InputBinding(KeyboardKeys.None);
|
||||
|
||||
[DefaultValue(typeof(InputBinding), "None")]
|
||||
[EditorDisplay("Scene"), EditorOrder(573)]
|
||||
public InputBinding PilotActor = new InputBinding(KeyboardKeys.None);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Tools
|
||||
|
||||
[DefaultValue(typeof(InputBinding), "Ctrl+F10")]
|
||||
[EditorDisplay("Tools", "Build scenes data"), EditorOrder(600)]
|
||||
public InputBinding BuildScenesData = new InputBinding(KeyboardKeys.F10, KeyboardKeys.Control);
|
||||
|
||||
[DefaultValue(typeof(InputBinding), "None")]
|
||||
[EditorDisplay("Tools", "Bake lightmaps"), EditorOrder(601)]
|
||||
public InputBinding BakeLightmaps = new InputBinding(KeyboardKeys.None);
|
||||
|
||||
[DefaultValue(typeof(InputBinding), "None")]
|
||||
[EditorDisplay("Tools", "Clear lightmaps data"), EditorOrder(602)]
|
||||
public InputBinding ClearLightmaps = new InputBinding(KeyboardKeys.None);
|
||||
|
||||
[DefaultValue(typeof(InputBinding), "None")]
|
||||
[EditorDisplay("Tools", "Bake all env probes"), EditorOrder(603)]
|
||||
public InputBinding BakeEnvProbes = new InputBinding(KeyboardKeys.None);
|
||||
|
||||
[DefaultValue(typeof(InputBinding), "None")]
|
||||
[EditorDisplay("Tools", "Build CSG mesh"), EditorOrder(604)]
|
||||
public InputBinding BuildCSG = new InputBinding(KeyboardKeys.None);
|
||||
|
||||
[DefaultValue(typeof(InputBinding), "None")]
|
||||
[EditorDisplay("Tools", "Build Nav Mesh"), EditorOrder(605)]
|
||||
public InputBinding BuildNav = new InputBinding(KeyboardKeys.None);
|
||||
|
||||
[DefaultValue(typeof(InputBinding), "None")]
|
||||
[EditorDisplay("Tools", "Build all meshes SDF"), EditorOrder(606)]
|
||||
public InputBinding BuildSDF = new InputBinding(KeyboardKeys.None);
|
||||
|
||||
[DefaultValue(typeof(InputBinding), "F12")]
|
||||
[EditorDisplay("Tools", "Take screenshot"), EditorOrder(607)]
|
||||
public InputBinding TakeScreenshot = new InputBinding(KeyboardKeys.F12);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Profiler
|
||||
|
||||
[DefaultValue(typeof(InputBinding), "None")]
|
||||
[EditorDisplay("Profiler", "Open Profiler Window"), EditorOrder(630)]
|
||||
public InputBinding ProfilerWindow = new InputBinding(KeyboardKeys.None);
|
||||
|
||||
[DefaultValue(typeof(InputBinding), "None")]
|
||||
[EditorDisplay("Profiler", "Start/Stop Profiler"), EditorOrder(631)]
|
||||
public InputBinding ProfilerStartStop = new InputBinding(KeyboardKeys.None);
|
||||
|
||||
[DefaultValue(typeof(InputBinding), "None")]
|
||||
[EditorDisplay("Profiler", "Clear Profiler data"), EditorOrder(632)]
|
||||
public InputBinding ProfilerClear = new InputBinding(KeyboardKeys.None);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Debugger
|
||||
|
||||
[DefaultValue(typeof(InputBinding), "F5")]
|
||||
[EditorDisplay("Debugger", "Continue"), EditorOrder(610)]
|
||||
[EditorDisplay("Debugger", "Continue"), EditorOrder(810)]
|
||||
public InputBinding DebuggerContinue = new InputBinding(KeyboardKeys.F5);
|
||||
|
||||
[DefaultValue(typeof(InputBinding), "F10")]
|
||||
[EditorDisplay("Debugger", "Step Over"), EditorOrder(620)]
|
||||
[EditorDisplay("Debugger", "Step Over"), EditorOrder(820)]
|
||||
public InputBinding DebuggerStepOver = new InputBinding(KeyboardKeys.F10);
|
||||
|
||||
[DefaultValue(typeof(InputBinding), "F11")]
|
||||
[EditorDisplay("Debugger", "Step Into"), EditorOrder(630)]
|
||||
[EditorDisplay("Debugger", "Step Into"), EditorOrder(830)]
|
||||
public InputBinding DebuggerStepInto = new InputBinding(KeyboardKeys.F11);
|
||||
|
||||
[DefaultValue(typeof(InputBinding), "Shift+F11")]
|
||||
[EditorDisplay("Debugger", "Step Out"), EditorOrder(640)]
|
||||
[EditorDisplay("Debugger", "Step Out"), EditorOrder(840)]
|
||||
public InputBinding DebuggerStepOut = new InputBinding(KeyboardKeys.F11, KeyboardKeys.Shift);
|
||||
|
||||
#endregion
|
||||
@@ -132,6 +236,10 @@ namespace FlaxEditor.Options
|
||||
[EditorDisplay("Gizmo"), EditorOrder(1020)]
|
||||
public InputBinding ScaleMode = new InputBinding(KeyboardKeys.Alpha3);
|
||||
|
||||
[DefaultValue(typeof(InputBinding), "Alpha4")]
|
||||
[EditorDisplay("Gizmo"), EditorOrder(1030)]
|
||||
public InputBinding ToggleTransformSpace = new InputBinding(KeyboardKeys.Alpha4);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Viewport
|
||||
@@ -160,28 +268,40 @@ namespace FlaxEditor.Options
|
||||
[EditorDisplay("Viewport"), EditorOrder(1550)]
|
||||
public InputBinding Down = new InputBinding(KeyboardKeys.Q);
|
||||
|
||||
[DefaultValue(typeof(InputBinding), "None")]
|
||||
[EditorDisplay("Viewport", "Toggle Camera Rotation"), EditorOrder(1560)]
|
||||
public InputBinding CameraToggleRotation = new InputBinding(KeyboardKeys.None);
|
||||
|
||||
[DefaultValue(typeof(InputBinding), "None")]
|
||||
[EditorDisplay("Viewport", "Increase Camera Move Speed"), EditorOrder(1570)]
|
||||
public InputBinding CameraIncreaseMoveSpeed = new InputBinding(KeyboardKeys.None);
|
||||
|
||||
[DefaultValue(typeof(InputBinding), "None")]
|
||||
[EditorDisplay("Viewport", "Decrease Camera Move Speed"), EditorOrder(1571)]
|
||||
public InputBinding CameraDecreaseMoveSpeed = new InputBinding(KeyboardKeys.None);
|
||||
|
||||
[DefaultValue(typeof(InputBinding), "Numpad0")]
|
||||
[EditorDisplay("Viewport"), EditorOrder(1600)]
|
||||
[EditorDisplay("Viewport"), EditorOrder(1700)]
|
||||
public InputBinding ViewpointFront = new InputBinding(KeyboardKeys.Numpad0);
|
||||
|
||||
[DefaultValue(typeof(InputBinding), "Numpad5")]
|
||||
[EditorDisplay("Viewport"), EditorOrder(1610)]
|
||||
[EditorDisplay("Viewport"), EditorOrder(1710)]
|
||||
public InputBinding ViewpointBack = new InputBinding(KeyboardKeys.Numpad5);
|
||||
|
||||
[DefaultValue(typeof(InputBinding), "Numpad4")]
|
||||
[EditorDisplay("Viewport"), EditorOrder(1620)]
|
||||
[EditorDisplay("Viewport"), EditorOrder(1720)]
|
||||
public InputBinding ViewpointLeft = new InputBinding(KeyboardKeys.Numpad4);
|
||||
|
||||
[DefaultValue(typeof(InputBinding), "Numpad6")]
|
||||
[EditorDisplay("Viewport"), EditorOrder(1630)]
|
||||
[EditorDisplay("Viewport"), EditorOrder(1730)]
|
||||
public InputBinding ViewpointRight = new InputBinding(KeyboardKeys.Numpad6);
|
||||
|
||||
[DefaultValue(typeof(InputBinding), "Numpad8")]
|
||||
[EditorDisplay("Viewport"), EditorOrder(1640)]
|
||||
[EditorDisplay("Viewport"), EditorOrder(1740)]
|
||||
public InputBinding ViewpointTop = new InputBinding(KeyboardKeys.Numpad8);
|
||||
|
||||
[DefaultValue(typeof(InputBinding), "Numpad2")]
|
||||
[EditorDisplay("Viewport"), EditorOrder(1650)]
|
||||
[EditorDisplay("Viewport"), EditorOrder(1750)]
|
||||
public InputBinding ViewpointBottom = new InputBinding(KeyboardKeys.Numpad2);
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -166,7 +166,19 @@ namespace FlaxEditor.Options
|
||||
/// Gets or sets the output log text font.
|
||||
/// </summary>
|
||||
[EditorDisplay("Output Log", "Text Font"), EditorOrder(320), Tooltip("The output log text font.")]
|
||||
public FontReference OutputLogTextFont { get; set; } = new FontReference(FlaxEngine.Content.LoadAsyncInternal<FontAsset>(EditorAssets.InconsolataRegularFont), 10);
|
||||
public FontReference OutputLogTextFont
|
||||
{
|
||||
get => _outputLogFont;
|
||||
set
|
||||
{
|
||||
if (value == null)
|
||||
_outputLogFont = new FontReference(FlaxEngine.Content.LoadAsyncInternal<FontAsset>(EditorAssets.InconsolataRegularFont), 10);
|
||||
else if (!value.Font)
|
||||
_outputLogFont.Font = FlaxEngine.Content.LoadAsyncInternal<FontAsset>(EditorAssets.InconsolataRegularFont);
|
||||
else
|
||||
_outputLogFont = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the output log text color.
|
||||
@@ -225,29 +237,82 @@ namespace FlaxEditor.Options
|
||||
public int NumberOfGameClientsToLaunch = 1;
|
||||
|
||||
private static FontAsset DefaultFont => FlaxEngine.Content.LoadAsyncInternal<FontAsset>(EditorAssets.PrimaryFont);
|
||||
private FontReference _titleFont = new FontReference(DefaultFont, 18);
|
||||
private FontReference _largeFont = new FontReference(DefaultFont, 14);
|
||||
private FontReference _mediumFont = new FontReference(DefaultFont, 9);
|
||||
private FontReference _smallFont = new FontReference(DefaultFont, 9);
|
||||
private FontReference _outputLogFont = new FontReference(FlaxEngine.Content.LoadAsyncInternal<FontAsset>(EditorAssets.InconsolataRegularFont), 10);
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the title font for editor UI.
|
||||
/// </summary>
|
||||
[EditorDisplay("Fonts"), EditorOrder(600), Tooltip("The title font for editor UI.")]
|
||||
public FontReference TitleFont { get; set; } = new FontReference(DefaultFont, 18);
|
||||
public FontReference TitleFont
|
||||
{
|
||||
get => _titleFont;
|
||||
set
|
||||
{
|
||||
if (value == null)
|
||||
_titleFont = new FontReference(DefaultFont, 18);
|
||||
else if (!value.Font)
|
||||
_titleFont.Font = DefaultFont;
|
||||
else
|
||||
_titleFont = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the large font for editor UI.
|
||||
/// </summary>
|
||||
[EditorDisplay("Fonts"), EditorOrder(610), Tooltip("The large font for editor UI.")]
|
||||
public FontReference LargeFont { get; set; } = new FontReference(DefaultFont, 14);
|
||||
public FontReference LargeFont
|
||||
{
|
||||
get => _largeFont;
|
||||
set
|
||||
{
|
||||
if (value == null)
|
||||
_largeFont = new FontReference(DefaultFont, 14);
|
||||
else if (!value.Font)
|
||||
_largeFont.Font = DefaultFont;
|
||||
else
|
||||
_largeFont = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the medium font for editor UI.
|
||||
/// </summary>
|
||||
[EditorDisplay("Fonts"), EditorOrder(620), Tooltip("The medium font for editor UI.")]
|
||||
public FontReference MediumFont { get; set; } = new FontReference(DefaultFont, 9);
|
||||
public FontReference MediumFont
|
||||
{
|
||||
get => _mediumFont;
|
||||
set
|
||||
{
|
||||
if (value == null)
|
||||
_mediumFont = new FontReference(DefaultFont, 9);
|
||||
else if (!value.Font)
|
||||
_mediumFont.Font = DefaultFont;
|
||||
else
|
||||
_mediumFont = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the small font for editor UI.
|
||||
/// </summary>
|
||||
[EditorDisplay("Fonts"), EditorOrder(630), Tooltip("The small font for editor UI.")]
|
||||
public FontReference SmallFont { get; set; } = new FontReference(DefaultFont, 9);
|
||||
public FontReference SmallFont
|
||||
{
|
||||
get => _smallFont;
|
||||
set
|
||||
{
|
||||
if (value == null)
|
||||
_smallFont = new FontReference(DefaultFont, 9);
|
||||
else if (!value.Font)
|
||||
_smallFont.Font = DefaultFont;
|
||||
else
|
||||
_smallFont = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -244,6 +244,13 @@ namespace FlaxEditor.Options
|
||||
CollectionBackgroundColor = Color.FromBgra(0x14CCCCCC),
|
||||
ProgressNormal = Color.FromBgra(0xFF0ad328),
|
||||
|
||||
Statusbar = new Style.StatusbarStyle()
|
||||
{
|
||||
PlayMode = Color.FromBgra(0xFF2F9135),
|
||||
Failed = Color.FromBgra(0xFF9C2424),
|
||||
Loading = Color.FromBgra(0xFF2D2D30)
|
||||
},
|
||||
|
||||
// Fonts
|
||||
FontTitle = options.Interface.TitleFont.GetFont(),
|
||||
FontLarge = options.Interface.LargeFont.GetFont(),
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
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)
|
||||
|
||||
@@ -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
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
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;
|
||||
};
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool RayCastSelf(ref RayCastData ray, out Real distance, out Vector3 normal)
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
|
||||
/// <inheritdoc />
|
||||
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()
|
||||
|
||||
@@ -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;
|
||||
@@ -21,7 +22,7 @@ namespace FlaxEditor.SceneGraph.Actors
|
||||
[HideInEditor]
|
||||
public sealed class SplineNode : ActorNode
|
||||
{
|
||||
private sealed class SplinePointNode : ActorChildNode<SplineNode>
|
||||
internal sealed class SplinePointNode : ActorChildNode<SplineNode>
|
||||
{
|
||||
public unsafe SplinePointNode(SplineNode node, Guid id, int index)
|
||||
: base(node, id, index)
|
||||
@@ -167,10 +168,11 @@ namespace FlaxEditor.SceneGraph.Actors
|
||||
|
||||
public override bool RayCastSelf(ref RayCastData ray, out Real distance, out Vector3 normal)
|
||||
{
|
||||
normal = -ray.Ray.Direction;
|
||||
var actor = (Spline)_node.Actor;
|
||||
var pos = actor.GetSplinePoint(Index);
|
||||
normal = -ray.Ray.Direction;
|
||||
return new BoundingSphere(pos, 7.0f).Intersects(ref ray.Ray, out distance);
|
||||
var nodeSize = NodeSizeByDistance(Transform.Translation, PointNodeSize);
|
||||
return new BoundingSphere(pos, nodeSize).Intersects(ref ray.Ray, out distance);
|
||||
}
|
||||
|
||||
public override void OnDebugDraw(ViewportDebugDrawData data)
|
||||
@@ -179,29 +181,32 @@ namespace FlaxEditor.SceneGraph.Actors
|
||||
var pos = actor.GetSplinePoint(Index);
|
||||
var tangentIn = actor.GetSplineTangent(Index, true).Translation;
|
||||
var tangentOut = actor.GetSplineTangent(Index, false).Translation;
|
||||
var pointSize = NodeSizeByDistance(pos, PointNodeSize);
|
||||
var tangentInSize = NodeSizeByDistance(tangentIn, TangentNodeSize);
|
||||
var tangentOutSize = NodeSizeByDistance(tangentOut, TangentNodeSize);
|
||||
|
||||
// Draw spline path
|
||||
ParentNode.OnDebugDraw(data);
|
||||
|
||||
// Draw selected point highlight
|
||||
DebugDraw.DrawSphere(new BoundingSphere(pos, 5.0f), Color.Yellow, 0, false);
|
||||
DebugDraw.DrawSphere(new BoundingSphere(pos, pointSize), Color.Yellow, 0, false);
|
||||
|
||||
// Draw tangent points
|
||||
if (tangentIn != pos)
|
||||
{
|
||||
DebugDraw.DrawLine(pos, tangentIn, Color.Blue.AlphaMultiplied(0.6f), 0, false);
|
||||
DebugDraw.DrawWireSphere(new BoundingSphere(tangentIn, 4.0f), Color.Blue, 0, false);
|
||||
DebugDraw.DrawWireSphere(new BoundingSphere(tangentIn, tangentInSize), Color.Blue, 0, false);
|
||||
}
|
||||
if (tangentOut != pos)
|
||||
{
|
||||
DebugDraw.DrawLine(pos, tangentOut, Color.Red.AlphaMultiplied(0.6f), 0, false);
|
||||
DebugDraw.DrawWireSphere(new BoundingSphere(tangentOut, 4.0f), Color.Red, 0, false);
|
||||
DebugDraw.DrawWireSphere(new BoundingSphere(tangentOut, tangentOutSize), Color.Red, 0, false);
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
@@ -219,7 +224,7 @@ namespace FlaxEditor.SceneGraph.Actors
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class SplinePointTangentNode : ActorChildNode
|
||||
internal sealed class SplinePointTangentNode : ActorChildNode
|
||||
{
|
||||
private SplineNode _node;
|
||||
private int _index;
|
||||
@@ -249,10 +254,11 @@ namespace FlaxEditor.SceneGraph.Actors
|
||||
|
||||
public override bool RayCastSelf(ref RayCastData ray, out Real distance, out Vector3 normal)
|
||||
{
|
||||
normal = -ray.Ray.Direction;
|
||||
var actor = (Spline)_node.Actor;
|
||||
var pos = actor.GetSplineTangent(_index, _isIn).Translation;
|
||||
normal = -ray.Ray.Direction;
|
||||
return new BoundingSphere(pos, 7.0f).Intersects(ref ray.Ray, out distance);
|
||||
var tangentSize = NodeSizeByDistance(Transform.Translation, TangentNodeSize);
|
||||
return new BoundingSphere(pos, tangentSize).Intersects(ref ray.Ray, out distance);
|
||||
}
|
||||
|
||||
public override void OnDebugDraw(ViewportDebugDrawData data)
|
||||
@@ -263,12 +269,13 @@ namespace FlaxEditor.SceneGraph.Actors
|
||||
// Draw selected tangent highlight
|
||||
var actor = (Spline)_node.Actor;
|
||||
var pos = actor.GetSplineTangent(_index, _isIn).Translation;
|
||||
DebugDraw.DrawSphere(new BoundingSphere(pos, 5.0f), Color.YellowGreen, 0, false);
|
||||
var tangentSize = NodeSizeByDistance(Transform.Translation, TangentNodeSize);
|
||||
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()
|
||||
@@ -279,6 +286,9 @@ namespace FlaxEditor.SceneGraph.Actors
|
||||
}
|
||||
}
|
||||
|
||||
private const Real PointNodeSize = 1.5f;
|
||||
private const Real TangentNodeSize = 1.0f;
|
||||
|
||||
/// <inheritdoc />
|
||||
public SplineNode(Actor actor)
|
||||
: base(actor)
|
||||
@@ -345,9 +355,9 @@ namespace FlaxEditor.SceneGraph.Actors
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
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);
|
||||
@@ -398,6 +408,13 @@ namespace FlaxEditor.SceneGraph.Actors
|
||||
}
|
||||
}
|
||||
|
||||
internal static Real NodeSizeByDistance(Vector3 nodePosition, Real nodeSize)
|
||||
{
|
||||
var cameraTransform = Editor.Instance.Windows.EditWin.Viewport.ViewportCamera.Viewport.ViewTransform;
|
||||
var distance = Vector3.Distance(cameraTransform.Translation, nodePosition) / 100;
|
||||
return distance * nodeSize;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool RayCastSelf(ref RayCastData ray, out Real distance, out Vector3 normal)
|
||||
{
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
/// <summary>
|
||||
/// Called when scene tree window wants to show the context menu. Allows to add custom options.
|
||||
/// </summary>
|
||||
public virtual void OnContextMenu(FlaxEditor.GUI.ContextMenu.ContextMenu contextMenu)
|
||||
public virtual void OnContextMenu(FlaxEditor.GUI.ContextMenu.ContextMenu contextMenu, EditorWindow window)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,8 @@ namespace
|
||||
String version;
|
||||
|
||||
RiderInstallation(const String& path_, const String& version_)
|
||||
: path(path_), version(version_)
|
||||
: path(path_)
|
||||
, version(version_)
|
||||
{
|
||||
}
|
||||
};
|
||||
@@ -44,6 +45,10 @@ namespace
|
||||
if (document.HasParseError())
|
||||
return;
|
||||
|
||||
// Check if this is actually rider and not another jetbrains product
|
||||
if (document.FindMember("name")->value != "JetBrains Rider")
|
||||
return;
|
||||
|
||||
// Find version
|
||||
auto versionMember = document.FindMember("version");
|
||||
if (versionMember == document.MemberEnd())
|
||||
@@ -141,14 +146,14 @@ bool sortInstallations(RiderInstallation* const& i1, RiderInstallation* const& i
|
||||
int32 version2[3] = { 0 };
|
||||
StringUtils::Parse(values1[0].Get(), &version1[0]);
|
||||
StringUtils::Parse(values1[1].Get(), &version1[1]);
|
||||
|
||||
if(values1.Count() > 2)
|
||||
|
||||
if (values1.Count() > 2)
|
||||
StringUtils::Parse(values1[2].Get(), &version1[2]);
|
||||
|
||||
|
||||
StringUtils::Parse(values2[0].Get(), &version2[0]);
|
||||
StringUtils::Parse(values2[1].Get(), &version2[1]);
|
||||
|
||||
if(values2.Count() > 2)
|
||||
|
||||
if (values2.Count() > 2)
|
||||
StringUtils::Parse(values2[2].Get(), &version2[2]);
|
||||
|
||||
// Compare by MAJOR.MINOR.BUILD
|
||||
@@ -174,7 +179,7 @@ void RiderCodeEditor::FindEditors(Array<CodeEditor*>* output)
|
||||
|
||||
String localAppDataPath;
|
||||
FileSystem::GetSpecialFolderPath(SpecialFolder::LocalAppData, localAppDataPath);
|
||||
|
||||
|
||||
#if PLATFORM_WINDOWS
|
||||
// Lookup from all known registry locations
|
||||
SearchRegistry(&installations, HKEY_CURRENT_USER, TEXT("SOFTWARE\\WOW6432Node\\JetBrains\\Rider for Unreal Engine"));
|
||||
@@ -187,6 +192,7 @@ void RiderCodeEditor::FindEditors(Array<CodeEditor*>* output)
|
||||
SearchRegistry(&installations, HKEY_LOCAL_MACHINE, TEXT("SOFTWARE\\WOW6432Node\\JetBrains\\JetBrains Rider"));
|
||||
|
||||
// Versions installed via JetBrains Toolbox
|
||||
FileSystem::GetChildDirectories(subDirectories, localAppDataPath / TEXT("Programs"));
|
||||
FileSystem::GetChildDirectories(subDirectories, localAppDataPath / TEXT("JetBrains\\Toolbox\\apps\\Rider\\ch-0\\"));
|
||||
FileSystem::GetChildDirectories(subDirectories, localAppDataPath / TEXT("JetBrains\\Toolbox\\apps\\Rider\\ch-1\\")); // Beta versions
|
||||
#endif
|
||||
@@ -201,6 +207,7 @@ void RiderCodeEditor::FindEditors(Array<CodeEditor*>* output)
|
||||
FileSystem::GetChildDirectories(subDirectories, TEXT("/opt/"));
|
||||
|
||||
// Versions installed via JetBrains Toolbox
|
||||
SearchDirectory(&installations, localAppDataPath / TEXT(".local/share/JetBrains/Toolbox/apps/rider/"));
|
||||
FileSystem::GetChildDirectories(subDirectories, localAppDataPath / TEXT(".local/share/JetBrains/Toolbox/apps/Rider/ch-0"));
|
||||
FileSystem::GetChildDirectories(subDirectories, localAppDataPath / TEXT(".local/share/JetBrains/Toolbox/apps/Rider/ch-1")); // Beta versions
|
||||
|
||||
@@ -210,7 +217,24 @@ void RiderCodeEditor::FindEditors(Array<CodeEditor*>* output)
|
||||
TEXT("flatpak run com.jetbrains.Rider"));
|
||||
#endif
|
||||
|
||||
for (auto directory : subDirectories)
|
||||
#if PLATFORM_MAC
|
||||
String applicationSupportFolder;
|
||||
FileSystem::GetSpecialFolderPath(SpecialFolder::ProgramData, applicationSupportFolder);
|
||||
|
||||
Array<String> subMacDirectories;
|
||||
FileSystem::GetChildDirectories(subMacDirectories, applicationSupportFolder / TEXT("JetBrains/Toolbox/apps/Rider/ch-0/"));
|
||||
FileSystem::GetChildDirectories(subMacDirectories, applicationSupportFolder / TEXT("JetBrains/Toolbox/apps/Rider/ch-1/"));
|
||||
for (const String& directory : subMacDirectories)
|
||||
{
|
||||
String riderAppDirectory = directory / TEXT("Rider.app/Contents/Resources");
|
||||
SearchDirectory(&installations, riderAppDirectory);
|
||||
}
|
||||
|
||||
// Check the local installer version
|
||||
SearchDirectory(&installations, TEXT("/Applications/Rider.app/Contents/Resources"));
|
||||
#endif
|
||||
|
||||
for (const String& directory : subDirectories)
|
||||
SearchDirectory(&installations, directory);
|
||||
|
||||
// Sort found installations by version number
|
||||
@@ -244,8 +268,16 @@ void RiderCodeEditor::OpenFile(const String& path, int32 line)
|
||||
// Open file
|
||||
line = line > 0 ? line : 1;
|
||||
CreateProcessSettings procSettings;
|
||||
|
||||
#if !PLATFORM_MAC
|
||||
procSettings.FileName = _execPath;
|
||||
procSettings.Arguments = String::Format(TEXT("\"{0}\" --line {2} \"{1}\""), _solutionPath, path, line);
|
||||
#else
|
||||
// This follows pretty much how all the other engines open rider which deals with cross architecture issues
|
||||
procSettings.FileName = "/usr/bin/open";
|
||||
procSettings.Arguments = String::Format(TEXT("-n -a \"{0}\" --args \"{1}\" --line {3} \"{2}\""), _execPath, _solutionPath, path, line);
|
||||
#endif
|
||||
|
||||
procSettings.HiddenWindow = false;
|
||||
procSettings.WaitForEnd = false;
|
||||
procSettings.LogOutput = false;
|
||||
@@ -263,8 +295,14 @@ void RiderCodeEditor::OpenSolution()
|
||||
|
||||
// Open solution
|
||||
CreateProcessSettings procSettings;
|
||||
#if !PLATFORM_MAC
|
||||
procSettings.FileName = _execPath;
|
||||
procSettings.Arguments = String::Format(TEXT("\"{0}\""), _solutionPath);
|
||||
#else
|
||||
// This follows pretty much how all the other engines open rider which deals with cross architecture issues
|
||||
procSettings.FileName = "/usr/bin/open";
|
||||
procSettings.Arguments = String::Format(TEXT("-n -a \"{0}\" \"{1}\""), _execPath, _solutionPath);
|
||||
#endif
|
||||
procSettings.HiddenWindow = false;
|
||||
procSettings.WaitForEnd = false;
|
||||
procSettings.LogOutput = false;
|
||||
|
||||
@@ -1393,5 +1393,32 @@ namespace FlaxEditor.Scripting
|
||||
return _custom.GetMembers(name, MemberTypes.Method, bindingAttr).FirstOrDefault();
|
||||
return ScriptMemberInfo.Null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Basic check to see if a type could be casted to another type
|
||||
/// </summary>
|
||||
/// <param name="from">Source type</param>
|
||||
/// <param name="to">Target type</param>
|
||||
/// <returns>True if the type can be casted</returns>
|
||||
public static bool CanCast(ScriptType from, ScriptType to)
|
||||
{
|
||||
if (from == to)
|
||||
return true;
|
||||
if (from == Null || to == Null)
|
||||
return false;
|
||||
return (from.Type != typeof(void) && from.Type != typeof(FlaxEngine.Object)) &&
|
||||
(to.Type != typeof(void) && to.Type != typeof(FlaxEngine.Object)) &&
|
||||
from.IsAssignableFrom(to);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Basic check to see if this type could be casted to another type
|
||||
/// </summary>
|
||||
/// <param name="to">Target type</param>
|
||||
/// <returns>True if the type can be casted</returns>
|
||||
public bool CanCastTo(ScriptType to)
|
||||
{
|
||||
return CanCast(this, to);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -246,20 +246,17 @@ bool ScriptsBuilder::RunBuildTool(const StringView& args, const StringView& work
|
||||
Log::FileNotFoundException(monoPath).SetLevel(LogType::Fatal);
|
||||
return true;
|
||||
}
|
||||
//const String monoPath = TEXT("mono");
|
||||
cmdLine.Append(TEXT("\""));
|
||||
const String monoPath = TEXT("mono");
|
||||
cmdLine.Append(monoPath);
|
||||
cmdLine.Append(TEXT("\" "));
|
||||
cmdLine.Append(TEXT(" "));
|
||||
// TODO: Set env var for the mono MONO_GC_PARAMS=nursery-size64m to boost build performance -> profile it
|
||||
#endif
|
||||
cmdLine.Append(TEXT("\""));
|
||||
cmdLine.Append(buildToolPath);
|
||||
cmdLine.Append(TEXT("\" "));
|
||||
cmdLine.Append(args.Get(), args.Length());
|
||||
|
||||
// Call build tool
|
||||
CreateProcessSettings procSettings;
|
||||
procSettings.FileName = StringView(*cmdLine, cmdLine.Length());
|
||||
procSettings.Arguments = args.Get();
|
||||
procSettings.WorkingDirectory = workingDir;
|
||||
const int32 result = Platform::CreateProcess(procSettings);
|
||||
if (result != 0)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.Utilities;
|
||||
|
||||
@@ -54,6 +55,13 @@ namespace FlaxEditor.States
|
||||
}
|
||||
else if (Editor.Options.Options.General.ForceScriptCompilationOnStartup && !skipCompile)
|
||||
{
|
||||
// Generate project files when Cache is missing or was cleared previously
|
||||
if (!Directory.Exists(Path.Combine(Editor.GameProject?.ProjectFolderPath, "Cache", "Intermediate")) ||
|
||||
!Directory.Exists(Path.Combine(Editor.GameProject?.ProjectFolderPath, "Cache", "Projects")))
|
||||
{
|
||||
var customArgs = Editor.Instance.CodeEditing.SelectedEditor.GenerateProjectCustomArgs;
|
||||
ScriptsBuilder.GenerateProject(customArgs);
|
||||
}
|
||||
// Compile scripts before loading any scenes, then we load them and can open scenes
|
||||
ScriptsBuilder.Compile();
|
||||
}
|
||||
|
||||
@@ -744,6 +744,16 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
|
||||
base.OnDestroy();
|
||||
}
|
||||
|
||||
internal static bool IsInputCompatible(NodeArchetype nodeArch, ScriptType outputType, ConnectionsHint hint)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
internal static bool IsOutputCompatible(NodeArchetype nodeArch, ScriptType inputType, ConnectionsHint hint)
|
||||
{
|
||||
return inputType.IsVoid;
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class InvokeMethodNode : SurfaceNode
|
||||
@@ -1151,6 +1161,54 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
|
||||
base.OnDestroy();
|
||||
}
|
||||
|
||||
internal static bool IsInputCompatible(NodeArchetype nodeArch, ScriptType outputType, ConnectionsHint hint)
|
||||
{
|
||||
if (nodeArch.Tag is not ScriptMemberInfo memberInfo)
|
||||
return false;
|
||||
|
||||
if (!memberInfo.IsStatic)
|
||||
{
|
||||
if (VisjectSurface.FullCastCheck(memberInfo.DeclaringType, outputType, hint))
|
||||
return true;
|
||||
}
|
||||
|
||||
var parameters = memberInfo.GetParameters();
|
||||
bool isPure = (parameters.Length == 0 && !memberInfo.ValueType.IsVoid);
|
||||
if (outputType.IsVoid)
|
||||
return !isPure;
|
||||
|
||||
foreach (var param in parameters)
|
||||
{
|
||||
if (param.IsOut)
|
||||
continue;
|
||||
if (VisjectSurface.FullCastCheck(param.Type, outputType, hint))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
internal static bool IsOutputCompatible(NodeArchetype nodeArch, ScriptType inputType, ConnectionsHint hint)
|
||||
{
|
||||
if (nodeArch.Tag is not ScriptMemberInfo memberInfo)
|
||||
return false;
|
||||
if (VisjectSurface.FullCastCheck(memberInfo.ValueType, inputType, hint))
|
||||
return true;
|
||||
|
||||
var parameters = memberInfo.GetParameters();
|
||||
bool isPure = (parameters.Length == 0 && !memberInfo.ValueType.IsVoid);
|
||||
if (inputType.IsVoid)
|
||||
return !isPure;
|
||||
|
||||
foreach (var param in memberInfo.GetParameters())
|
||||
{
|
||||
if (!param.IsOut)
|
||||
continue;
|
||||
if (VisjectSurface.FullCastCheck(param.Type, inputType, hint))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class ReturnNode : SurfaceNode
|
||||
@@ -1777,6 +1835,16 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
|
||||
base.OnDestroy();
|
||||
}
|
||||
|
||||
internal static bool IsInputCompatible(NodeArchetype nodeArch, ScriptType outputType, ConnectionsHint hint)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
internal static bool IsOutputCompatible(NodeArchetype nodeArch, ScriptType inputType, ConnectionsHint hint)
|
||||
{
|
||||
return inputType.IsVoid;
|
||||
}
|
||||
}
|
||||
|
||||
private abstract class FieldNodeBase : SurfaceNode
|
||||
@@ -1913,6 +1981,64 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
Title = "Get " + SurfaceUtils.GetMethodDisplayName((string)Values[1]);
|
||||
UpdateSignature();
|
||||
}
|
||||
|
||||
internal static bool IsInputCompatible(NodeArchetype nodeArch, ScriptType outputType, ConnectionsHint hint)
|
||||
{
|
||||
var scriptType = TypeUtils.GetType((string)nodeArch.DefaultValues[0]);
|
||||
if (scriptType == ScriptType.Null)
|
||||
return false;
|
||||
|
||||
var members = scriptType.GetMembers((string)nodeArch.DefaultValues[1], MemberTypes.Field, BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly);
|
||||
foreach (var member in members)
|
||||
{
|
||||
if (!SurfaceUtils.IsValidVisualScriptField(member))
|
||||
continue;
|
||||
|
||||
if (member)
|
||||
{
|
||||
if (!member.IsStatic && VisjectSurface.FullCastCheck(scriptType, outputType, hint))
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
var isStatic = (bool)nodeArch.DefaultValues[3];
|
||||
if (!isStatic && VisjectSurface.FullCastCheck(scriptType, outputType, hint))
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
internal static bool IsOutputCompatible(NodeArchetype nodeArch, ScriptType inputType, ConnectionsHint hint)
|
||||
{
|
||||
var scriptType = TypeUtils.GetType((string)nodeArch.DefaultValues[0]);
|
||||
if (scriptType == ScriptType.Null)
|
||||
return false;
|
||||
|
||||
var members = scriptType.GetMembers((string)nodeArch.DefaultValues[1], MemberTypes.Field, BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly);
|
||||
foreach (var member in members)
|
||||
{
|
||||
if (!SurfaceUtils.IsValidVisualScriptField(member))
|
||||
continue;
|
||||
|
||||
if (member)
|
||||
{
|
||||
if (VisjectSurface.FullCastCheck(member.ValueType, inputType, hint))
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
var typeName = (string)nodeArch.DefaultValues[2];
|
||||
if (VisjectSurface.FullCastCheck(TypeUtils.GetType(typeName), inputType, hint))
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class SetFieldNode : FieldNodeBase
|
||||
@@ -1966,6 +2092,48 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
Title = "Set " + SurfaceUtils.GetMethodDisplayName((string)Values[1]);
|
||||
UpdateSignature();
|
||||
}
|
||||
|
||||
internal static bool IsInputCompatible(NodeArchetype nodeArch, ScriptType outputType, ConnectionsHint hint)
|
||||
{
|
||||
if (outputType.IsVoid)
|
||||
return true;
|
||||
|
||||
var scriptType = TypeUtils.GetType((string)nodeArch.DefaultValues[0]);
|
||||
if (scriptType == ScriptType.Null)
|
||||
return false;
|
||||
|
||||
var members = scriptType.GetMembers((string)nodeArch.DefaultValues[1], MemberTypes.Field, BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly);
|
||||
foreach (var member in members)
|
||||
{
|
||||
if (!SurfaceUtils.IsValidVisualScriptField(member))
|
||||
continue;
|
||||
|
||||
if (member)
|
||||
{
|
||||
if (VisjectSurface.FullCastCheck(member.ValueType, outputType, hint))
|
||||
return true;
|
||||
if (!member.IsStatic && VisjectSurface.FullCastCheck(scriptType, outputType, hint))
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
var typeName = (string)nodeArch.DefaultValues[2];
|
||||
if (VisjectSurface.FullCastCheck(TypeUtils.GetType(typeName), outputType, hint))
|
||||
return true;
|
||||
var isStatic = (bool)nodeArch.DefaultValues[3];
|
||||
if (!isStatic && VisjectSurface.FullCastCheck(scriptType, outputType, hint))
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
internal static bool IsOutputCompatible(NodeArchetype nodeArch, ScriptType inputType, ConnectionsHint hint)
|
||||
{
|
||||
return inputType.IsVoid;
|
||||
}
|
||||
}
|
||||
|
||||
private abstract class EventBaseNode : SurfaceNode, IFunctionsDependantNode
|
||||
@@ -2184,6 +2352,43 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
|
||||
base.OnDestroy();
|
||||
}
|
||||
|
||||
internal static bool IsInputCompatible(NodeArchetype nodeArch, ScriptType outputType, ConnectionsHint hint)
|
||||
{
|
||||
// Event based nodes always have a pulse input, so it's always compatible with void
|
||||
if (outputType.IsVoid)
|
||||
return true;
|
||||
|
||||
var eventName = (string)nodeArch.DefaultValues[1];
|
||||
var eventType = TypeUtils.GetType((string)nodeArch.DefaultValues[0]);
|
||||
var member = eventType.GetMember(eventName, MemberTypes.Event, BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance);
|
||||
if (member && SurfaceUtils.IsValidVisualScriptEvent(member))
|
||||
{
|
||||
if (!member.IsStatic)
|
||||
{
|
||||
if (VisjectSurface.FullCastCheck(eventType, outputType, hint))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
internal static bool IsOutputCompatible(NodeArchetype nodeArch, ScriptType inputType, ConnectionsHint hint)
|
||||
{
|
||||
// Event based nodes always have a pulse output, so it's always compatible with void
|
||||
if (inputType.IsVoid)
|
||||
return true;
|
||||
|
||||
var eventName = (string)nodeArch.DefaultValues[1];
|
||||
var eventType = TypeUtils.GetType((string)nodeArch.DefaultValues[0]);
|
||||
var member = eventType.GetMember(eventName, MemberTypes.Event, BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance);
|
||||
if (member && SurfaceUtils.IsValidVisualScriptEvent(member))
|
||||
{
|
||||
if (VisjectSurface.FullCastCheck(member.ValueType, inputType, hint))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class BindEventNode : EventBaseNode
|
||||
@@ -2265,6 +2470,8 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
Title = string.Empty,
|
||||
Description = "Overrides the base class method with custom implementation",
|
||||
Flags = NodeFlags.VisualScriptGraph | NodeFlags.NoSpawnViaGUI | NodeFlags.NoSpawnViaPaste,
|
||||
IsInputCompatible = MethodOverrideNode.IsInputCompatible,
|
||||
IsOutputCompatible = MethodOverrideNode.IsOutputCompatible,
|
||||
Size = new Float2(240, 60),
|
||||
DefaultValues = new object[]
|
||||
{
|
||||
@@ -2277,6 +2484,8 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
{
|
||||
TypeID = 4,
|
||||
Create = (id, context, arch, groupArch) => new InvokeMethodNode(id, context, arch, groupArch),
|
||||
IsInputCompatible = InvokeMethodNode.IsInputCompatible,
|
||||
IsOutputCompatible = InvokeMethodNode.IsOutputCompatible,
|
||||
Title = string.Empty,
|
||||
Flags = NodeFlags.VisualScriptGraph | NodeFlags.NoSpawnViaGUI,
|
||||
Size = new Float2(240, 60),
|
||||
@@ -2317,6 +2526,8 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
{
|
||||
TypeID = 6,
|
||||
Create = (id, context, arch, groupArch) => new VisualScriptFunctionNode(id, context, arch, groupArch),
|
||||
IsInputCompatible = VisualScriptFunctionNode.IsInputCompatible,
|
||||
IsOutputCompatible = VisualScriptFunctionNode.IsOutputCompatible,
|
||||
Title = "New Function",
|
||||
Description = "Adds a new function to the script",
|
||||
Flags = NodeFlags.VisualScriptGraph,
|
||||
@@ -2330,6 +2541,8 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
{
|
||||
TypeID = 7,
|
||||
Create = (id, context, arch, groupArch) => new GetFieldNode(id, context, arch, groupArch),
|
||||
IsInputCompatible = GetFieldNode.IsInputCompatible,
|
||||
IsOutputCompatible = GetFieldNode.IsOutputCompatible,
|
||||
Title = string.Empty,
|
||||
Flags = NodeFlags.VisualScriptGraph | NodeFlags.NoSpawnViaGUI,
|
||||
Size = new Float2(240, 60),
|
||||
@@ -2345,6 +2558,8 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
{
|
||||
TypeID = 8,
|
||||
Create = (id, context, arch, groupArch) => new SetFieldNode(id, context, arch, groupArch),
|
||||
IsInputCompatible = SetFieldNode.IsInputCompatible,
|
||||
IsOutputCompatible = SetFieldNode.IsOutputCompatible,
|
||||
Title = string.Empty,
|
||||
Flags = NodeFlags.VisualScriptGraph | NodeFlags.NoSpawnViaGUI,
|
||||
Size = new Float2(240, 60),
|
||||
@@ -2361,6 +2576,8 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
{
|
||||
TypeID = 9,
|
||||
Create = (id, context, arch, groupArch) => new BindEventNode(id, context, arch, groupArch),
|
||||
IsInputCompatible = EventBaseNode.IsInputCompatible,
|
||||
IsOutputCompatible = EventBaseNode.IsOutputCompatible,
|
||||
Title = string.Empty,
|
||||
Flags = NodeFlags.VisualScriptGraph | NodeFlags.NoSpawnViaGUI,
|
||||
Size = new Float2(260, 60),
|
||||
@@ -2383,6 +2600,8 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
{
|
||||
TypeID = 10,
|
||||
Create = (id, context, arch, groupArch) => new UnbindEventNode(id, context, arch, groupArch),
|
||||
IsInputCompatible = EventBaseNode.IsInputCompatible,
|
||||
IsOutputCompatible = EventBaseNode.IsOutputCompatible,
|
||||
Title = string.Empty,
|
||||
Flags = NodeFlags.VisualScriptGraph | NodeFlags.NoSpawnViaGUI,
|
||||
Size = new Float2(260, 60),
|
||||
|
||||
@@ -126,7 +126,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
Title = "Clamp",
|
||||
Description = "Clamps value to the specified range",
|
||||
Flags = NodeFlags.AllGraphs,
|
||||
Size = new Float2(110, 60),
|
||||
Size = new Float2(140, 60),
|
||||
ConnectionsHints = ConnectionsHint.Numeric,
|
||||
IndependentBoxes = new[] { 0 },
|
||||
DependentBoxes = new[] { 1, 2, 3 },
|
||||
|
||||
@@ -216,6 +216,35 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
: base(id, context, nodeArch, groupArch, false)
|
||||
{
|
||||
}
|
||||
|
||||
internal static bool IsInputCompatible(NodeArchetype nodeArch, ScriptType outputType, ConnectionsHint hint)
|
||||
{
|
||||
var typeName = (string)nodeArch.DefaultValues[0];
|
||||
var type = TypeUtils.GetType(typeName);
|
||||
if (type)
|
||||
{
|
||||
var fields = type.GetMembers(BindingFlags.Public | BindingFlags.Instance).Where(x => x.IsField).ToArray();
|
||||
var fieldsLength = fields.Length;
|
||||
for (var i = 0; i < fieldsLength; i++)
|
||||
{
|
||||
if (VisjectSurface.FullCastCheck(fields[i].ValueType, outputType, hint))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
internal static bool IsOutputCompatible(NodeArchetype nodeArch, ScriptType inputType, ConnectionsHint hint)
|
||||
{
|
||||
var typeName = (string)nodeArch.DefaultValues[0];
|
||||
var type = TypeUtils.GetType(typeName);
|
||||
if (type)
|
||||
{
|
||||
if (VisjectSurface.FullCastCheck(type, inputType, hint))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class UnpackStructureNode : StructureNode
|
||||
@@ -225,6 +254,35 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
: base(id, context, nodeArch, groupArch, true)
|
||||
{
|
||||
}
|
||||
|
||||
internal static bool IsInputCompatible(NodeArchetype nodeArch, ScriptType outputType, ConnectionsHint hint)
|
||||
{
|
||||
var typeName = (string)nodeArch.DefaultValues[0];
|
||||
var type = TypeUtils.GetType(typeName);
|
||||
if (type)
|
||||
{
|
||||
if (VisjectSurface.FullCastCheck(type, outputType, hint))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
internal static bool IsOutputCompatible(NodeArchetype nodeArch, ScriptType inputType, ConnectionsHint hint)
|
||||
{
|
||||
var typeName = (string)nodeArch.DefaultValues[0];
|
||||
var type = TypeUtils.GetType(typeName);
|
||||
if (type)
|
||||
{
|
||||
var fields = type.GetMembers(BindingFlags.Public | BindingFlags.Instance).Where(x => x.IsField).ToArray();
|
||||
var fieldsLength = fields.Length;
|
||||
for (var i = 0; i < fieldsLength; i++)
|
||||
{
|
||||
if (VisjectSurface.FullCastCheck(fields[i].ValueType, inputType, hint))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -351,6 +409,8 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
TypeID = 26,
|
||||
Title = "Pack Structure",
|
||||
Create = (id, context, arch, groupArch) => new PackStructureNode(id, context, arch, groupArch),
|
||||
IsInputCompatible = PackStructureNode.IsInputCompatible,
|
||||
IsOutputCompatible = PackStructureNode.IsOutputCompatible,
|
||||
Description = "Makes the structure data to from the components.",
|
||||
Flags = NodeFlags.VisualScriptGraph | NodeFlags.AnimGraph | NodeFlags.NoSpawnViaGUI,
|
||||
Size = new Float2(180, 20),
|
||||
@@ -461,6 +521,8 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
TypeID = 36,
|
||||
Title = "Unpack Structure",
|
||||
Create = (id, context, arch, groupArch) => new UnpackStructureNode(id, context, arch, groupArch),
|
||||
IsInputCompatible = UnpackStructureNode.IsInputCompatible,
|
||||
IsOutputCompatible = UnpackStructureNode.IsOutputCompatible,
|
||||
Description = "Breaks the structure data to allow extracting components from it.",
|
||||
Flags = NodeFlags.VisualScriptGraph | NodeFlags.AnimGraph | NodeFlags.NoSpawnViaGUI,
|
||||
Size = new Float2(180, 20),
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the type of the picker and the type of the output box
|
||||
/// </summary>
|
||||
/// <param name="type">Target Type</param>
|
||||
public void SetPickerValue(ScriptType type)
|
||||
{
|
||||
_picker.Value = type;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnDestroy()
|
||||
{
|
||||
@@ -999,6 +1008,15 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
GetBox(4).CurrentType = type ? type : _picker.Type;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the type of the picker and the type of the output box
|
||||
/// </summary>
|
||||
/// <param name="type">Target Type</param>
|
||||
public void SetPickerValue(ScriptType type)
|
||||
{
|
||||
_picker.Value = type;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnDestroy()
|
||||
{
|
||||
|
||||
@@ -5,7 +5,6 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using FlaxEditor.GUI.ContextMenu;
|
||||
using FlaxEditor.GUI.Input;
|
||||
using FlaxEditor.Scripting;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
using FlaxEngine.Utilities;
|
||||
@@ -40,6 +39,8 @@ namespace FlaxEditor.Surface.ContextMenu
|
||||
public delegate List<SurfaceParameter> ParameterGetterDelegate();
|
||||
|
||||
private readonly List<VisjectCMGroup> _groups = new List<VisjectCMGroup>(16);
|
||||
private CheckBox _contextSensitiveToggle;
|
||||
private bool _contextSensitiveSearchEnabled = true;
|
||||
private readonly TextBox _searchBox;
|
||||
private bool _waitingForInput;
|
||||
private VisjectCMGroup _surfaceParametersGroup;
|
||||
@@ -127,7 +128,7 @@ namespace FlaxEditor.Surface.ContextMenu
|
||||
_parameterSetNodeArchetype = info.ParameterSetNodeArchetype ?? Archetypes.Parameters.Nodes[3];
|
||||
|
||||
// Context menu dimensions
|
||||
Size = new Float2(320, 248);
|
||||
Size = new Float2(300, 400);
|
||||
|
||||
var headerPanel = new Panel(ScrollBars.None)
|
||||
{
|
||||
@@ -139,17 +140,41 @@ namespace FlaxEditor.Surface.ContextMenu
|
||||
};
|
||||
|
||||
// Title bar
|
||||
var titleFontReference = new FontReference(Style.Current.FontLarge.Asset, 10);
|
||||
var titleLabel = new Label
|
||||
{
|
||||
Width = Width - 8,
|
||||
Width = Width * 0.5f - 8f,
|
||||
Height = 20,
|
||||
X = 4,
|
||||
Parent = headerPanel,
|
||||
Text = "Select Node",
|
||||
HorizontalAlignment = TextAlignment.Center,
|
||||
Font = new FontReference(Style.Current.FontLarge.Asset, 10),
|
||||
HorizontalAlignment = TextAlignment.Near,
|
||||
Font = titleFontReference,
|
||||
};
|
||||
|
||||
// Context sensitive toggle
|
||||
var contextSensitiveLabel = new Label
|
||||
{
|
||||
Width = Width * 0.5f - 28,
|
||||
Height = 20,
|
||||
X = Width * 0.5f,
|
||||
Parent = headerPanel,
|
||||
Text = "Context Sensitive",
|
||||
TooltipText = "Should the nodes be filtered to only show those that can be connected in the current context?",
|
||||
HorizontalAlignment = TextAlignment.Far,
|
||||
Font = titleFontReference,
|
||||
};
|
||||
|
||||
_contextSensitiveToggle = new CheckBox
|
||||
{
|
||||
Width = 20,
|
||||
Height = 20,
|
||||
X = Width - 24,
|
||||
Parent = headerPanel,
|
||||
Checked = _contextSensitiveSearchEnabled,
|
||||
};
|
||||
_contextSensitiveToggle.StateChanged += OnContextSensitiveToggleStateChanged;
|
||||
|
||||
// Search box
|
||||
_searchBox = new SearchBox(false, 2, 22)
|
||||
{
|
||||
@@ -288,6 +313,10 @@ namespace FlaxEditor.Surface.ContextMenu
|
||||
OnSearchFilterChanged();
|
||||
}
|
||||
}
|
||||
else if (_contextSensitiveSearchEnabled)
|
||||
{
|
||||
group.EvaluateVisibilityWithBox(_selectedBox);
|
||||
}
|
||||
|
||||
Profiler.EndEvent();
|
||||
}
|
||||
@@ -321,6 +350,8 @@ namespace FlaxEditor.Surface.ContextMenu
|
||||
Parent = group
|
||||
};
|
||||
}
|
||||
if (_contextSensitiveSearchEnabled)
|
||||
group.EvaluateVisibilityWithBox(_selectedBox);
|
||||
group.SortChildren();
|
||||
group.Parent = _groupsPanel;
|
||||
_groups.Add(group);
|
||||
@@ -418,8 +449,26 @@ namespace FlaxEditor.Surface.ContextMenu
|
||||
return;
|
||||
|
||||
Profiler.BeginEvent("VisjectCM.OnSearchFilterChanged");
|
||||
|
||||
if (string.IsNullOrEmpty(_searchBox.Text))
|
||||
UpdateFilters();
|
||||
_searchBox.Focus();
|
||||
Profiler.EndEvent();
|
||||
}
|
||||
|
||||
private void OnContextSensitiveToggleStateChanged(CheckBox checkBox)
|
||||
{
|
||||
// Skip events during setup or init stuff
|
||||
if (IsLayoutLocked)
|
||||
return;
|
||||
|
||||
Profiler.BeginEvent("VisjectCM.OnContextSensitiveToggleStateChanged");
|
||||
_contextSensitiveSearchEnabled = checkBox.Checked;
|
||||
UpdateFilters();
|
||||
Profiler.EndEvent();
|
||||
}
|
||||
|
||||
private void UpdateFilters()
|
||||
{
|
||||
if (string.IsNullOrEmpty(_searchBox.Text) && _selectedBox == null)
|
||||
{
|
||||
ResetView();
|
||||
Profiler.EndEvent();
|
||||
@@ -430,7 +479,7 @@ namespace FlaxEditor.Surface.ContextMenu
|
||||
LockChildrenRecursive();
|
||||
for (int i = 0; i < _groups.Count; i++)
|
||||
{
|
||||
_groups[i].UpdateFilter(_searchBox.Text);
|
||||
_groups[i].UpdateFilter(_searchBox.Text, _contextSensitiveSearchEnabled ? _selectedBox : null);
|
||||
_groups[i].UpdateItemSort(_selectedBox);
|
||||
}
|
||||
SortGroups();
|
||||
@@ -443,9 +492,6 @@ namespace FlaxEditor.Surface.ContextMenu
|
||||
PerformLayout();
|
||||
if (SelectedItem != null)
|
||||
_panel1.ScrollViewTo(SelectedItem);
|
||||
_searchBox.Focus();
|
||||
Profiler.EndEvent();
|
||||
|
||||
Profiler.EndEvent();
|
||||
}
|
||||
|
||||
@@ -503,7 +549,11 @@ namespace FlaxEditor.Surface.ContextMenu
|
||||
_searchBox.Clear();
|
||||
SelectedItem = null;
|
||||
for (int i = 0; i < _groups.Count; i++)
|
||||
{
|
||||
_groups[i].ResetView();
|
||||
if (_contextSensitiveSearchEnabled)
|
||||
_groups[i].EvaluateVisibilityWithBox(_selectedBox);
|
||||
}
|
||||
UnlockChildrenRecursive();
|
||||
|
||||
SortGroups();
|
||||
@@ -671,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;
|
||||
}
|
||||
@@ -759,5 +807,12 @@ namespace FlaxEditor.Surface.ContextMenu
|
||||
{
|
||||
return GetPreviousSiblings(item).OfType<T>();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnDestroy()
|
||||
{
|
||||
_contextSensitiveToggle.StateChanged -= OnContextSensitiveToggleStateChanged;
|
||||
base.OnDestroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,7 +66,7 @@ namespace FlaxEditor.Surface.ContextMenu
|
||||
{
|
||||
if (_children[i] is VisjectCMItem item)
|
||||
{
|
||||
item.UpdateFilter(null);
|
||||
item.UpdateFilter(null, null);
|
||||
item.UpdateScore(null);
|
||||
}
|
||||
}
|
||||
@@ -84,23 +84,42 @@ namespace FlaxEditor.Surface.ContextMenu
|
||||
/// Updates the filter.
|
||||
/// </summary>
|
||||
/// <param name="filterText">The filter text.</param>
|
||||
public void UpdateFilter(string filterText)
|
||||
/// <param name="selectedBox">The optionally selected box to show hints for it.</param>
|
||||
public void UpdateFilter(string filterText, Box selectedBox)
|
||||
{
|
||||
Profiler.BeginEvent("VisjectCMGroup.UpdateFilter");
|
||||
|
||||
// Update items
|
||||
bool isAnyVisible = false;
|
||||
bool groupHeaderMatches = QueryFilterHelper.Match(filterText, HeaderText);
|
||||
for (int i = 0; i < _children.Count; i++)
|
||||
{
|
||||
if (_children[i] is VisjectCMItem item)
|
||||
{
|
||||
item.UpdateFilter(filterText);
|
||||
item.UpdateFilter(filterText, selectedBox, groupHeaderMatches);
|
||||
isAnyVisible |= item.Visible;
|
||||
}
|
||||
}
|
||||
|
||||
// Update header title
|
||||
if (QueryFilterHelper.Match(filterText, HeaderText))
|
||||
// Update itself
|
||||
if (isAnyVisible)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(filterText))
|
||||
Open(false);
|
||||
Visible = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Hide group if none of the items matched the filter
|
||||
Visible = false;
|
||||
}
|
||||
|
||||
Profiler.EndEvent();
|
||||
}
|
||||
|
||||
internal void EvaluateVisibilityWithBox(Box selectedBox)
|
||||
{
|
||||
if (selectedBox == null)
|
||||
{
|
||||
for (int i = 0; i < _children.Count; i++)
|
||||
{
|
||||
@@ -109,14 +128,25 @@ namespace FlaxEditor.Surface.ContextMenu
|
||||
item.Visible = true;
|
||||
}
|
||||
}
|
||||
isAnyVisible = true;
|
||||
Visible = true;
|
||||
return;
|
||||
}
|
||||
|
||||
Profiler.BeginEvent("VisjectCMGroup.EvaluateVisibilityWithBox");
|
||||
|
||||
bool isAnyVisible = false;
|
||||
for (int i = 0; i < _children.Count; i++)
|
||||
{
|
||||
if (_children[i] is VisjectCMItem item)
|
||||
{
|
||||
item.Visible = item.CanConnectTo(selectedBox);
|
||||
isAnyVisible |= item.Visible;
|
||||
}
|
||||
}
|
||||
|
||||
// Update itself
|
||||
if (isAnyVisible)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(filterText))
|
||||
Open(false);
|
||||
Visible = true;
|
||||
}
|
||||
else
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using FlaxEditor.Scripting;
|
||||
using FlaxEditor.Surface.Elements;
|
||||
using FlaxEditor.Utilities;
|
||||
using FlaxEngine;
|
||||
@@ -56,7 +57,7 @@ namespace FlaxEditor.Surface.ContextMenu
|
||||
/// <param name="groupArchetype">The group archetype.</param>
|
||||
/// <param name="archetype">The archetype.</param>
|
||||
public VisjectCMItem(VisjectCMGroup group, GroupArchetype groupArchetype, NodeArchetype archetype)
|
||||
: base(0, 0, 120, 12)
|
||||
: base(0, 0, 120, 14)
|
||||
{
|
||||
Group = group;
|
||||
_groupArchetype = groupArchetype;
|
||||
@@ -77,7 +78,7 @@ namespace FlaxEditor.Surface.ContextMenu
|
||||
if (!Visible)
|
||||
return;
|
||||
|
||||
if (selectedBox != null && CanConnectTo(selectedBox, NodeArchetype))
|
||||
if (selectedBox != null && CanConnectTo(selectedBox))
|
||||
SortScore += 1;
|
||||
if (Data != null)
|
||||
SortScore += 1;
|
||||
@@ -92,35 +93,93 @@ namespace FlaxEditor.Surface.ContextMenu
|
||||
textRect = new Rectangle(22, 0, Width - 24, Height);
|
||||
}
|
||||
|
||||
private bool CanConnectTo(Box startBox, NodeArchetype nodeArchetype)
|
||||
/// <summary>
|
||||
/// Checks if this context menu item can be connected to a given box, before a node is actually spawned.
|
||||
/// </summary>
|
||||
/// <param name="startBox">The connected box</param>
|
||||
/// <returns>True if the connected box is compatible with this item</returns>
|
||||
public bool CanConnectTo(Box startBox)
|
||||
{
|
||||
// Is compatible if box is null for reset reasons
|
||||
if (startBox == null)
|
||||
return false;
|
||||
if (!startBox.IsOutput)
|
||||
return false; // For now, I'm only handing the output box case
|
||||
return true;
|
||||
|
||||
if (nodeArchetype.Elements != null)
|
||||
if (_archetype == null)
|
||||
return false;
|
||||
|
||||
bool isCompatible = false;
|
||||
if (startBox.IsOutput && _archetype.IsInputCompatible != null)
|
||||
{
|
||||
for (int i = 0; i < nodeArchetype.Elements.Length; i++)
|
||||
{
|
||||
if (nodeArchetype.Elements[i].Type == NodeElementType.Input &&
|
||||
startBox.CanUseType(nodeArchetype.Elements[i].ConnectionsType))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
isCompatible |= _archetype.IsInputCompatible.Invoke(_archetype, startBox.CurrentType, _archetype.ConnectionsHints);
|
||||
}
|
||||
return false;
|
||||
else if (!startBox.IsOutput && _archetype.IsOutputCompatible != null)
|
||||
{
|
||||
isCompatible |= _archetype.IsOutputCompatible.Invoke(_archetype, startBox.CurrentType, startBox.ParentNode.Archetype.ConnectionsHints);
|
||||
}
|
||||
else if (_archetype.Elements != null)
|
||||
{
|
||||
// Check compatibility based on the defined elements in the archetype. This handles all the default groups and items
|
||||
isCompatible = CheckElementsCompatibility(startBox);
|
||||
}
|
||||
|
||||
return isCompatible;
|
||||
}
|
||||
|
||||
private bool CheckElementsCompatibility(Box startBox)
|
||||
{
|
||||
bool isCompatible = false;
|
||||
foreach (NodeElementArchetype element in _archetype.Elements)
|
||||
{
|
||||
// Ignore all elements that aren't inputs or outputs (e.g. input fields)
|
||||
if (element.Type != NodeElementType.Output && element.Type != NodeElementType.Input)
|
||||
continue;
|
||||
|
||||
// Ignore elements with the same direction as the box
|
||||
if ((startBox.IsOutput && element.Type == NodeElementType.Output) || (!startBox.IsOutput && element.Type == NodeElementType.Input))
|
||||
continue;
|
||||
|
||||
ScriptType fromType;
|
||||
ScriptType toType;
|
||||
ConnectionsHint hint;
|
||||
if (startBox.IsOutput)
|
||||
{
|
||||
fromType = element.ConnectionsType;
|
||||
toType = startBox.CurrentType;
|
||||
hint = _archetype.ConnectionsHints;
|
||||
}
|
||||
else
|
||||
{
|
||||
fromType = startBox.CurrentType;
|
||||
toType = element.ConnectionsType;
|
||||
hint = startBox.ParentNode.Archetype.ConnectionsHints;
|
||||
}
|
||||
|
||||
isCompatible |= VisjectSurface.FullCastCheck(fromType, toType, hint);
|
||||
}
|
||||
|
||||
return isCompatible;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the filter.
|
||||
/// </summary>
|
||||
/// <param name="filterText">The filter text.</param>
|
||||
public void UpdateFilter(string filterText)
|
||||
/// <param name="selectedBox">The optionally selected box to show hints for it.</param>
|
||||
/// <param name="groupHeaderMatches">True if item's group header got a filter match and item should stay visible.</param>
|
||||
public void UpdateFilter(string filterText, Box selectedBox, bool groupHeaderMatches = false)
|
||||
{
|
||||
if (selectedBox != null)
|
||||
{
|
||||
Visible = CanConnectTo(selectedBox);
|
||||
if (!Visible)
|
||||
{
|
||||
_highlights?.Clear();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
_isStartsWithMatch = _isFullMatch = false;
|
||||
if (filterText == null)
|
||||
if (string.IsNullOrEmpty(filterText))
|
||||
{
|
||||
// Clear filter
|
||||
_highlights?.Clear();
|
||||
@@ -184,7 +243,7 @@ namespace FlaxEditor.Surface.ContextMenu
|
||||
|
||||
Data = data;
|
||||
}
|
||||
else
|
||||
else if (!groupHeaderMatches)
|
||||
{
|
||||
// Hide
|
||||
_highlights?.Clear();
|
||||
|
||||
@@ -91,7 +91,7 @@ namespace FlaxEditor.Surface.Elements
|
||||
_currentType = value;
|
||||
|
||||
// Check if will need to update box connections due to type change
|
||||
if ((Surface == null || Surface._isUpdatingBoxTypes == 0) && HasAnyConnection && !CanCast(prev, _currentType))
|
||||
if ((Surface == null || Surface._isUpdatingBoxTypes == 0) && HasAnyConnection && !prev.CanCastTo(_currentType))
|
||||
{
|
||||
// Remove all invalid connections and update those which still can be valid
|
||||
var connections = Connections.ToArray();
|
||||
@@ -231,58 +231,8 @@ namespace FlaxEditor.Surface.Elements
|
||||
}
|
||||
|
||||
// Check using connection hints
|
||||
var connectionsHints = ParentNode.Archetype.ConnectionsHints;
|
||||
if (Archetype.ConnectionsType == ScriptType.Null && connectionsHints != ConnectionsHint.None)
|
||||
{
|
||||
if ((connectionsHints & ConnectionsHint.Anything) == ConnectionsHint.Anything)
|
||||
return true;
|
||||
if ((connectionsHints & ConnectionsHint.Value) == ConnectionsHint.Value && type.Type != typeof(void))
|
||||
return true;
|
||||
if ((connectionsHints & ConnectionsHint.Enum) == ConnectionsHint.Enum && type.IsEnum)
|
||||
return true;
|
||||
if ((connectionsHints & ConnectionsHint.Array) == ConnectionsHint.Array && type.IsArray)
|
||||
return true;
|
||||
if ((connectionsHints & ConnectionsHint.Dictionary) == ConnectionsHint.Dictionary && type.IsDictionary)
|
||||
return true;
|
||||
if ((connectionsHints & ConnectionsHint.Vector) == ConnectionsHint.Vector)
|
||||
{
|
||||
var t = type.Type;
|
||||
if (t == typeof(Vector2) ||
|
||||
t == typeof(Vector3) ||
|
||||
t == typeof(Vector4) ||
|
||||
t == typeof(Float2) ||
|
||||
t == typeof(Float3) ||
|
||||
t == typeof(Float4) ||
|
||||
t == typeof(Double2) ||
|
||||
t == typeof(Double3) ||
|
||||
t == typeof(Double4) ||
|
||||
t == typeof(Int2) ||
|
||||
t == typeof(Int3) ||
|
||||
t == typeof(Int4) ||
|
||||
t == typeof(Color))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if ((connectionsHints & ConnectionsHint.Scalar) == ConnectionsHint.Scalar)
|
||||
{
|
||||
var t = type.Type;
|
||||
if (t == typeof(bool) ||
|
||||
t == typeof(char) ||
|
||||
t == typeof(byte) ||
|
||||
t == typeof(short) ||
|
||||
t == typeof(ushort) ||
|
||||
t == typeof(int) ||
|
||||
t == typeof(uint) ||
|
||||
t == typeof(long) ||
|
||||
t == typeof(ulong) ||
|
||||
t == typeof(float) ||
|
||||
t == typeof(double))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (VisjectSurface.IsTypeCompatible(Archetype.ConnectionsType, type, ParentNode.Archetype.ConnectionsHints))
|
||||
return true;
|
||||
|
||||
// Check independent and if there is box with bigger potential because it may block current one from changing type
|
||||
var parentArch = ParentNode.Archetype;
|
||||
@@ -296,7 +246,7 @@ namespace FlaxEditor.Surface.Elements
|
||||
var b = ParentNode.GetBox(boxes[i]);
|
||||
|
||||
// Check if its the same and tested type matches the default value type
|
||||
if (b == this && CanCast(parentArch.DefaultType, type))
|
||||
if (b == this && parentArch.DefaultType.CanCastTo(type))
|
||||
{
|
||||
// Can
|
||||
return true;
|
||||
@@ -718,17 +668,6 @@ namespace FlaxEditor.Surface.Elements
|
||||
}
|
||||
}
|
||||
|
||||
private static bool CanCast(ScriptType oB, ScriptType iB)
|
||||
{
|
||||
if (oB == iB)
|
||||
return true;
|
||||
if (oB == ScriptType.Null || iB == ScriptType.Null)
|
||||
return false;
|
||||
return (oB.Type != typeof(void) && oB.Type != typeof(FlaxEngine.Object)) &&
|
||||
(iB.Type != typeof(void) && iB.Type != typeof(FlaxEngine.Object)) &&
|
||||
oB.IsAssignableFrom(iB);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool AreConnected(IConnectionInstigator other)
|
||||
{
|
||||
@@ -787,7 +726,7 @@ namespace FlaxEditor.Surface.Elements
|
||||
{
|
||||
if (!iB.CanUseType(oB.CurrentType))
|
||||
{
|
||||
if (!CanCast(oB.CurrentType, iB.CurrentType))
|
||||
if (!oB.CurrentType.CanCastTo(iB.CurrentType))
|
||||
{
|
||||
// Cannot
|
||||
return false;
|
||||
@@ -798,7 +737,7 @@ namespace FlaxEditor.Surface.Elements
|
||||
{
|
||||
if (!oB.CanUseType(iB.CurrentType))
|
||||
{
|
||||
if (!CanCast(oB.CurrentType, iB.CurrentType))
|
||||
if (!oB.CurrentType.CanCastTo(iB.CurrentType))
|
||||
{
|
||||
// Cannot
|
||||
return false;
|
||||
@@ -871,7 +810,7 @@ namespace FlaxEditor.Surface.Elements
|
||||
bool useCaster = false;
|
||||
if (!iB.CanUseType(oB.CurrentType))
|
||||
{
|
||||
if (CanCast(oB.CurrentType, iB.CurrentType))
|
||||
if (oB.CurrentType.CanCastTo(iB.CurrentType))
|
||||
useCaster = true;
|
||||
else
|
||||
return;
|
||||
@@ -881,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
|
||||
{
|
||||
|
||||
@@ -34,9 +34,7 @@ namespace FlaxEditor.Surface.Elements
|
||||
public static void DrawConnection(ref Float2 start, ref Float2 end, ref Color color, float thickness = 1)
|
||||
{
|
||||
// Calculate control points
|
||||
var dst = (end - start) * new Float2(0.5f, 0.05f);
|
||||
var control1 = new Float2(start.X + dst.X, start.Y + dst.Y);
|
||||
var control2 = new Float2(end.X - dst.X, end.Y + dst.Y);
|
||||
CalculateBezierControlPoints(start, end, out var control1, out var control2);
|
||||
|
||||
// Draw line
|
||||
Render2D.DrawBezier(start, control1, control2, end, color, thickness);
|
||||
@@ -49,6 +47,23 @@ namespace FlaxEditor.Surface.Elements
|
||||
*/
|
||||
}
|
||||
|
||||
private static void CalculateBezierControlPoints(Float2 start, Float2 end, out Float2 control1, out Float2 control2)
|
||||
{
|
||||
// Control points parameters
|
||||
const float minControlLength = 100f;
|
||||
const float maxControlLength = 150f;
|
||||
var dst = (end - start).Length;
|
||||
var yDst = Mathf.Abs(start.Y - end.Y);
|
||||
|
||||
// Calculate control points
|
||||
var minControlDst = dst * 0.5f;
|
||||
var maxControlDst = Mathf.Max(Mathf.Min(maxControlLength, dst), minControlLength);
|
||||
var controlDst = Mathf.Lerp(minControlDst, maxControlDst, Mathf.Clamp(yDst / minControlLength, 0f, 1f));
|
||||
|
||||
control1 = new Float2(start.X + controlDst, start.Y);
|
||||
control2 = new Float2(end.X - controlDst, end.Y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a point intersects a connection
|
||||
/// </summary>
|
||||
@@ -71,17 +86,15 @@ namespace FlaxEditor.Surface.Elements
|
||||
public static bool IntersectsConnection(ref Float2 start, ref Float2 end, ref Float2 point, float distance)
|
||||
{
|
||||
// Pretty much a point in rectangle check
|
||||
if ((point.X - start.X) * (end.X - point.X) < 0) return false;
|
||||
if ((point.X - start.X) * (end.X - point.X) < 0)
|
||||
return false;
|
||||
|
||||
float offset = Mathf.Sign(end.Y - start.Y) * distance;
|
||||
if ((point.Y - (start.Y - offset)) * ((end.Y + offset) - point.Y) < 0) return false;
|
||||
if ((point.Y - (start.Y - offset)) * ((end.Y + offset) - point.Y) < 0)
|
||||
return false;
|
||||
|
||||
// Taken from the Render2D.DrawBezier code
|
||||
float squaredDistance = distance;
|
||||
|
||||
var dst = (end - start) * new Float2(0.5f, 0.05f);
|
||||
var control1 = new Float2(start.X + dst.X, start.Y + dst.Y);
|
||||
var control2 = new Float2(end.X - dst.X, end.Y + dst.Y);
|
||||
CalculateBezierControlPoints(start, end, out var control1, out var control2);
|
||||
|
||||
var d1 = control1 - start;
|
||||
var d2 = control2 - control1;
|
||||
|
||||
@@ -89,6 +89,11 @@ namespace FlaxEditor.Surface
|
||||
/// <returns>The created node object.</returns>
|
||||
public delegate SurfaceNode CreateCustomNodeFunc(uint id, VisjectSurfaceContext context, NodeArchetype nodeArch, GroupArchetype groupArch);
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the given type is compatible with the given node archetype. Used for custom nodes
|
||||
/// </summary>
|
||||
public delegate bool IsCompatible(NodeArchetype nodeArch, ScriptType portType, ConnectionsHint hint);
|
||||
|
||||
/// <summary>
|
||||
/// Unique node type ID within a single group.
|
||||
/// </summary>
|
||||
@@ -99,6 +104,16 @@ namespace FlaxEditor.Surface
|
||||
/// </summary>
|
||||
public CreateCustomNodeFunc Create;
|
||||
|
||||
/// <summary>
|
||||
/// Function for asynchronously loaded nodes to check if input ports are compatible, for filtering.
|
||||
/// </summary>
|
||||
public IsCompatible IsInputCompatible;
|
||||
|
||||
/// <summary>
|
||||
/// Function for asynchronously loaded nodes to check if output ports are compatible, for filtering.
|
||||
/// </summary>
|
||||
public IsCompatible IsOutputCompatible;
|
||||
|
||||
/// <summary>
|
||||
/// Default initial size of the node.
|
||||
/// </summary>
|
||||
@@ -184,6 +199,8 @@ namespace FlaxEditor.Surface
|
||||
{
|
||||
TypeID = TypeID,
|
||||
Create = Create,
|
||||
IsInputCompatible = IsInputCompatible,
|
||||
IsOutputCompatible = IsOutputCompatible,
|
||||
Size = Size,
|
||||
Flags = Flags,
|
||||
Title = Title,
|
||||
|
||||
@@ -406,8 +406,8 @@ namespace FlaxEditor.Surface
|
||||
|
||||
internal static bool IsValidVisualScriptType(ScriptType scriptType)
|
||||
{
|
||||
if (!scriptType.IsPublic ||
|
||||
scriptType.HasAttribute(typeof(HideInEditorAttribute), true) ||
|
||||
if (!scriptType.IsPublic ||
|
||||
scriptType.HasAttribute(typeof(HideInEditorAttribute), true) ||
|
||||
scriptType.HasAttribute(typeof(System.Runtime.CompilerServices.CompilerGeneratedAttribute), false))
|
||||
return false;
|
||||
if (scriptType.IsGenericType)
|
||||
|
||||
@@ -136,6 +136,88 @@ namespace FlaxEditor.Surface
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a type is compatible with another type and can be casted by using a connection hint
|
||||
/// </summary>
|
||||
/// <param name="from">Source type</param>
|
||||
/// <param name="to">Type to check compatibility with</param>
|
||||
/// <param name="hint">Hint to check if casting is possible</param>
|
||||
/// <returns>True if the source type is compatible with the target type</returns>
|
||||
public static bool IsTypeCompatible(ScriptType from, ScriptType to, ConnectionsHint hint)
|
||||
{
|
||||
if (from == ScriptType.Null && hint != ConnectionsHint.None)
|
||||
{
|
||||
if ((hint & ConnectionsHint.Anything) == ConnectionsHint.Anything)
|
||||
return true;
|
||||
if ((hint & ConnectionsHint.Value) == ConnectionsHint.Value && to.Type != typeof(void))
|
||||
return true;
|
||||
if ((hint & ConnectionsHint.Enum) == ConnectionsHint.Enum && to.IsEnum)
|
||||
return true;
|
||||
if ((hint & ConnectionsHint.Array) == ConnectionsHint.Array && to.IsArray)
|
||||
return true;
|
||||
if ((hint & ConnectionsHint.Dictionary) == ConnectionsHint.Dictionary && to.IsDictionary)
|
||||
return true;
|
||||
if ((hint & ConnectionsHint.Vector) == ConnectionsHint.Vector)
|
||||
{
|
||||
var t = to.Type;
|
||||
if (t == typeof(Vector2) ||
|
||||
t == typeof(Vector3) ||
|
||||
t == typeof(Vector4) ||
|
||||
t == typeof(Float2) ||
|
||||
t == typeof(Float3) ||
|
||||
t == typeof(Float4) ||
|
||||
t == typeof(Double2) ||
|
||||
t == typeof(Double3) ||
|
||||
t == typeof(Double4) ||
|
||||
t == typeof(Int2) ||
|
||||
t == typeof(Int3) ||
|
||||
t == typeof(Int4) ||
|
||||
t == typeof(Color))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if ((hint & ConnectionsHint.Scalar) == ConnectionsHint.Scalar)
|
||||
{
|
||||
var t = to.Type;
|
||||
if (t == typeof(bool) ||
|
||||
t == typeof(char) ||
|
||||
t == typeof(byte) ||
|
||||
t == typeof(short) ||
|
||||
t == typeof(ushort) ||
|
||||
t == typeof(int) ||
|
||||
t == typeof(uint) ||
|
||||
t == typeof(long) ||
|
||||
t == typeof(ulong) ||
|
||||
t == typeof(float) ||
|
||||
t == typeof(double))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a type is compatible with another type and can be casted by using a connection hint
|
||||
/// </summary>
|
||||
/// <param name="from">Source type</param>
|
||||
/// <param name="to">Target type</param>
|
||||
/// <param name="hint">Connection hint</param>
|
||||
/// <returns>True if any method of casting or compatibility check succeeds</returns>
|
||||
public static bool FullCastCheck(ScriptType from, ScriptType to, ConnectionsHint hint)
|
||||
{
|
||||
// Yes, from and to are switched on purpose
|
||||
if (CanUseDirectCastStatic(to, from, false))
|
||||
return true;
|
||||
if (IsTypeCompatible(from, to, hint))
|
||||
return true;
|
||||
// Same here
|
||||
return to.CanCastTo(from);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if can use direct conversion from one type to another.
|
||||
/// </summary>
|
||||
|
||||
@@ -213,6 +213,23 @@ namespace FlaxEditor.Surface
|
||||
return;
|
||||
}
|
||||
|
||||
if (_middleMouseDown)
|
||||
{
|
||||
// Calculate delta
|
||||
var delta = location - _middleMouseDownPos;
|
||||
if (delta.LengthSquared > 0.01f)
|
||||
{
|
||||
// Move view
|
||||
_mouseMoveAmount += delta.Length;
|
||||
_rootControl.Location += delta;
|
||||
_middleMouseDownPos = location;
|
||||
Cursor = CursorType.SizeAll;
|
||||
}
|
||||
|
||||
// Handled
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if user is selecting or moving node(s)
|
||||
if (_leftMouseDown)
|
||||
{
|
||||
@@ -269,6 +286,11 @@ namespace FlaxEditor.Surface
|
||||
_rightMouseDown = false;
|
||||
Cursor = CursorType.Default;
|
||||
}
|
||||
if (_middleMouseDown)
|
||||
{
|
||||
_middleMouseDown = false;
|
||||
Cursor = CursorType.Default;
|
||||
}
|
||||
_isMovingSelection = false;
|
||||
ConnectingEnd(null);
|
||||
|
||||
@@ -291,7 +313,7 @@ namespace FlaxEditor.Surface
|
||||
if (IsMouseOver && !_leftMouseDown && !IsPrimaryMenuOpened)
|
||||
{
|
||||
var nextViewScale = ViewScale + delta * 0.1f;
|
||||
|
||||
|
||||
if (delta > 0 && !_rightMouseDown)
|
||||
{
|
||||
// Scale towards mouse when zooming in
|
||||
@@ -306,7 +328,7 @@ namespace FlaxEditor.Surface
|
||||
ViewScale = nextViewScale;
|
||||
ViewCenterPosition = viewCenter;
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -380,6 +402,7 @@ namespace FlaxEditor.Surface
|
||||
_isMovingSelection = false;
|
||||
_rightMouseDown = false;
|
||||
_leftMouseDown = false;
|
||||
_middleMouseDown = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -399,6 +422,11 @@ namespace FlaxEditor.Surface
|
||||
_rightMouseDown = true;
|
||||
_rightMouseDownPos = location;
|
||||
}
|
||||
if (button == MouseButton.Middle)
|
||||
{
|
||||
_middleMouseDown = true;
|
||||
_middleMouseDownPos = location;
|
||||
}
|
||||
|
||||
// Check if any node is under the mouse
|
||||
SurfaceControl controlUnderMouse = GetControlUnderMouse();
|
||||
@@ -444,7 +472,7 @@ namespace FlaxEditor.Surface
|
||||
Focus();
|
||||
return true;
|
||||
}
|
||||
if (_rightMouseDown)
|
||||
if (_rightMouseDown || _middleMouseDown)
|
||||
{
|
||||
// Start navigating
|
||||
StartMouseCapture();
|
||||
@@ -513,6 +541,13 @@ namespace FlaxEditor.Surface
|
||||
}
|
||||
_mouseMoveAmount = 0;
|
||||
}
|
||||
if (_middleMouseDown && button == MouseButton.Middle)
|
||||
{
|
||||
_middleMouseDown = false;
|
||||
EndMouseCapture();
|
||||
Cursor = CursorType.Default;
|
||||
_mouseMoveAmount = 0;
|
||||
}
|
||||
|
||||
// Base
|
||||
bool handled = base.OnMouseUp(location, button);
|
||||
@@ -523,6 +558,7 @@ namespace FlaxEditor.Surface
|
||||
// Clear flags
|
||||
_rightMouseDown = false;
|
||||
_leftMouseDown = false;
|
||||
_middleMouseDown = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -706,6 +742,8 @@ namespace FlaxEditor.Surface
|
||||
{
|
||||
if (_inputBrackets.Count == 0)
|
||||
{
|
||||
if (currentInputText.StartsWith(' '))
|
||||
currentInputText = "";
|
||||
ResetInput();
|
||||
ShowPrimaryMenu(_mousePos, false, currentInputText);
|
||||
}
|
||||
|
||||
@@ -59,6 +59,11 @@ namespace FlaxEditor.Surface
|
||||
/// </summary>
|
||||
protected bool _rightMouseDown;
|
||||
|
||||
/// <summary>
|
||||
/// The middle mouse down flag.
|
||||
/// </summary>
|
||||
protected bool _middleMouseDown;
|
||||
|
||||
/// <summary>
|
||||
/// The left mouse down position.
|
||||
/// </summary>
|
||||
@@ -69,6 +74,11 @@ namespace FlaxEditor.Surface
|
||||
/// </summary>
|
||||
protected Float2 _rightMouseDownPos = Float2.Minimum;
|
||||
|
||||
/// <summary>
|
||||
/// The middle mouse down position.
|
||||
/// </summary>
|
||||
protected Float2 _middleMouseDownPos = Float2.Minimum;
|
||||
|
||||
/// <summary>
|
||||
/// The mouse position.
|
||||
/// </summary>
|
||||
@@ -902,7 +912,7 @@ namespace FlaxEditor.Surface
|
||||
{
|
||||
return _context.FindNode(id);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Adds the undo action to be batched (eg. if multiple undo actions is performed in a sequence during single update).
|
||||
/// </summary>
|
||||
|
||||
@@ -137,14 +137,23 @@ namespace FlaxEditor.Tools.Foliage
|
||||
Offsets = Margin.Zero,
|
||||
Parent = _noFoliagePanel
|
||||
};
|
||||
|
||||
var buttonText = "Create new foliage";
|
||||
_createNewFoliage = new Button
|
||||
{
|
||||
Text = "Create new foliage",
|
||||
Text = buttonText,
|
||||
AnchorPreset = AnchorPresets.MiddleCenter,
|
||||
Offsets = new Margin(-60, 120, -12, 24),
|
||||
Parent = _noFoliagePanel,
|
||||
Enabled = false
|
||||
};
|
||||
var textSize = Style.Current.FontMedium.MeasureText(buttonText);
|
||||
if (_createNewFoliage.Width < textSize.X)
|
||||
{
|
||||
_createNewFoliage.LocalX -= (textSize.X - _createNewFoliage.Width) / 2;
|
||||
_createNewFoliage.Width = textSize.X + 6;
|
||||
}
|
||||
|
||||
_createNewFoliage.Clicked += OnCreateNewFoliageClicked;
|
||||
}
|
||||
|
||||
|
||||
@@ -375,7 +375,7 @@ namespace FlaxEditor.Tools.Foliage
|
||||
|
||||
private void OnModified()
|
||||
{
|
||||
Editor.Instance.Scene.MarkSceneEdited(_proxy.Foliage?.Scene);
|
||||
Editor.Instance.Scene.MarkSceneEdited(_proxy.Foliage != null ? _proxy.Foliage.Scene : null);
|
||||
}
|
||||
|
||||
private void OnSelectedFoliageChanged()
|
||||
|
||||
@@ -218,7 +218,7 @@ namespace FlaxEditor.Tools.Foliage
|
||||
|
||||
private void OnModified()
|
||||
{
|
||||
Editor.Instance.Scene.MarkSceneEdited(_proxy.Foliage?.Scene);
|
||||
Editor.Instance.Scene.MarkSceneEdited(_proxy.Foliage != null ? _proxy.Foliage.Scene : null);
|
||||
}
|
||||
|
||||
private void OnSelectedFoliageChanged()
|
||||
|
||||
@@ -95,14 +95,23 @@ namespace FlaxEditor.Tools.Terrain
|
||||
Offsets = Margin.Zero,
|
||||
Parent = _noTerrainPanel
|
||||
};
|
||||
|
||||
var buttonText = "Create new terrain";
|
||||
_createTerrainButton = new Button
|
||||
{
|
||||
Text = "Create new terrain",
|
||||
Text = buttonText,
|
||||
AnchorPreset = AnchorPresets.MiddleCenter,
|
||||
Offsets = new Margin(-60, 120, -12, 24),
|
||||
Parent = _noTerrainPanel,
|
||||
Enabled = false
|
||||
};
|
||||
var textSize = Style.Current.FontMedium.MeasureText(buttonText);
|
||||
if (_createTerrainButton.Width < textSize.X)
|
||||
{
|
||||
_createTerrainButton.LocalX -= (textSize.X - _createTerrainButton.Width) / 2;
|
||||
_createTerrainButton.Width = textSize.X + 6;
|
||||
}
|
||||
|
||||
_createTerrainButton.Clicked += OnCreateNewTerrainClicked;
|
||||
}
|
||||
|
||||
|
||||
@@ -544,7 +544,7 @@ namespace FlaxEditor.Tools
|
||||
public override bool IsControllingMouse => IsPainting;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override BoundingSphere FocusBounds => _selectedModel?.Sphere ?? base.FocusBounds;
|
||||
public override BoundingSphere FocusBounds => _selectedModel != null ? _selectedModel.Sphere : base.FocusBounds;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Update(float dt)
|
||||
|
||||
@@ -144,7 +144,7 @@ namespace FlaxEditor.Actions
|
||||
{
|
||||
var node = nodeParents[i];
|
||||
var actor = node.Actor;
|
||||
var parent = actor?.Parent;
|
||||
var parent = actor != null ? actor.Parent : null;
|
||||
if (parent != null)
|
||||
{
|
||||
bool IsNameValid(string name)
|
||||
|
||||
@@ -14,5 +14,11 @@ namespace FlaxEditor.Utilities
|
||||
public const string FacebookUrl = "https://facebook.com/FlaxEngine";
|
||||
public const string YoutubeUrl = "https://youtube.com/c/FlaxEngine";
|
||||
public const string TwitterUrl = "https://twitter.com/FlaxEngine";
|
||||
|
||||
#if PLATFORM_MAC
|
||||
public const string ShowInExplorer = "Show in Finder";
|
||||
#else
|
||||
public const string ShowInExplorer = "Show in explorer";
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,10 +18,10 @@ using FlaxEditor.GUI.ContextMenu;
|
||||
using FlaxEditor.GUI.Input;
|
||||
using FlaxEditor.GUI.Tree;
|
||||
using FlaxEditor.SceneGraph;
|
||||
using FlaxEditor.Scripting;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
using FlaxEngine.Utilities;
|
||||
using FlaxEditor.Windows;
|
||||
|
||||
namespace FlaxEngine
|
||||
{
|
||||
@@ -1235,5 +1235,60 @@ namespace FlaxEditor.Utilities
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Binds global input actions for the window.
|
||||
/// </summary>
|
||||
/// <param name="window">The editor window.</param>
|
||||
public static void SetupCommonInputActions(EditorWindow window)
|
||||
{
|
||||
var inputActions = window.InputActions;
|
||||
|
||||
// Setup input actions
|
||||
inputActions.Add(options => options.Save, Editor.Instance.SaveAll);
|
||||
inputActions.Add(options => options.Undo, () =>
|
||||
{
|
||||
Editor.Instance.PerformUndo();
|
||||
window.Focus();
|
||||
});
|
||||
inputActions.Add(options => options.Redo, () =>
|
||||
{
|
||||
Editor.Instance.PerformRedo();
|
||||
window.Focus();
|
||||
});
|
||||
inputActions.Add(options => options.Cut, Editor.Instance.SceneEditing.Cut);
|
||||
inputActions.Add(options => options.Copy, Editor.Instance.SceneEditing.Copy);
|
||||
inputActions.Add(options => options.Paste, Editor.Instance.SceneEditing.Paste);
|
||||
inputActions.Add(options => options.Duplicate, Editor.Instance.SceneEditing.Duplicate);
|
||||
inputActions.Add(options => options.SelectAll, Editor.Instance.SceneEditing.SelectAllScenes);
|
||||
inputActions.Add(options => options.Delete, Editor.Instance.SceneEditing.Delete);
|
||||
inputActions.Add(options => options.Search, () => Editor.Instance.Windows.SceneWin.Search());
|
||||
inputActions.Add(options => options.MoveActorToViewport, Editor.Instance.UI.MoveActorToViewport);
|
||||
inputActions.Add(options => options.AlignActorWithViewport, Editor.Instance.UI.AlignActorWithViewport);
|
||||
inputActions.Add(options => options.AlignViewportWithActor, Editor.Instance.UI.AlignViewportWithActor);
|
||||
inputActions.Add(options => options.PilotActor, Editor.Instance.UI.PilotActor);
|
||||
inputActions.Add(options => options.Play, Editor.Instance.Simulation.DelegatePlayOrStopPlayInEditor);
|
||||
inputActions.Add(options => options.PlayCurrentScenes, Editor.Instance.Simulation.RequestPlayScenesOrStopPlay);
|
||||
inputActions.Add(options => options.Pause, Editor.Instance.Simulation.RequestResumeOrPause);
|
||||
inputActions.Add(options => options.StepFrame, Editor.Instance.Simulation.RequestPlayOneFrame);
|
||||
inputActions.Add(options => options.CookAndRun, () => Editor.Instance.Windows.GameCookerWin.BuildAndRun());
|
||||
inputActions.Add(options => options.RunCookedGame, () => Editor.Instance.Windows.GameCookerWin.RunCooked());
|
||||
inputActions.Add(options => options.BuildScenesData, Editor.Instance.BuildScenesOrCancel);
|
||||
inputActions.Add(options => options.BakeLightmaps, Editor.Instance.BakeLightmapsOrCancel);
|
||||
inputActions.Add(options => options.ClearLightmaps, Editor.Instance.ClearLightmaps);
|
||||
inputActions.Add(options => options.BakeEnvProbes, Editor.Instance.BakeAllEnvProbes);
|
||||
inputActions.Add(options => options.BuildCSG, Editor.Instance.BuildCSG);
|
||||
inputActions.Add(options => options.BuildNav, Editor.Instance.BuildNavMesh);
|
||||
inputActions.Add(options => options.BuildSDF, Editor.Instance.BuildAllMeshesSDF);
|
||||
inputActions.Add(options => options.TakeScreenshot, Editor.Instance.Windows.TakeScreenshot);
|
||||
inputActions.Add(options => options.ProfilerWindow, () => Editor.Instance.Windows.ProfilerWin.FocusOrShow());
|
||||
inputActions.Add(options => options.ProfilerStartStop, () => { Editor.Instance.Windows.ProfilerWin.LiveRecording = !Editor.Instance.Windows.ProfilerWin.LiveRecording; Editor.Instance.UI.AddStatusMessage($"Profiling {(Editor.Instance.Windows.ProfilerWin.LiveRecording ? "started" : "stopped")}."); });
|
||||
inputActions.Add(options => options.ProfilerClear, () => { Editor.Instance.Windows.ProfilerWin.Clear(); Editor.Instance.UI.AddStatusMessage($"Profiling results cleared."); });
|
||||
inputActions.Add(options => options.SaveScenes, () => Editor.Instance.Scene.SaveScenes());
|
||||
inputActions.Add(options => options.CloseScenes, () => Editor.Instance.Scene.CloseAllScenes());
|
||||
inputActions.Add(options => options.OpenScriptsProject, () => Editor.Instance.CodeEditing.OpenSolution());
|
||||
inputActions.Add(options => options.GenerateScriptsProject, () => Editor.Instance.ProgressReporting.GenerateScriptsProjectFiles.RunAsync());
|
||||
inputActions.Add(options => options.RecompileScripts, ScriptsBuilder.Compile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
{
|
||||
@@ -137,7 +140,7 @@ namespace FlaxEditor.Viewport
|
||||
|
||||
// Input
|
||||
|
||||
private bool _isControllingMouse, _isViewportControllingMouse;
|
||||
private bool _isControllingMouse, _isViewportControllingMouse, _wasVirtualMouseRightDown, _isVirtualMouseRightDown;
|
||||
private int _deltaFilteringStep;
|
||||
private Float2 _startPos;
|
||||
private Float2 _mouseDeltaLast;
|
||||
@@ -441,6 +444,9 @@ namespace FlaxEditor.Viewport
|
||||
|
||||
if (useWidgets)
|
||||
{
|
||||
var largestText = "Invert Panning";
|
||||
var textSize = Style.Current.FontMedium.MeasureText(largestText);
|
||||
var xLocationForExtras = textSize.X + 5;
|
||||
// Camera speed widget
|
||||
var camSpeed = new ViewportWidgetsContainer(ViewportWidgetLocation.UpperRight);
|
||||
var camSpeedCM = new ContextMenu();
|
||||
@@ -483,10 +489,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<LayersMask>(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<ViewFlags>(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++)
|
||||
{
|
||||
@@ -501,7 +560,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;
|
||||
@@ -510,6 +569,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<ViewMode>(Clipboard.Text);
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
});
|
||||
debugView.AddSeparator();
|
||||
for (int i = 0; i < EditorViewportViewModeValues.Length; i++)
|
||||
{
|
||||
ref var v = ref EditorViewportViewModeValues[i];
|
||||
@@ -541,7 +612,7 @@ namespace FlaxEditor.Viewport
|
||||
{
|
||||
var ortho = ViewWidgetButtonMenu.AddButton("Orthographic");
|
||||
ortho.CloseMenuOnClick = false;
|
||||
var orthoValue = new CheckBox(90, 2, _isOrtho)
|
||||
var orthoValue = new CheckBox(xLocationForExtras, 2, _isOrtho)
|
||||
{
|
||||
Parent = ortho
|
||||
};
|
||||
@@ -581,7 +652,7 @@ namespace FlaxEditor.Viewport
|
||||
{
|
||||
var fov = ViewWidgetButtonMenu.AddButton("Field Of View");
|
||||
fov.CloseMenuOnClick = false;
|
||||
var fovValue = new FloatValueBox(1, 90, 2, 70.0f, 35.0f, 160.0f, 0.1f)
|
||||
var fovValue = new FloatValueBox(1, xLocationForExtras, 2, 70.0f, 35.0f, 160.0f, 0.1f)
|
||||
{
|
||||
Parent = fov
|
||||
};
|
||||
@@ -598,7 +669,7 @@ namespace FlaxEditor.Viewport
|
||||
{
|
||||
var orthoSize = ViewWidgetButtonMenu.AddButton("Ortho Scale");
|
||||
orthoSize.CloseMenuOnClick = false;
|
||||
var orthoSizeValue = new FloatValueBox(_orthoSize, 90, 2, 70.0f, 0.001f, 100000.0f, 0.01f)
|
||||
var orthoSizeValue = new FloatValueBox(_orthoSize, xLocationForExtras, 2, 70.0f, 0.001f, 100000.0f, 0.01f)
|
||||
{
|
||||
Parent = orthoSize
|
||||
};
|
||||
@@ -615,7 +686,7 @@ namespace FlaxEditor.Viewport
|
||||
{
|
||||
var nearPlane = ViewWidgetButtonMenu.AddButton("Near Plane");
|
||||
nearPlane.CloseMenuOnClick = false;
|
||||
var nearPlaneValue = new FloatValueBox(2.0f, 90, 2, 70.0f, 0.001f, 1000.0f)
|
||||
var nearPlaneValue = new FloatValueBox(2.0f, xLocationForExtras, 2, 70.0f, 0.001f, 1000.0f)
|
||||
{
|
||||
Parent = nearPlane
|
||||
};
|
||||
@@ -627,7 +698,7 @@ namespace FlaxEditor.Viewport
|
||||
{
|
||||
var farPlane = ViewWidgetButtonMenu.AddButton("Far Plane");
|
||||
farPlane.CloseMenuOnClick = false;
|
||||
var farPlaneValue = new FloatValueBox(1000, 90, 2, 70.0f, 10.0f)
|
||||
var farPlaneValue = new FloatValueBox(1000, xLocationForExtras, 2, 70.0f, 10.0f)
|
||||
{
|
||||
Parent = farPlane
|
||||
};
|
||||
@@ -639,7 +710,7 @@ namespace FlaxEditor.Viewport
|
||||
{
|
||||
var brightness = ViewWidgetButtonMenu.AddButton("Brightness");
|
||||
brightness.CloseMenuOnClick = false;
|
||||
var brightnessValue = new FloatValueBox(1.0f, 90, 2, 70.0f, 0.001f, 10.0f, 0.001f)
|
||||
var brightnessValue = new FloatValueBox(1.0f, xLocationForExtras, 2, 70.0f, 0.001f, 10.0f, 0.001f)
|
||||
{
|
||||
Parent = brightness
|
||||
};
|
||||
@@ -651,7 +722,7 @@ namespace FlaxEditor.Viewport
|
||||
{
|
||||
var resolution = ViewWidgetButtonMenu.AddButton("Resolution");
|
||||
resolution.CloseMenuOnClick = false;
|
||||
var resolutionValue = new FloatValueBox(1.0f, 90, 2, 70.0f, 0.1f, 4.0f, 0.001f)
|
||||
var resolutionValue = new FloatValueBox(1.0f, xLocationForExtras, 2, 70.0f, 0.1f, 4.0f, 0.001f)
|
||||
{
|
||||
Parent = resolution
|
||||
};
|
||||
@@ -663,7 +734,7 @@ namespace FlaxEditor.Viewport
|
||||
{
|
||||
var invert = ViewWidgetButtonMenu.AddButton("Invert Panning");
|
||||
invert.CloseMenuOnClick = false;
|
||||
var invertValue = new CheckBox(90, 2, _invertPanning)
|
||||
var invertValue = new CheckBox(xLocationForExtras, 2, _invertPanning)
|
||||
{
|
||||
Parent = invert
|
||||
};
|
||||
@@ -685,6 +756,9 @@ namespace FlaxEditor.Viewport
|
||||
InputActions.Add(options => options.ViewpointBack, () => OrientViewport(Quaternion.Euler(EditorViewportCameraViewpointValues.First(vp => vp.Name == "Back").Orientation)));
|
||||
InputActions.Add(options => options.ViewpointRight, () => OrientViewport(Quaternion.Euler(EditorViewportCameraViewpointValues.First(vp => vp.Name == "Right").Orientation)));
|
||||
InputActions.Add(options => options.ViewpointLeft, () => OrientViewport(Quaternion.Euler(EditorViewportCameraViewpointValues.First(vp => vp.Name == "Left").Orientation)));
|
||||
InputActions.Add(options => options.CameraToggleRotation, () => _isVirtualMouseRightDown = !_isVirtualMouseRightDown);
|
||||
InputActions.Add(options => options.CameraIncreaseMoveSpeed, () => AdjustCameraMoveSpeed(1));
|
||||
InputActions.Add(options => options.CameraDecreaseMoveSpeed, () => AdjustCameraMoveSpeed(-1));
|
||||
|
||||
// Link for task event
|
||||
task.Begin += OnRenderBegin;
|
||||
@@ -722,6 +796,30 @@ namespace FlaxEditor.Viewport
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Increases or decreases the camera movement speed.
|
||||
/// </summary>
|
||||
/// <param name="step">The stepping direction for speed adjustment.</param>
|
||||
protected void AdjustCameraMoveSpeed(int step)
|
||||
{
|
||||
int camValueIndex = -1;
|
||||
for (int i = 0; i < EditorViewportCameraSpeedValues.Length; i++)
|
||||
{
|
||||
if (Mathf.NearEqual(EditorViewportCameraSpeedValues[i], _movementSpeed))
|
||||
{
|
||||
camValueIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (camValueIndex == -1)
|
||||
return;
|
||||
|
||||
if (step > 0)
|
||||
MovementSpeed = EditorViewportCameraSpeedValues[Mathf.Min(camValueIndex + 1, EditorViewportCameraSpeedValues.Length - 1)];
|
||||
else if (step < 0)
|
||||
MovementSpeed = EditorViewportCameraSpeedValues[Mathf.Max(camValueIndex - 1, 0)];
|
||||
}
|
||||
|
||||
private void OnEditorOptionsChanged(EditorOptions options)
|
||||
{
|
||||
_mouseSensitivity = options.Viewport.MouseSensitivity;
|
||||
@@ -1048,6 +1146,15 @@ namespace FlaxEditor.Viewport
|
||||
// Track controlling mouse state change
|
||||
bool wasControllingMouse = _prevInput.IsControllingMouse;
|
||||
_isControllingMouse = _input.IsControllingMouse;
|
||||
|
||||
// Simulate holding mouse right down for trackpad users
|
||||
if ((_prevInput.IsMouseRightDown && !_input.IsMouseRightDown) || win.GetKeyDown(KeyboardKeys.Escape))
|
||||
_isVirtualMouseRightDown = false; // Cancel when mouse right or escape is pressed
|
||||
if (_wasVirtualMouseRightDown)
|
||||
wasControllingMouse = true;
|
||||
if (_isVirtualMouseRightDown)
|
||||
_isControllingMouse = _isVirtualMouseRightDown;
|
||||
|
||||
if (wasControllingMouse != _isControllingMouse)
|
||||
{
|
||||
if (_isControllingMouse)
|
||||
@@ -1061,16 +1168,18 @@ namespace FlaxEditor.Viewport
|
||||
OnLeftMouseButtonDown();
|
||||
else if (_prevInput.IsMouseLeftDown && !_input.IsMouseLeftDown)
|
||||
OnLeftMouseButtonUp();
|
||||
//
|
||||
if (!_prevInput.IsMouseRightDown && _input.IsMouseRightDown)
|
||||
|
||||
if ((!_prevInput.IsMouseRightDown && _input.IsMouseRightDown) || (!_wasVirtualMouseRightDown && _isVirtualMouseRightDown))
|
||||
OnRightMouseButtonDown();
|
||||
else if (_prevInput.IsMouseRightDown && !_input.IsMouseRightDown)
|
||||
else if ((_prevInput.IsMouseRightDown && !_input.IsMouseRightDown) || (_wasVirtualMouseRightDown && !_isVirtualMouseRightDown))
|
||||
OnRightMouseButtonUp();
|
||||
//
|
||||
|
||||
if (!_prevInput.IsMouseMiddleDown && _input.IsMouseMiddleDown)
|
||||
OnMiddleMouseButtonDown();
|
||||
else if (_prevInput.IsMouseMiddleDown && !_input.IsMouseMiddleDown)
|
||||
OnMiddleMouseButtonUp();
|
||||
|
||||
_wasVirtualMouseRightDown = _isVirtualMouseRightDown;
|
||||
}
|
||||
|
||||
// Get clamped delta time (more stable during lags)
|
||||
@@ -1088,7 +1197,7 @@ namespace FlaxEditor.Viewport
|
||||
bool isAltDown = _input.IsAltDown;
|
||||
bool lbDown = _input.IsMouseLeftDown;
|
||||
bool mbDown = _input.IsMouseMiddleDown;
|
||||
bool rbDown = _input.IsMouseRightDown;
|
||||
bool rbDown = _input.IsMouseRightDown || _isVirtualMouseRightDown;
|
||||
bool wheelInUse = Math.Abs(_input.MouseWheelDelta) > Mathf.Epsilon;
|
||||
|
||||
_input.IsPanning = !isAltDown && mbDown && !rbDown;
|
||||
@@ -1098,32 +1207,20 @@ namespace FlaxEditor.Viewport
|
||||
_input.IsOrbiting = isAltDown && lbDown && !mbDown && !rbDown;
|
||||
|
||||
// Control move speed with RMB+Wheel
|
||||
rmbWheel = useMovementSpeed && _input.IsMouseRightDown && wheelInUse;
|
||||
rmbWheel = useMovementSpeed && (_input.IsMouseRightDown || _isVirtualMouseRightDown) && wheelInUse;
|
||||
if (rmbWheel)
|
||||
{
|
||||
float step = 4.0f;
|
||||
const float step = 4.0f;
|
||||
_wheelMovementChangeDeltaSum += _input.MouseWheelDelta * options.Viewport.MouseWheelSensitivity;
|
||||
int camValueIndex = -1;
|
||||
for (int i = 0; i < EditorViewportCameraSpeedValues.Length; i++)
|
||||
if (_wheelMovementChangeDeltaSum >= step)
|
||||
{
|
||||
if (Mathf.NearEqual(EditorViewportCameraSpeedValues[i], _movementSpeed))
|
||||
{
|
||||
camValueIndex = i;
|
||||
break;
|
||||
}
|
||||
_wheelMovementChangeDeltaSum -= step;
|
||||
AdjustCameraMoveSpeed(1);
|
||||
}
|
||||
if (camValueIndex != -1)
|
||||
else if (_wheelMovementChangeDeltaSum <= -step)
|
||||
{
|
||||
if (_wheelMovementChangeDeltaSum >= step)
|
||||
{
|
||||
_wheelMovementChangeDeltaSum -= step;
|
||||
MovementSpeed = EditorViewportCameraSpeedValues[Mathf.Min(camValueIndex + 1, EditorViewportCameraSpeedValues.Length - 1)];
|
||||
}
|
||||
else if (_wheelMovementChangeDeltaSum <= -step)
|
||||
{
|
||||
_wheelMovementChangeDeltaSum += step;
|
||||
MovementSpeed = EditorViewportCameraSpeedValues[Mathf.Max(camValueIndex - 1, 0)];
|
||||
}
|
||||
_wheelMovementChangeDeltaSum += step;
|
||||
AdjustCameraMoveSpeed(-1);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1165,7 +1262,7 @@ namespace FlaxEditor.Viewport
|
||||
|
||||
// Calculate smooth mouse delta not dependant on viewport size
|
||||
var offset = _viewMousePos - _startPos;
|
||||
if (_input.IsZooming && !_input.IsMouseRightDown && !_input.IsMouseLeftDown && !_input.IsMouseMiddleDown && !_isOrtho && !rmbWheel)
|
||||
if (_input.IsZooming && !_input.IsMouseRightDown && !_input.IsMouseLeftDown && !_input.IsMouseMiddleDown && !_isOrtho && !rmbWheel && !_isVirtualMouseRightDown)
|
||||
{
|
||||
offset = Float2.Zero;
|
||||
}
|
||||
@@ -1213,7 +1310,7 @@ namespace FlaxEditor.Viewport
|
||||
UpdateView(dt, ref moveDelta, ref mouseDelta, out var centerMouse);
|
||||
|
||||
// Move mouse back to the root position
|
||||
if (centerMouse && (_input.IsMouseRightDown || _input.IsMouseLeftDown || _input.IsMouseMiddleDown))
|
||||
if (centerMouse && (_input.IsMouseRightDown || _input.IsMouseLeftDown || _input.IsMouseMiddleDown || _isVirtualMouseRightDown))
|
||||
{
|
||||
var center = PointToWindow(_startPos);
|
||||
win.MousePosition = center;
|
||||
@@ -1229,7 +1326,7 @@ namespace FlaxEditor.Viewport
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_input.IsMouseLeftDown || _input.IsMouseRightDown)
|
||||
if (_input.IsMouseLeftDown || _input.IsMouseRightDown || _isVirtualMouseRightDown)
|
||||
{
|
||||
// Calculate smooth mouse delta not dependant on viewport size
|
||||
var offset = _viewMousePos - _startPos;
|
||||
@@ -1359,6 +1456,7 @@ namespace FlaxEditor.Viewport
|
||||
{
|
||||
OnControlMouseEnd(RootWindow.Window);
|
||||
_isControllingMouse = false;
|
||||
_isVirtualMouseRightDown = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1561,6 +1659,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);
|
||||
|
||||
@@ -194,6 +194,7 @@ namespace FlaxEditor.Viewport
|
||||
{
|
||||
_editor = editor;
|
||||
_dragAssets = new DragAssets<DragDropEventArgs>(ValidateDragItem);
|
||||
var inputOptions = editor.Options.Options.Input;
|
||||
|
||||
// Prepare rendering task
|
||||
Task.ActorsSource = ActorsSources.Scenes;
|
||||
@@ -250,7 +251,7 @@ namespace FlaxEditor.Viewport
|
||||
var transformSpaceToggle = new ViewportWidgetButton(string.Empty, editor.Icons.Globe32, null, true)
|
||||
{
|
||||
Checked = TransformGizmo.ActiveTransformSpace == TransformGizmoBase.TransformSpace.World,
|
||||
TooltipText = "Gizmo transform space (world or local)",
|
||||
TooltipText = $"Gizmo transform space (world or local) ({inputOptions.ToggleTransformSpace})",
|
||||
Parent = transformSpaceWidget
|
||||
};
|
||||
transformSpaceToggle.Toggled += OnTransformSpaceToggle;
|
||||
@@ -347,7 +348,7 @@ namespace FlaxEditor.Viewport
|
||||
_gizmoModeTranslate = new ViewportWidgetButton(string.Empty, editor.Icons.Translate32, null, true)
|
||||
{
|
||||
Tag = TransformGizmoBase.Mode.Translate,
|
||||
TooltipText = "Translate gizmo mode",
|
||||
TooltipText = $"Translate gizmo mode ({inputOptions.TranslateMode})",
|
||||
Checked = true,
|
||||
Parent = gizmoMode
|
||||
};
|
||||
@@ -355,14 +356,14 @@ namespace FlaxEditor.Viewport
|
||||
_gizmoModeRotate = new ViewportWidgetButton(string.Empty, editor.Icons.Rotate32, null, true)
|
||||
{
|
||||
Tag = TransformGizmoBase.Mode.Rotate,
|
||||
TooltipText = "Rotate gizmo mode",
|
||||
TooltipText = $"Rotate gizmo mode ({inputOptions.RotateMode})",
|
||||
Parent = gizmoMode
|
||||
};
|
||||
_gizmoModeRotate.Toggled += OnGizmoModeToggle;
|
||||
_gizmoModeScale = new ViewportWidgetButton(string.Empty, editor.Icons.Scale32, null, true)
|
||||
{
|
||||
Tag = TransformGizmoBase.Mode.Scale,
|
||||
TooltipText = "Scale gizmo mode",
|
||||
TooltipText = $"Scale gizmo mode ({inputOptions.ScaleMode})",
|
||||
Parent = gizmoMode
|
||||
};
|
||||
_gizmoModeScale.Toggled += OnGizmoModeToggle;
|
||||
@@ -390,6 +391,7 @@ namespace FlaxEditor.Viewport
|
||||
InputActions.Add(options => options.TranslateMode, () => TransformGizmo.ActiveMode = TransformGizmoBase.Mode.Translate);
|
||||
InputActions.Add(options => options.RotateMode, () => TransformGizmo.ActiveMode = TransformGizmoBase.Mode.Rotate);
|
||||
InputActions.Add(options => options.ScaleMode, () => TransformGizmo.ActiveMode = TransformGizmoBase.Mode.Scale);
|
||||
InputActions.Add(options => options.ToggleTransformSpace, () => { OnTransformSpaceToggle(transformSpaceToggle); transformSpaceToggle.Checked = !transformSpaceToggle.Checked; });
|
||||
InputActions.Add(options => options.LockFocusSelection, LockFocusSelection);
|
||||
InputActions.Add(options => options.FocusSelection, FocusSelection);
|
||||
InputActions.Add(options => options.RotateSelection, RotateSelection);
|
||||
|
||||
@@ -84,6 +84,7 @@ namespace FlaxEditor.Viewport
|
||||
_dragAssets = new DragAssets(ValidateDragItem);
|
||||
ShowDebugDraw = true;
|
||||
ShowEditorPrimitives = true;
|
||||
var inputOptions = window.Editor.Options.Options.Input;
|
||||
|
||||
// Prepare rendering task
|
||||
Task.ActorsSource = ActorsSources.CustomActors;
|
||||
@@ -113,7 +114,7 @@ namespace FlaxEditor.Viewport
|
||||
var transformSpaceToggle = new ViewportWidgetButton(string.Empty, window.Editor.Icons.Globe32, null, true)
|
||||
{
|
||||
Checked = TransformGizmo.ActiveTransformSpace == TransformGizmoBase.TransformSpace.World,
|
||||
TooltipText = "Gizmo transform space (world or local)",
|
||||
TooltipText = $"Gizmo transform space (world or local) ({inputOptions.ToggleTransformSpace})",
|
||||
Parent = transformSpaceWidget
|
||||
};
|
||||
transformSpaceToggle.Toggled += OnTransformSpaceToggle;
|
||||
@@ -205,7 +206,7 @@ namespace FlaxEditor.Viewport
|
||||
_gizmoModeTranslate = new ViewportWidgetButton(string.Empty, window.Editor.Icons.Translate32, null, true)
|
||||
{
|
||||
Tag = TransformGizmoBase.Mode.Translate,
|
||||
TooltipText = "Translate gizmo mode",
|
||||
TooltipText = $"Translate gizmo mode ({inputOptions.TranslateMode})",
|
||||
Checked = true,
|
||||
Parent = gizmoMode
|
||||
};
|
||||
@@ -213,14 +214,14 @@ namespace FlaxEditor.Viewport
|
||||
_gizmoModeRotate = new ViewportWidgetButton(string.Empty, window.Editor.Icons.Rotate32, null, true)
|
||||
{
|
||||
Tag = TransformGizmoBase.Mode.Rotate,
|
||||
TooltipText = "Rotate gizmo mode",
|
||||
TooltipText = $"Rotate gizmo mode ({inputOptions.RotateMode})",
|
||||
Parent = gizmoMode
|
||||
};
|
||||
_gizmoModeRotate.Toggled += OnGizmoModeToggle;
|
||||
_gizmoModeScale = new ViewportWidgetButton(string.Empty, window.Editor.Icons.Scale32, null, true)
|
||||
{
|
||||
Tag = TransformGizmoBase.Mode.Scale,
|
||||
TooltipText = "Scale gizmo mode",
|
||||
TooltipText = $"Scale gizmo mode ({inputOptions.ScaleMode})",
|
||||
Parent = gizmoMode
|
||||
};
|
||||
_gizmoModeScale.Toggled += OnGizmoModeToggle;
|
||||
@@ -233,6 +234,7 @@ namespace FlaxEditor.Viewport
|
||||
InputActions.Add(options => options.TranslateMode, () => TransformGizmo.ActiveMode = TransformGizmoBase.Mode.Translate);
|
||||
InputActions.Add(options => options.RotateMode, () => TransformGizmo.ActiveMode = TransformGizmoBase.Mode.Rotate);
|
||||
InputActions.Add(options => options.ScaleMode, () => TransformGizmo.ActiveMode = TransformGizmoBase.Mode.Scale);
|
||||
InputActions.Add(options => options.ToggleTransformSpace, () => { OnTransformSpaceToggle(transformSpaceToggle); transformSpaceToggle.Checked = !transformSpaceToggle.Checked; });
|
||||
InputActions.Add(options => options.FocusSelection, ShowSelectedActors);
|
||||
|
||||
SetUpdate(ref _update, OnUpdate);
|
||||
|
||||
@@ -68,7 +68,7 @@ namespace FlaxEditor.Viewport.Previews
|
||||
/// </summary>
|
||||
public bool ShowBounds
|
||||
{
|
||||
get => _boundsModel?.IsActive ?? false;
|
||||
get => _boundsModel != null ? _boundsModel.IsActive : false;
|
||||
set
|
||||
{
|
||||
if (value == ShowBounds)
|
||||
@@ -110,7 +110,7 @@ namespace FlaxEditor.Viewport.Previews
|
||||
/// </summary>
|
||||
public bool ShowOrigin
|
||||
{
|
||||
get => _originModel?.IsActive ?? false;
|
||||
get => _originModel != null ? _originModel.IsActive : false;
|
||||
set
|
||||
{
|
||||
if (value == ShowOrigin)
|
||||
|
||||
@@ -500,7 +500,7 @@ namespace FlaxEditor.Viewport.Previews
|
||||
/// <inheritdoc />
|
||||
protected override void CalculateTextureRect(out Rectangle rect)
|
||||
{
|
||||
CalculateTextureRect(_asset?.Size ?? new Float2(100), Size, out rect);
|
||||
CalculateTextureRect(_asset != null ? _asset.Size : new Float2(100), Size, out rect);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -549,7 +549,7 @@ namespace FlaxEditor.Viewport.Previews
|
||||
/// <inheritdoc />
|
||||
protected override void CalculateTextureRect(out Rectangle rect)
|
||||
{
|
||||
CalculateTextureRect(_asset?.Size ?? new Float2(100), Size, out rect);
|
||||
CalculateTextureRect(_asset != null ? _asset.Size : new Float2(100), Size, out rect);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -604,7 +604,7 @@ namespace FlaxEditor.Viewport.Previews
|
||||
/// <inheritdoc />
|
||||
protected override void CalculateTextureRect(out Rectangle rect)
|
||||
{
|
||||
CalculateTextureRect(_asset?.Size ?? new Float2(100), Size, out rect);
|
||||
CalculateTextureRect(_asset != null ? _asset.Size : new Float2(100), Size, out rect);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -659,7 +659,7 @@ namespace FlaxEditor.Viewport.Previews
|
||||
/// <inheritdoc />
|
||||
protected override void CalculateTextureRect(out Rectangle rect)
|
||||
{
|
||||
CalculateTextureRect(_asset?.Size ?? new Float2(100), Size, out rect);
|
||||
CalculateTextureRect(_asset != null ? _asset.Size : new Float2(100), Size, out rect);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -52,9 +52,11 @@ namespace FlaxEditor.Windows
|
||||
VerticalAlignment = TextAlignment.Near,
|
||||
Parent = this
|
||||
};
|
||||
var copyVersionButton = new Button(Width - 104, 6, 100, 20)
|
||||
var buttonText = "Copy version info";
|
||||
var fontSize = Style.Current.FontMedium.MeasureText(buttonText);
|
||||
var copyVersionButton = new Button(Width - fontSize.X - 8, 6, fontSize.X + 4, 20)
|
||||
{
|
||||
Text = "Copy version info",
|
||||
Text = buttonText,
|
||||
TooltipText = "Copies the current engine version information to system clipboard.",
|
||||
Parent = this
|
||||
};
|
||||
|
||||
@@ -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
|
||||
/// </summary>
|
||||
public void Reimport()
|
||||
{
|
||||
if (_window?._previewSource != null)
|
||||
{
|
||||
_window._previewSource.Stop();
|
||||
_window.UpdateToolstrip();
|
||||
}
|
||||
Editor.Instance.ContentImporting.Reimport((BinaryAssetItem)_window.Item, ImportSettings, true);
|
||||
}
|
||||
|
||||
|
||||
@@ -200,6 +200,7 @@ namespace FlaxEditor.Windows.Assets
|
||||
ViewportCamera = new FPSCamera(),
|
||||
Parent = _split.Panel1
|
||||
};
|
||||
_preview.Task.ViewFlags &= ~ViewFlags.Sky & ~ViewFlags.Bloom;
|
||||
|
||||
// Asset properties
|
||||
_propertiesPresenter = new CustomEditorPresenter(null);
|
||||
|
||||
@@ -69,7 +69,7 @@ namespace FlaxEditor.Windows.Assets
|
||||
[EditorDisplay("General"), Tooltip("The base material used to override it's properties")]
|
||||
public MaterialBase BaseMaterial
|
||||
{
|
||||
get => Window?.Asset?.BaseMaterial;
|
||||
get => Window?.Asset != null ? Window?.Asset.BaseMaterial : null;
|
||||
set
|
||||
{
|
||||
var asset = Window?.Asset;
|
||||
@@ -101,10 +101,12 @@ namespace FlaxEditor.Windows.Assets
|
||||
[HideInEditor]
|
||||
public object[] Values
|
||||
{
|
||||
get => Window?.Asset?.Parameters.Select(x => x.Value).ToArray();
|
||||
get => Window?.Asset != null ? Window?.Asset.Parameters.Select(x => x.Value).ToArray() : null;
|
||||
set
|
||||
{
|
||||
var parameters = Window?.Asset?.Parameters;
|
||||
if (Window?.Asset == null)
|
||||
return;
|
||||
var parameters = Window?.Asset.Parameters;
|
||||
if (value != null && parameters != null)
|
||||
{
|
||||
if (value.Length != parameters.Length)
|
||||
@@ -131,9 +133,11 @@ namespace FlaxEditor.Windows.Assets
|
||||
[HideInEditor]
|
||||
public FlaxEngine.Object[] ValuesRef
|
||||
{
|
||||
get => Window?.Asset?.Parameters.Select(x => x.Value as FlaxEngine.Object).ToArray();
|
||||
get => Window?.Asset != null ? Window?.Asset.Parameters.Select(x => x.Value as FlaxEngine.Object).ToArray() : null;
|
||||
set
|
||||
{
|
||||
if (Window?.Asset == null)
|
||||
return;
|
||||
var parameters = Window?.Asset?.Parameters;
|
||||
if (value != null && parameters != null)
|
||||
{
|
||||
@@ -293,7 +297,7 @@ namespace FlaxEditor.Windows.Assets
|
||||
var p = (MaterialParameter)e.Tag;
|
||||
|
||||
// Try to get default value (from the base material)
|
||||
var pBase = baseMaterial?.GetParameter(p.Name);
|
||||
var pBase = baseMaterial != null ? baseMaterial.GetParameter(p.Name) : null;
|
||||
if (pBase != null && pBase.ParameterType == p.ParameterType)
|
||||
{
|
||||
valueContainer.SetDefaultValue(pBase.Value);
|
||||
|
||||
@@ -134,7 +134,7 @@ namespace FlaxEditor.Windows.Assets
|
||||
if (Window._skipEffectsGuiEvents)
|
||||
return;
|
||||
|
||||
Window._isolateIndex = mesh?.MaterialSlotIndex ?? -1;
|
||||
Window._isolateIndex = mesh != null ? mesh.MaterialSlotIndex : -1;
|
||||
Window.UpdateEffectsOnAsset();
|
||||
UpdateEffectsOnUI();
|
||||
}
|
||||
@@ -144,7 +144,7 @@ namespace FlaxEditor.Windows.Assets
|
||||
if (Window._skipEffectsGuiEvents)
|
||||
return;
|
||||
|
||||
Window._highlightIndex = mesh?.MaterialSlotIndex ?? -1;
|
||||
Window._highlightIndex = mesh != null ? mesh.MaterialSlotIndex : -1;
|
||||
Window.UpdateEffectsOnAsset();
|
||||
UpdateEffectsOnUI();
|
||||
}
|
||||
@@ -326,7 +326,7 @@ namespace FlaxEditor.Windows.Assets
|
||||
[EditorOrder(10), EditorDisplay("Materials", EditorDisplayAttribute.InlineStyle)]
|
||||
public MaterialSlot[] MaterialSlots
|
||||
{
|
||||
get => Asset?.MaterialSlots;
|
||||
get => Asset != null ? Asset.MaterialSlots : null;
|
||||
set
|
||||
{
|
||||
if (Asset != null)
|
||||
|
||||
@@ -187,7 +187,7 @@ namespace FlaxEditor.Windows.Assets
|
||||
base.Initialize(layout);
|
||||
|
||||
var emitterTrack = Values[0] as EmitterTrackProxy;
|
||||
if (emitterTrack?._effect?.Parameters == null)
|
||||
if (emitterTrack?._effect == null || emitterTrack?._effect.Parameters == null)
|
||||
return;
|
||||
|
||||
var group = layout.Group("Parameters");
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user