Merge branch '1.1' of https://github.com/FlaxEngine/FlaxEngine into ortho
Conflicts: Source/Editor/Viewport/EditorViewport.cs
This commit is contained in:
@@ -1,64 +0,0 @@
|
||||
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.Content.Settings
|
||||
{
|
||||
partial class BuildSettings
|
||||
{
|
||||
/// <summary>
|
||||
/// The build presets.
|
||||
/// </summary>
|
||||
[EditorOrder(5000), EditorDisplay("Presets", EditorDisplayAttribute.InlineStyle), Tooltip("Build presets configuration")]
|
||||
public BuildPreset[] Presets =
|
||||
{
|
||||
new BuildPreset
|
||||
{
|
||||
Name = "Development",
|
||||
Targets = new[]
|
||||
{
|
||||
new BuildTarget
|
||||
{
|
||||
Name = "Windows 64bit",
|
||||
Output = "Output\\Win64",
|
||||
Platform = BuildPlatform.Windows64,
|
||||
Mode = BuildConfiguration.Development,
|
||||
},
|
||||
}
|
||||
},
|
||||
new BuildPreset
|
||||
{
|
||||
Name = "Release",
|
||||
Targets = new[]
|
||||
{
|
||||
new BuildTarget
|
||||
{
|
||||
Name = "Windows 64bit",
|
||||
Output = "Output\\Win64",
|
||||
Platform = BuildPlatform.Windows64,
|
||||
Mode = BuildConfiguration.Release,
|
||||
},
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Gets the preset of the given name (ignore case search) or returns null if cannot find it.
|
||||
/// </summary>
|
||||
/// <param name="name">The preset name.</param>
|
||||
/// <returns>Found preset or null if is missing.</returns>
|
||||
public BuildPreset GetPreset(string name)
|
||||
{
|
||||
if (Presets != null)
|
||||
{
|
||||
for (int i = 0; i < Presets.Length; i++)
|
||||
{
|
||||
if (string.Equals(Presets[i].Name, name, StringComparison.OrdinalIgnoreCase))
|
||||
return Presets[i];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,341 +0,0 @@
|
||||
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.Content.Settings
|
||||
{
|
||||
partial class GameSettings
|
||||
{
|
||||
internal const string PS4PlatformSettingsTypename = "FlaxEditor.Content.Settings.PS4PlatformSettings";
|
||||
internal const string XboxScarlettPlatformSettingsTypename = "FlaxEditor.Content.Settings.XboxScarlettPlatformSettings";
|
||||
|
||||
/// <summary>
|
||||
/// The default application icon.
|
||||
/// </summary>
|
||||
[EditorOrder(30), EditorDisplay("General"), Tooltip("The default icon of the application.")]
|
||||
public Texture Icon;
|
||||
|
||||
/// <summary>
|
||||
/// Reference to the first scene to load on a game startup.
|
||||
/// </summary>
|
||||
[EditorOrder(900), EditorDisplay("Startup"), Tooltip("Reference to the first scene to load on a game startup.")]
|
||||
public SceneReference FirstScene;
|
||||
|
||||
/// <summary>
|
||||
/// True if skip showing splash screen image on the game startup.
|
||||
/// </summary>
|
||||
[EditorOrder(910), EditorDisplay("Startup", "No Splash Screen"), Tooltip("True if skip showing splash screen image on the game startup.")]
|
||||
public bool NoSplashScreen;
|
||||
|
||||
/// <summary>
|
||||
/// Reference to the splash screen image to show on a game startup.
|
||||
/// </summary>
|
||||
[EditorOrder(920), EditorDisplay("Startup"), Tooltip("Reference to the splash screen image to show on a game startup.")]
|
||||
public Texture SplashScreen;
|
||||
|
||||
/// <summary>
|
||||
/// Reference to <see cref="TimeSettings"/> asset.
|
||||
/// </summary>
|
||||
[EditorOrder(1010), EditorDisplay("Other Settings"), AssetReference(typeof(TimeSettings), true), Tooltip("Reference to Time Settings asset")]
|
||||
public JsonAsset Time;
|
||||
|
||||
/// <summary>
|
||||
/// Reference to <see cref="AudioSettings"/> asset.
|
||||
/// </summary>
|
||||
[EditorOrder(1015), EditorDisplay("Other Settings"), AssetReference(typeof(AudioSettings), true), Tooltip("Reference to Audio Settings asset")]
|
||||
public JsonAsset Audio;
|
||||
|
||||
/// <summary>
|
||||
/// Reference to <see cref="LayersAndTagsSettings"/> asset.
|
||||
/// </summary>
|
||||
[EditorOrder(1020), EditorDisplay("Other Settings"), AssetReference(typeof(LayersAndTagsSettings), true), Tooltip("Reference to Layers & Tags Settings asset")]
|
||||
public JsonAsset LayersAndTags;
|
||||
|
||||
/// <summary>
|
||||
/// Reference to <see cref="PhysicsSettings"/> asset.
|
||||
/// </summary>
|
||||
[EditorOrder(1030), EditorDisplay("Other Settings"), AssetReference(typeof(PhysicsSettings), true), Tooltip("Reference to Physics Settings asset")]
|
||||
public JsonAsset Physics;
|
||||
|
||||
/// <summary>
|
||||
/// Reference to <see cref="InputSettings"/> asset.
|
||||
/// </summary>
|
||||
[EditorOrder(1035), EditorDisplay("Other Settings"), AssetReference(typeof(InputSettings), true), Tooltip("Reference to Input Settings asset")]
|
||||
public JsonAsset Input;
|
||||
|
||||
/// <summary>
|
||||
/// Reference to <see cref="GraphicsSettings"/> asset.
|
||||
/// </summary>
|
||||
[EditorOrder(1040), EditorDisplay("Other Settings"), AssetReference(typeof(GraphicsSettings), true), Tooltip("Reference to Graphics Settings asset")]
|
||||
public JsonAsset Graphics;
|
||||
|
||||
/// <summary>
|
||||
/// Reference to <see cref="NavigationSettings"/> asset.
|
||||
/// </summary>
|
||||
[EditorOrder(1045), EditorDisplay("Other Settings"), AssetReference(typeof(NavigationSettings), true), Tooltip("Reference to Navigation Settings asset")]
|
||||
public JsonAsset Navigation;
|
||||
|
||||
/// <summary>
|
||||
/// Reference to <see cref="BuildSettings"/> asset.
|
||||
/// </summary>
|
||||
[EditorOrder(1050), EditorDisplay("Other Settings"), AssetReference(typeof(BuildSettings), true), Tooltip("Reference to Build Settings asset")]
|
||||
public JsonAsset GameCooking;
|
||||
|
||||
/// <summary>
|
||||
/// The custom settings to use with a game. Can be specified by the user to define game-specific options and be used by the external plugins (used as key-value pair).
|
||||
/// </summary>
|
||||
[EditorOrder(1100), EditorDisplay("Other Settings"), Tooltip("The custom settings to use with a game. Can be specified by the user to define game-specific options and be used by the external plugins (used as key-value pair).")]
|
||||
public Dictionary<string, JsonAsset> CustomSettings;
|
||||
|
||||
/// <summary>
|
||||
/// Reference to <see cref="WindowsPlatformSettings"/> asset. Used to apply configuration on Windows platform.
|
||||
/// </summary>
|
||||
[EditorOrder(2010), EditorDisplay("Platform Settings", "Windows"), AssetReference(typeof(WindowsPlatformSettings), true), Tooltip("Reference to Windows Platform Settings asset")]
|
||||
public JsonAsset WindowsPlatform;
|
||||
|
||||
/// <summary>
|
||||
/// Reference to <see cref="UWPPlatformSettings"/> asset. Used to apply configuration on Universal Windows Platform.
|
||||
/// </summary>
|
||||
[EditorOrder(2020), EditorDisplay("Platform Settings", "Universal Windows Platform"), AssetReference(typeof(UWPPlatformSettings), true), Tooltip("Reference to Universal Windows Platform Settings asset")]
|
||||
public JsonAsset UWPPlatform;
|
||||
|
||||
/// <summary>
|
||||
/// Reference to <see cref="LinuxPlatformSettings"/> asset. Used to apply configuration on Linux platform.
|
||||
/// </summary>
|
||||
[EditorOrder(2030), EditorDisplay("Platform Settings", "Linux"), AssetReference(typeof(LinuxPlatformSettings), true), Tooltip("Reference to Linux Platform Settings asset")]
|
||||
public JsonAsset LinuxPlatform;
|
||||
|
||||
/// <summary>
|
||||
/// Reference to PS4 Platform Settings asset. Used to apply configuration on PS4 platform.
|
||||
/// </summary>
|
||||
[EditorOrder(2040), EditorDisplay("Platform Settings", "PlayStation 4"), AssetReference(PS4PlatformSettingsTypename, true), Tooltip("Reference to PS4 Platform Settings asset")]
|
||||
public JsonAsset PS4Platform;
|
||||
|
||||
/// <summary>
|
||||
/// Reference to Xbox Scarlett Platform Settings asset. Used to apply configuration on Xbox Scarlett platform.
|
||||
/// </summary>
|
||||
[EditorOrder(2050), EditorDisplay("Platform Settings", "Xbox Scarlett"), AssetReference(XboxScarlettPlatformSettingsTypename, true), Tooltip("Reference to Xbox Scarlett Platform Settings asset")]
|
||||
public JsonAsset XboxScarlettPlatform;
|
||||
|
||||
/// <summary>
|
||||
/// Reference to <see cref="AndroidPlatformSettings"/> asset. Used to apply configuration on Android platform.
|
||||
/// </summary>
|
||||
[EditorOrder(2060), EditorDisplay("Platform Settings", "Android"), AssetReference(typeof(AndroidPlatformSettings), true), Tooltip("Reference to Android Platform Settings asset")]
|
||||
public JsonAsset AndroidPlatform;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the absolute path to the game settings asset file.
|
||||
/// </summary>
|
||||
public static string GameSettingsAssetPath
|
||||
{
|
||||
get { return StringUtils.CombinePaths(Globals.ProjectContentFolder, "GameSettings.json"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads the game settings asset.
|
||||
/// </summary>
|
||||
/// <returns>The loaded game settings.</returns>
|
||||
public static GameSettings Load()
|
||||
{
|
||||
var asset = FlaxEngine.Content.LoadAsync<JsonAsset>(GameSettingsAssetPath);
|
||||
if (asset && !asset.WaitForLoaded())
|
||||
{
|
||||
if (asset.CreateInstance() is GameSettings result)
|
||||
return result;
|
||||
}
|
||||
return new GameSettings();
|
||||
}
|
||||
|
||||
private static T LoadAsset<T>(JsonAsset asset) where T : new()
|
||||
{
|
||||
if (asset && !asset.WaitForLoaded())
|
||||
{
|
||||
if (asset.CreateInstance() is T result)
|
||||
return result;
|
||||
}
|
||||
return new T();
|
||||
}
|
||||
|
||||
private static SettingsBase LoadAsset(JsonAsset asset, string typename)
|
||||
{
|
||||
if (asset && !asset.WaitForLoaded() && asset.DataTypeName == typename)
|
||||
{
|
||||
if (asset.CreateInstance() is SettingsBase result)
|
||||
return result;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads the settings of the given type.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Supports loading game settings, any sub settings container (e.g. <see cref="PhysicsSettings"/>) and custom settings (see <see cref="CustomSettings"/>).
|
||||
/// </remarks>
|
||||
/// <code>
|
||||
/// var time = GameSettings.Load&ltTimeSettings&gt;();
|
||||
/// </code>
|
||||
/// <typeparam name="T">The game settings type (e.g. <see cref="TimeSettings"/>).</typeparam>
|
||||
/// <returns>Loaded settings object or null if fails.</returns>
|
||||
public static T Load<T>() where T : SettingsBase
|
||||
{
|
||||
var gameSettings = Load();
|
||||
var type = typeof(T);
|
||||
|
||||
if (type == typeof(GameSettings))
|
||||
return gameSettings as T;
|
||||
|
||||
if (type == typeof(TimeSettings))
|
||||
return LoadAsset<TimeSettings>(gameSettings.Time) as T;
|
||||
if (type == typeof(LayersAndTagsSettings))
|
||||
return LoadAsset<LayersAndTagsSettings>(gameSettings.LayersAndTags) as T;
|
||||
if (type == typeof(PhysicsSettings))
|
||||
return LoadAsset<PhysicsSettings>(gameSettings.Physics) as T;
|
||||
if (type == typeof(GraphicsSettings))
|
||||
return LoadAsset<GraphicsSettings>(gameSettings.Graphics) as T;
|
||||
if (type == typeof(NavigationSettings))
|
||||
return LoadAsset<NavigationSettings>(gameSettings.Navigation) as T;
|
||||
if (type == typeof(BuildSettings))
|
||||
return LoadAsset<BuildSettings>(gameSettings.GameCooking) as T;
|
||||
if (type == typeof(InputSettings))
|
||||
return LoadAsset<InputSettings>(gameSettings.Input) as T;
|
||||
if (type == typeof(WindowsPlatformSettings))
|
||||
return LoadAsset<WindowsPlatformSettings>(gameSettings.WindowsPlatform) as T;
|
||||
if (type == typeof(UWPPlatformSettings))
|
||||
return LoadAsset<UWPPlatformSettings>(gameSettings.UWPPlatform) as T;
|
||||
if (type == typeof(LinuxPlatformSettings))
|
||||
return LoadAsset<LinuxPlatformSettings>(gameSettings.LinuxPlatform) as T;
|
||||
if (type.FullName == PS4PlatformSettingsTypename)
|
||||
return LoadAsset(gameSettings.PS4Platform, PS4PlatformSettingsTypename) as T;
|
||||
if (type.FullName == XboxScarlettPlatformSettingsTypename)
|
||||
return LoadAsset(gameSettings.XboxScarlettPlatform, XboxScarlettPlatformSettingsTypename) as T;
|
||||
if (type == typeof(AndroidPlatformSettings))
|
||||
return LoadAsset<AndroidPlatformSettings>(gameSettings.AndroidPlatform) as T;
|
||||
if (type == typeof(AudioSettings))
|
||||
return LoadAsset<AudioSettings>(gameSettings.Audio) as T;
|
||||
|
||||
if (gameSettings.CustomSettings != null)
|
||||
{
|
||||
foreach (var e in gameSettings.CustomSettings)
|
||||
{
|
||||
if (e.Value && !e.Value.WaitForLoaded() && e.Value.DataTypeName == type.FullName)
|
||||
{
|
||||
var custom = e.Value.CreateInstance();
|
||||
if (custom is T result)
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static bool SaveAsset<T>(GameSettings gameSettings, ref JsonAsset asset, T obj) where T : SettingsBase
|
||||
{
|
||||
if (asset)
|
||||
{
|
||||
// Override settings
|
||||
return Editor.SaveJsonAsset(asset.Path, obj);
|
||||
}
|
||||
|
||||
// Create new settings asset and link it to the game settings
|
||||
var path = StringUtils.CombinePaths(Globals.ProjectContentFolder, CustomEditors.CustomEditorsUtil.GetPropertyNameUI(typeof(T).Name) + ".json");
|
||||
if (Editor.SaveJsonAsset(path, obj))
|
||||
return true;
|
||||
asset = FlaxEngine.Content.LoadAsync<JsonAsset>(path);
|
||||
return Editor.SaveJsonAsset(GameSettingsAssetPath, gameSettings);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves the settings of the given type.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Supports saving game settings, any sub settings container (e.g. <see cref="PhysicsSettings"/>).
|
||||
/// </remarks>
|
||||
/// <code>
|
||||
/// var time = GameSettings.Load&ltTimeSettings&gt;();
|
||||
/// time.TimeScale = 0.5f;
|
||||
/// GameSettings.Save&ltTimeSettings&gt;(time);
|
||||
/// </code>
|
||||
/// <typeparam name="T">The game settings type (e.g. <see cref="TimeSettings"/>).</typeparam>
|
||||
/// <returns>True if failed otherwise false.</returns>
|
||||
public static bool Save<T>(T obj) where T : SettingsBase
|
||||
{
|
||||
var type = typeof(T);
|
||||
|
||||
if (type == typeof(GameSettings))
|
||||
{
|
||||
return Editor.SaveJsonAsset(GameSettingsAssetPath, obj);
|
||||
}
|
||||
|
||||
var gameSettings = Load();
|
||||
|
||||
if (type == typeof(TimeSettings))
|
||||
return SaveAsset(gameSettings, ref gameSettings.Time, obj);
|
||||
if (type == typeof(LayersAndTagsSettings))
|
||||
return SaveAsset(gameSettings, ref gameSettings.LayersAndTags, obj);
|
||||
if (type == typeof(PhysicsSettings))
|
||||
return SaveAsset(gameSettings, ref gameSettings.Physics, obj);
|
||||
if (type == typeof(GraphicsSettings))
|
||||
return SaveAsset(gameSettings, ref gameSettings.Graphics, obj);
|
||||
if (type == typeof(NavigationSettings))
|
||||
return SaveAsset(gameSettings, ref gameSettings.Navigation, obj);
|
||||
if (type == typeof(BuildSettings))
|
||||
return SaveAsset(gameSettings, ref gameSettings.GameCooking, obj);
|
||||
if (type == typeof(InputSettings))
|
||||
return SaveAsset(gameSettings, ref gameSettings.Input, obj);
|
||||
if (type == typeof(WindowsPlatformSettings))
|
||||
return SaveAsset(gameSettings, ref gameSettings.WindowsPlatform, obj);
|
||||
if (type == typeof(UWPPlatformSettings))
|
||||
return SaveAsset(gameSettings, ref gameSettings.UWPPlatform, obj);
|
||||
if (type == typeof(LinuxPlatformSettings))
|
||||
return SaveAsset(gameSettings, ref gameSettings.LinuxPlatform, obj);
|
||||
if (type.FullName == PS4PlatformSettingsTypename)
|
||||
return SaveAsset(gameSettings, ref gameSettings.PS4Platform, obj);
|
||||
if (type.FullName == XboxScarlettPlatformSettingsTypename)
|
||||
return SaveAsset(gameSettings, ref gameSettings.XboxScarlettPlatform, obj);
|
||||
if (type == typeof(AndroidPlatformSettings))
|
||||
return SaveAsset(gameSettings, ref gameSettings.AndroidPlatform, obj);
|
||||
if (type == typeof(AudioSettings))
|
||||
return SaveAsset(gameSettings, ref gameSettings.Audio, obj);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the custom settings (or unsets if provided asset is null).
|
||||
/// </summary>
|
||||
/// <param name="key">The custom key (must be unique per context).</param>
|
||||
/// <param name="customSettingsAsset">The custom settings asset.</param>
|
||||
/// <returns>True if failed otherwise false.</returns>
|
||||
public static bool SetCustomSettings(string key, JsonAsset customSettingsAsset)
|
||||
{
|
||||
if (key == null)
|
||||
throw new ArgumentNullException(nameof(key));
|
||||
|
||||
var gameSettings = Load();
|
||||
|
||||
if (customSettingsAsset == null && gameSettings.CustomSettings != null)
|
||||
{
|
||||
gameSettings.CustomSettings.Remove(key);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (gameSettings.CustomSettings == null)
|
||||
gameSettings.CustomSettings = new Dictionary<string, JsonAsset>();
|
||||
gameSettings.CustomSettings[key] = customSettingsAsset;
|
||||
}
|
||||
|
||||
return Editor.SaveJsonAsset(GameSettingsAssetPath, gameSettings);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads the current game settings asset and applies it to the engine runtime configuration.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
public static extern void Apply();
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
|
||||
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.Content.Settings
|
||||
{
|
||||
partial class InputSettings
|
||||
{
|
||||
/// <summary>
|
||||
/// Maps a discrete button or key press events to a "friendly name" that will later be bound to event-driven behavior. The end effect is that pressing (and/or releasing) a key, mouse button, or keypad button.
|
||||
/// </summary>
|
||||
/// <seealso cref="Input.ActionMappings"/>
|
||||
[Collection(Spacing = 10)]
|
||||
[EditorOrder(100), EditorDisplay("Input Map"), Tooltip("Maps a discrete button or key press events to a \"friendly name\" that will later be bound to event-driven behavior. The end effect is that pressing (and/or releasing) a key, mouse button, or keypad button.")]
|
||||
public ActionConfig[] ActionMappings;
|
||||
|
||||
/// <summary>
|
||||
/// Maps keyboard, controller, or mouse inputs to a "friendly name" that will later be bound to continuous game behavior, such as movement. The inputs mapped in AxisMappings are continuously polled, even if they are just reporting that their input value.
|
||||
/// </summary>
|
||||
/// <seealso cref="Input.AxisMappings"/>
|
||||
[Collection(Spacing = 10)]
|
||||
[EditorOrder(200), EditorDisplay("Input Map"), Tooltip("Maps keyboard, controller, or mouse inputs to a \"friendly name\" that will later be bound to continuous game behavior, such as movement. The inputs mapped in AxisMappings are continuously polled, even if they are just reporting that their input value.")]
|
||||
public AxisConfig[] AxisMappings;
|
||||
}
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.Content.Settings
|
||||
{
|
||||
partial class LayersAndTagsSettings
|
||||
{
|
||||
/// <summary>
|
||||
/// The tag names.
|
||||
/// </summary>
|
||||
[EditorOrder(10), EditorDisplay("Tags", EditorDisplayAttribute.InlineStyle)]
|
||||
public List<string> Tags = new List<string>();
|
||||
|
||||
/// <summary>
|
||||
/// The layers names.
|
||||
/// </summary>
|
||||
[EditorOrder(10), EditorDisplay("Layers", EditorDisplayAttribute.InlineStyle), Collection(ReadOnly = true)]
|
||||
public string[] Layers = new string[32];
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current tags collection.
|
||||
/// </summary>
|
||||
/// <returns>The tags collection.</returns>
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
internal static extern string[] GetCurrentTags();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current layer names (max 32 items but trims last empty items).
|
||||
/// </summary>
|
||||
/// <returns>The layers.</returns>
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
public static extern string[] GetCurrentLayers();
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
|
||||
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.Content.Settings
|
||||
{
|
||||
partial class PhysicsSettings
|
||||
{
|
||||
/// <summary>
|
||||
/// The collision layers masks. Used to define layer-based collision detection.
|
||||
/// </summary>
|
||||
[EditorOrder(1040), EditorDisplay("Layers Matrix"), CustomEditor(typeof(FlaxEditor.CustomEditors.Dedicated.LayersMatrixEditor))]
|
||||
public uint[] LayerMasks = new uint[32];
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="PhysicsSettings"/> class.
|
||||
/// </summary>
|
||||
public PhysicsSettings()
|
||||
{
|
||||
for (int i = 0; i < 32; i++)
|
||||
{
|
||||
LayerMasks[i] = uint.MaxValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1081,12 +1081,12 @@ bool CookAssetsStep::Perform(CookingData& data)
|
||||
|
||||
data.StepProgress(TEXT("Creating assets cache"), Step2ProgressEnd);
|
||||
|
||||
// Create asset paths mapping for the root assets.
|
||||
// Create asset paths mapping for the assets.
|
||||
// Assets mapping is use to convert paths used in Content::Load(path) into the asset id.
|
||||
// It fixes the issues when in build game all assets are in the packages while engine parts are requesting in-build assets by name.
|
||||
// It fixes the issues when in build game all assets are in the packages and are requested by path.
|
||||
// E.g. game settings are loaded from `Content/GameSettings.json` file which is packages in one of the packages.
|
||||
// Additionally it improves the in-build assets loading performance.
|
||||
for (auto i = data.RootAssets.Begin(); i.IsNotEnd(); ++i)
|
||||
// Additionally it improves the in-build assets loading performance (no more registry linear lookup for path by dictionary access).
|
||||
for (auto i = data.Assets.Begin(); i.IsNotEnd(); ++i)
|
||||
{
|
||||
if (Content::GetAssetInfo(i->Item, assetInfo))
|
||||
{
|
||||
|
||||
@@ -43,10 +43,18 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
// Copy old values
|
||||
Array.Copy(array, 0, newValues, 0, sharedCount);
|
||||
|
||||
// Fill new entries with the last value
|
||||
for (int i = oldSize; i < newSize; i++)
|
||||
if (elementType.IsValueType)
|
||||
{
|
||||
Array.Copy(array, oldSize - 1, newValues, i, 1);
|
||||
// Fill new entries with the last value
|
||||
for (int i = oldSize; i < newSize; i++)
|
||||
Array.Copy(array, oldSize - 1, newValues, i, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Initialize new entries with default values
|
||||
var defaultValue = TypeUtils.GetDefaultValue(new ScriptType(elementType));
|
||||
for (int i = oldSize; i < newSize; i++)
|
||||
newValues.SetValue(defaultValue, i);
|
||||
}
|
||||
}
|
||||
else if (newSize > 0)
|
||||
@@ -54,9 +62,7 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
// Initialize new entries with default values
|
||||
var defaultValue = TypeUtils.GetDefaultValue(new ScriptType(elementType));
|
||||
for (int i = 0; i < newSize; i++)
|
||||
{
|
||||
newValues.SetValue(defaultValue, i);
|
||||
}
|
||||
}
|
||||
|
||||
SetValue(newValues);
|
||||
|
||||
@@ -434,17 +434,19 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
{
|
||||
Text = "+",
|
||||
TooltipText = "Create a new instance of the object",
|
||||
Height = ButtonSize,
|
||||
Width = ButtonSize,
|
||||
X = layout.ContainerControl.Width - ButtonSize - 4,
|
||||
Size = new Vector2(ButtonSize, ButtonSize),
|
||||
AnchorPreset = AnchorPresets.MiddleRight,
|
||||
Parent = layout.ContainerControl
|
||||
Parent = layout.ContainerControl,
|
||||
Location = new Vector2(layout.ContainerControl.Width - ButtonSize - 4, (layout.ContainerControl.Height - ButtonSize) * 0.5f),
|
||||
};
|
||||
button.Clicked += () =>
|
||||
{
|
||||
var newType = Values.Type;
|
||||
SetValue(newType.CreateInstance());
|
||||
RebuildLayoutOnRefresh();
|
||||
if (ParentEditor != null)
|
||||
ParentEditor.RebuildLayoutOnRefresh();
|
||||
else
|
||||
RebuildLayoutOnRefresh();
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -40,30 +40,35 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
// Allocate new list
|
||||
var listType = Values.Type;
|
||||
var newValues = (IList)listType.CreateInstance();
|
||||
var elementType = ElementType;
|
||||
|
||||
var sharedCount = Mathf.Min(oldSize, newSize);
|
||||
if (list != null && sharedCount > 0)
|
||||
{
|
||||
// Copy old values
|
||||
for (int i = 0; i < sharedCount; i++)
|
||||
{
|
||||
newValues.Add(list[i]);
|
||||
}
|
||||
|
||||
// Fill new entries with the last value
|
||||
for (int i = oldSize; i < newSize; i++)
|
||||
if (elementType.IsValueType)
|
||||
{
|
||||
newValues.Add(list[oldSize - 1]);
|
||||
// Fill new entries with the last value
|
||||
for (int i = oldSize; i < newSize; i++)
|
||||
newValues.Add(list[oldSize - 1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Initialize new entries with default values
|
||||
var defaultValue = Scripting.TypeUtils.GetDefaultValue(elementType);
|
||||
for (int i = oldSize; i < newSize; i++)
|
||||
newValues.Add(defaultValue);
|
||||
}
|
||||
}
|
||||
else if (newSize > 0)
|
||||
{
|
||||
// Fill new entries with default value
|
||||
var defaultValue = Scripting.TypeUtils.GetDefaultValue(ElementType);
|
||||
var defaultValue = Scripting.TypeUtils.GetDefaultValue(elementType);
|
||||
for (int i = oldSize; i < newSize; i++)
|
||||
{
|
||||
newValues.Add(defaultValue);
|
||||
}
|
||||
}
|
||||
|
||||
SetValue(newValues);
|
||||
|
||||
@@ -983,6 +983,103 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
}
|
||||
}
|
||||
|
||||
private class RerouteNode : SurfaceNode
|
||||
{
|
||||
public static readonly Vector2 DefaultSize = new Vector2(16);
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override bool ShowTooltip => false;
|
||||
|
||||
/// <inheritdoc />
|
||||
public RerouteNode(uint id, VisjectSurfaceContext context, NodeArchetype nodeArch, GroupArchetype groupArch)
|
||||
: base(id, context, nodeArch, groupArch)
|
||||
{
|
||||
Size = DefaultSize;
|
||||
Title = string.Empty;
|
||||
BackgroundColor = Color.Transparent;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnSurfaceLoaded()
|
||||
{
|
||||
base.OnSurfaceLoaded();
|
||||
|
||||
var inputBox = GetBox(0);
|
||||
var outputBox = GetBox(1);
|
||||
inputBox.Location = Vector2.Zero;
|
||||
outputBox.Location = Vector2.Zero;
|
||||
|
||||
UpdateBoxes();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void ConnectionTick(Box box)
|
||||
{
|
||||
base.ConnectionTick(box);
|
||||
|
||||
UpdateBoxes();
|
||||
}
|
||||
|
||||
private void UpdateBoxes()
|
||||
{
|
||||
var inputBox = GetBox(0);
|
||||
var outputBox = GetBox(1);
|
||||
|
||||
inputBox.Visible = !inputBox.HasAnyConnection;
|
||||
outputBox.Visible = !outputBox.HasAnyConnection;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanSelect(ref Vector2 location)
|
||||
{
|
||||
return new Rectangle(Location, DefaultSize).Contains(ref location);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void UpdateRectangles()
|
||||
{
|
||||
_headerRect = Rectangle.Empty;
|
||||
_closeButtonRect = Rectangle.Empty;
|
||||
_footerRect = Rectangle.Empty;
|
||||
}
|
||||
|
||||
public override void Draw()
|
||||
{
|
||||
var style = Surface.Style;
|
||||
var inputBox = GetBox(0);
|
||||
var outputBox = GetBox(1);
|
||||
var connectionColor = style.Colors.Default;
|
||||
float barHorizontalOffset = -2;
|
||||
float barHeight = 3;
|
||||
|
||||
if (inputBox.HasAnyConnection)
|
||||
{
|
||||
var hints = inputBox.Connections[0].ParentNode.Archetype.ConnectionsHints;
|
||||
Surface.Style.GetConnectionColor(inputBox.Connections[0].CurrentType, hints, out connectionColor);
|
||||
}
|
||||
|
||||
if (!inputBox.HasAnyConnection)
|
||||
{
|
||||
Render2D.FillRectangle(new Rectangle(-barHorizontalOffset - barHeight * 2, (DefaultSize.Y - barHeight) / 2, barHeight * 2, barHeight), connectionColor);
|
||||
}
|
||||
|
||||
if (!outputBox.HasAnyConnection)
|
||||
{
|
||||
Render2D.FillRectangle(new Rectangle(DefaultSize.X + barHorizontalOffset, (DefaultSize.Y - barHeight) / 2, barHeight * 2, barHeight), connectionColor);
|
||||
}
|
||||
|
||||
if (inputBox.HasAnyConnection && outputBox.HasAnyConnection)
|
||||
{
|
||||
var hints = inputBox.Connections[0].ParentNode.Archetype.ConnectionsHints;
|
||||
Surface.Style.GetConnectionColor(inputBox.Connections[0].CurrentType, hints, out connectionColor);
|
||||
SpriteHandle icon = style.Icons.BoxClose;
|
||||
Render2D.DrawSprite(icon, new Rectangle(Vector2.Zero, DefaultSize), connectionColor);
|
||||
}
|
||||
|
||||
base.Draw();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The nodes for that group.
|
||||
/// </summary>
|
||||
@@ -1438,6 +1535,23 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
NodeElementArchetype.Factory.Input(0, string.Empty, true, typeof(object), 1),
|
||||
}
|
||||
},
|
||||
new NodeArchetype
|
||||
{
|
||||
TypeID = 29,
|
||||
Title = "Reroute",
|
||||
Create = (id, context, arch, groupArch) => new RerouteNode(id, context, arch, groupArch),
|
||||
Description = "Reroute a connection.",
|
||||
Flags = NodeFlags.NoCloseButton | NodeFlags.NoSpawnViaGUI | NodeFlags.AllGraphs,
|
||||
Size = RerouteNode.DefaultSize,
|
||||
ConnectionsHints = ConnectionsHint.All,
|
||||
IndependentBoxes = new int[] { 0 },
|
||||
DependentBoxes = new int[] { 1 },
|
||||
Elements = new[]
|
||||
{
|
||||
NodeElementArchetype.Factory.Input(0, string.Empty, true, null, 0),
|
||||
NodeElementArchetype.Factory.Output(0, string.Empty, null, 1, true),
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -159,6 +159,7 @@ namespace FlaxEditor.Surface.ContextMenu
|
||||
var start = font.GetCharPosition(_archetype.Title, 0);
|
||||
var end = font.GetCharPosition(_archetype.Title, _archetype.Title.Length - 1);
|
||||
_highlights.Add(new Rectangle(start.X + 2, 0, end.X - start.X, Height));
|
||||
_isFullMatch = true;
|
||||
Visible = true;
|
||||
}
|
||||
else if (NodeArchetype.TryParseText != null && NodeArchetype.TryParseText(filterText, out var data))
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
using System;
|
||||
|
||||
namespace FlaxEditor.Surface.Elements
|
||||
{
|
||||
@@ -12,6 +13,11 @@ namespace FlaxEditor.Surface.Elements
|
||||
[HideInEditor]
|
||||
public class OutputBox : Box
|
||||
{
|
||||
/// <summary>
|
||||
/// Distance for the mouse to be considered above the connection
|
||||
/// </summary>
|
||||
public float MouseOverConnectionDistance => 100f / Surface.ViewScale;
|
||||
|
||||
/// <inheritdoc />
|
||||
public OutputBox(SurfaceNode parentNode, NodeElementArchetype archetype)
|
||||
: base(parentNode, archetype, archetype.Position + new Vector2(parentNode.Archetype.Size.X, 0))
|
||||
@@ -43,23 +49,96 @@ namespace FlaxEditor.Surface.Elements
|
||||
*/
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a point intersects a connection
|
||||
/// </summary>
|
||||
/// <param name="targetBox">The other box.</param>
|
||||
/// <param name="mousePosition">The mouse position</param>
|
||||
public bool IntersectsConnection(Box targetBox, ref Vector2 mousePosition)
|
||||
{
|
||||
var startPos = Parent.PointToParent(Center);
|
||||
Vector2 endPos = targetBox.Parent.PointToParent(targetBox.Center);
|
||||
return IntersectsConnection(ref startPos, ref endPos, ref mousePosition, MouseOverConnectionDistance);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a point intersects a bezier curve
|
||||
/// </summary>
|
||||
/// <param name="start">The start location.</param>
|
||||
/// <param name="end">The end location.</param>
|
||||
/// <param name="point">The point</param>
|
||||
/// <param name="distance">Distance at which its an intersection</param>
|
||||
public static bool IntersectsConnection(ref Vector2 start, ref Vector2 end, ref Vector2 point, float distance)
|
||||
{
|
||||
// Pretty much a point in rectangle check
|
||||
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;
|
||||
|
||||
// Taken from the Render2D.DrawBezier code
|
||||
float squaredDistance = distance;
|
||||
|
||||
var dst = (end - start) * new Vector2(0.5f, 0.05f);
|
||||
Vector2 control1 = new Vector2(start.X + dst.X, start.Y + dst.Y);
|
||||
Vector2 control2 = new Vector2(end.X - dst.X, end.Y + dst.Y);
|
||||
|
||||
Vector2 d1 = control1 - start;
|
||||
Vector2 d2 = control2 - control1;
|
||||
Vector2 d3 = end - control2;
|
||||
float len = d1.Length + d2.Length + d3.Length;
|
||||
int segmentCount = Math.Min(Math.Max(Mathf.CeilToInt(len * 0.05f), 1), 100);
|
||||
float segmentCountInv = 1.0f / segmentCount;
|
||||
|
||||
Bezier(ref start, ref control1, ref control2, ref end, 0, out Vector2 p);
|
||||
for (int i = 1; i <= segmentCount; i++)
|
||||
{
|
||||
Vector2 oldp = p;
|
||||
float t = i * segmentCountInv;
|
||||
Bezier(ref start, ref control1, ref control2, ref end, t, out p);
|
||||
|
||||
// Maybe it would be reasonable to return the point?
|
||||
CollisionsHelper.ClosestPointPointLine(ref point, ref oldp, ref p, out Vector2 result);
|
||||
if (Vector2.DistanceSquared(point, result) <= squaredDistance)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static void Bezier(ref Vector2 p0, ref Vector2 p1, ref Vector2 p2, ref Vector2 p3, float alpha, out Vector2 result)
|
||||
{
|
||||
Vector2.Lerp(ref p0, ref p1, alpha, out var p01);
|
||||
Vector2.Lerp(ref p1, ref p2, alpha, out var p12);
|
||||
Vector2.Lerp(ref p2, ref p3, alpha, out var p23);
|
||||
Vector2.Lerp(ref p01, ref p12, alpha, out var p012);
|
||||
Vector2.Lerp(ref p12, ref p23, alpha, out var p123);
|
||||
Vector2.Lerp(ref p012, ref p123, alpha, out result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draw all connections coming from this box.
|
||||
/// </summary>
|
||||
public void DrawConnections()
|
||||
public void DrawConnections(ref Vector2 mousePosition)
|
||||
{
|
||||
float mouseOverDistance = MouseOverConnectionDistance;
|
||||
// Draw all the connections
|
||||
var center = Size * 0.5f;
|
||||
var tmp = PointToParent(ref center);
|
||||
var startPos = Parent.PointToParent(ref tmp);
|
||||
var startPos = Parent.PointToParent(Center);
|
||||
var startHighlight = ConnectionsHighlightIntensity;
|
||||
for (int i = 0; i < Connections.Count; i++)
|
||||
{
|
||||
Box targetBox = Connections[i];
|
||||
tmp = targetBox.PointToParent(ref center);
|
||||
Vector2 endPos = targetBox.Parent.PointToParent(ref tmp);
|
||||
Vector2 endPos = targetBox.Parent.PointToParent(targetBox.Center);
|
||||
var highlight = 1 + Mathf.Max(startHighlight, targetBox.ConnectionsHighlightIntensity);
|
||||
var color = _currentTypeColor * highlight;
|
||||
|
||||
// TODO: Figure out how to only draw the topmost connection
|
||||
if (IntersectsConnection(ref startPos, ref endPos, ref mousePosition, mouseOverDistance))
|
||||
{
|
||||
highlight += 0.5f;
|
||||
}
|
||||
|
||||
DrawConnection(ref startPos, ref endPos, ref color, highlight);
|
||||
}
|
||||
}
|
||||
@@ -70,12 +149,9 @@ namespace FlaxEditor.Surface.Elements
|
||||
public void DrawSelectedConnection(Box targetBox)
|
||||
{
|
||||
// Draw all the connections
|
||||
var center = Size * 0.5f;
|
||||
var tmp = PointToParent(ref center);
|
||||
var startPos = Parent.PointToParent(ref tmp);
|
||||
tmp = targetBox.PointToParent(ref center);
|
||||
Vector2 endPos = targetBox.Parent.PointToParent(ref tmp);
|
||||
DrawConnection(ref startPos, ref endPos, ref _currentTypeColor, 2);
|
||||
var startPos = Parent.PointToParent(Center);
|
||||
Vector2 endPos = targetBox.Parent.PointToParent(targetBox.Center);
|
||||
DrawConnection(ref startPos, ref endPos, ref _currentTypeColor, 2.5f);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -125,6 +125,7 @@ namespace FlaxEditor.Surface
|
||||
AutoFocus = false;
|
||||
TooltipText = nodeArch.Description;
|
||||
CullChildren = false;
|
||||
BackgroundColor = Style.Current.BackgroundNormal;
|
||||
|
||||
if (Archetype.DefaultValues != null)
|
||||
{
|
||||
@@ -552,19 +553,22 @@ namespace FlaxEditor.Surface
|
||||
|
||||
internal Box GetNextBox(Box box)
|
||||
{
|
||||
int i = 0;
|
||||
for (; i < Elements.Count; i++)
|
||||
// Get the one after it
|
||||
for (int i = box.IndexInParent + 1; i < Elements.Count; i++)
|
||||
{
|
||||
if (Elements[i] == box)
|
||||
if (Elements[i] is Box b)
|
||||
{
|
||||
// We found the box
|
||||
break;
|
||||
return b;
|
||||
}
|
||||
}
|
||||
|
||||
// Get the one after it
|
||||
i++;
|
||||
for (; i < Elements.Count; i++)
|
||||
return null;
|
||||
}
|
||||
|
||||
internal Box GetPreviousBox(Box box)
|
||||
{
|
||||
// Get the one before it
|
||||
for (int i = box.IndexInParent - 1; i >= 0; i--)
|
||||
{
|
||||
if (Elements[i] is Box b)
|
||||
{
|
||||
@@ -754,29 +758,33 @@ namespace FlaxEditor.Surface
|
||||
{
|
||||
if (Elements[j] is OutputBox ob && ob.HasAnyConnection)
|
||||
{
|
||||
ob.DrawConnections();
|
||||
ob.DrawConnections(ref mousePosition);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draws all selected connections between surface objects related to this node.
|
||||
/// </summary>
|
||||
/// <param name="selectedConnectionIndex">The index of the currently selected connection.</param>
|
||||
public void DrawSelectedConnections(int selectedConnectionIndex)
|
||||
{
|
||||
if (_isSelected)
|
||||
{
|
||||
bool hasBoxesSelection = HasBoxesSelection;
|
||||
for (int j = 0; j < Elements.Count; j++)
|
||||
if (HasBoxesSelection)
|
||||
{
|
||||
if (Elements[j] is Box box && box.HasAnyConnection && (!hasBoxesSelection || box.IsSelected))
|
||||
for (int j = 0; j < Elements.Count; j++)
|
||||
{
|
||||
if (box is OutputBox ob)
|
||||
if (Elements[j] is Box box && box.IsSelected && selectedConnectionIndex < box.Connections.Count)
|
||||
{
|
||||
for (int i = 0; i < ob.Connections.Count; i++)
|
||||
if (box is OutputBox ob)
|
||||
{
|
||||
ob.DrawSelectedConnection(ob.Connections[i]);
|
||||
ob.DrawSelectedConnection(ob.Connections[selectedConnectionIndex]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < box.Connections.Count; i++)
|
||||
else
|
||||
{
|
||||
if (box.Connections[i] is OutputBox outputBox)
|
||||
if (box.Connections[selectedConnectionIndex] is OutputBox outputBox)
|
||||
{
|
||||
outputBox.DrawSelectedConnection(box);
|
||||
}
|
||||
@@ -784,6 +792,32 @@ namespace FlaxEditor.Surface
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int j = 0; j < Elements.Count; j++)
|
||||
{
|
||||
if (Elements[j] is Box box)
|
||||
{
|
||||
if (box is OutputBox ob)
|
||||
{
|
||||
for (int i = 0; i < ob.Connections.Count; i++)
|
||||
{
|
||||
ob.DrawSelectedConnection(ob.Connections[i]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < box.Connections.Count; i++)
|
||||
{
|
||||
if (box.Connections[i] is OutputBox outputBox)
|
||||
{
|
||||
outputBox.DrawSelectedConnection(box);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -943,7 +977,7 @@ namespace FlaxEditor.Surface
|
||||
|
||||
// Background
|
||||
var backgroundRect = new Rectangle(Vector2.Zero, Size);
|
||||
Render2D.FillRectangle(backgroundRect, style.BackgroundNormal);
|
||||
Render2D.FillRectangle(backgroundRect, BackgroundColor);
|
||||
|
||||
// Breakpoint hit
|
||||
if (Breakpoint.Hit)
|
||||
@@ -1006,7 +1040,7 @@ namespace FlaxEditor.Surface
|
||||
}
|
||||
|
||||
// Secondary Context Menu
|
||||
if (button == MouseButton.Right && false)
|
||||
if (button == MouseButton.Right)
|
||||
{
|
||||
if (!IsSelected)
|
||||
Surface.Select(this);
|
||||
|
||||
@@ -10,7 +10,7 @@ namespace FlaxEditor.Surface.Undo
|
||||
/// The helper structure for Surface node box handle.
|
||||
/// </summary>
|
||||
[HideInEditor]
|
||||
public struct BoxHandle
|
||||
public struct BoxHandle : IEquatable<BoxHandle>
|
||||
{
|
||||
private readonly uint _nodeId;
|
||||
private readonly int _boxId;
|
||||
@@ -51,5 +51,27 @@ namespace FlaxEditor.Surface.Undo
|
||||
throw new Exception("Missing box.");
|
||||
return box;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return obj is BoxHandle handle && Equals(handle);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool Equals(BoxHandle other)
|
||||
{
|
||||
return _nodeId == other._nodeId &&
|
||||
_boxId == other._boxId;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override int GetHashCode()
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
return (_nodeId.GetHashCode() * 397) ^ _boxId.GetHashCode();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,9 +106,12 @@ namespace FlaxEditor.Surface.Undo
|
||||
}
|
||||
for (int i = 0; i < output.Length; i++)
|
||||
{
|
||||
var box = output[i].Get(context);
|
||||
oB.Connections.Add(box);
|
||||
box.Connections.Add(oB);
|
||||
if (!output[i].Equals(_input))
|
||||
{
|
||||
var box = output[i].Get(context);
|
||||
oB.Connections.Add(box);
|
||||
box.Connections.Add(oB);
|
||||
}
|
||||
}
|
||||
|
||||
toUpdate.AddRange(iB.Connections);
|
||||
|
||||
@@ -103,7 +103,7 @@ namespace FlaxEditor.Surface
|
||||
{
|
||||
for (int j = 0; j < node.Elements.Count; j++)
|
||||
{
|
||||
if (node.Elements[j] is Box box)
|
||||
if (node.Elements[j] is Box box && box.Connections.Count > 0)
|
||||
{
|
||||
var dataModelBox = new DataModelBox
|
||||
{
|
||||
|
||||
@@ -103,6 +103,7 @@ namespace FlaxEditor.Surface
|
||||
for (int i = 0; i < Nodes.Count; i++)
|
||||
{
|
||||
Nodes[i].DrawConnections(ref mousePosition);
|
||||
Nodes[i].DrawSelectedConnections(_selectedConnectionIndex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -274,12 +274,47 @@ namespace FlaxEditor.Surface
|
||||
bool handled = base.OnMouseDoubleClick(location, button);
|
||||
if (!handled)
|
||||
CustomMouseDoubleClick?.Invoke(ref location, button, ref handled);
|
||||
if (handled)
|
||||
|
||||
if (!handled)
|
||||
{
|
||||
return true;
|
||||
var mousePos = _rootControl.PointFromParent(ref _mousePos);
|
||||
if (IntersectsConnection(mousePos, out InputBox inputBox, out OutputBox outputBox))
|
||||
{
|
||||
if (Undo != null)
|
||||
{
|
||||
bool undoEnabled = Undo.Enabled;
|
||||
Undo.Enabled = false;
|
||||
var rerouteNode = Context.SpawnNode(7, 29, mousePos);
|
||||
Undo.Enabled = undoEnabled;
|
||||
|
||||
var spawnNodeAction = new AddRemoveNodeAction(rerouteNode, true);
|
||||
|
||||
var disconnectBoxesAction = new ConnectBoxesAction(inputBox, outputBox, false);
|
||||
inputBox.BreakConnection(outputBox);
|
||||
disconnectBoxesAction.End();
|
||||
|
||||
var addConnectionsAction = new EditNodeConnections(Context, rerouteNode);
|
||||
outputBox.CreateConnection(rerouteNode.GetBoxes().First(b => !b.IsOutput));
|
||||
rerouteNode.GetBoxes().First(b => b.IsOutput).CreateConnection(inputBox);
|
||||
addConnectionsAction.End();
|
||||
|
||||
|
||||
Undo.AddAction(new MultiUndoAction(spawnNodeAction, disconnectBoxesAction, addConnectionsAction));
|
||||
}
|
||||
else
|
||||
{
|
||||
var rerouteNode = Context.SpawnNode(7, 29, mousePos);
|
||||
inputBox.BreakConnection(outputBox);
|
||||
outputBox.CreateConnection(rerouteNode.GetBoxes().First(b => !b.IsOutput));
|
||||
rerouteNode.GetBoxes().First(b => b.IsOutput).CreateConnection(inputBox);
|
||||
}
|
||||
MarkAsEdited();
|
||||
|
||||
handled = true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
return handled;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -527,6 +562,81 @@ namespace FlaxEditor.Surface
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (key == KeyboardKeys.ArrowUp || key == KeyboardKeys.ArrowDown)
|
||||
{
|
||||
Box selectedBox = GetSelectedBox(SelectedNodes);
|
||||
if (selectedBox == null) return true;
|
||||
|
||||
Box toSelect = (key == KeyboardKeys.ArrowUp) ?
|
||||
selectedBox?.ParentNode.GetPreviousBox(selectedBox) :
|
||||
selectedBox?.ParentNode.GetNextBox(selectedBox);
|
||||
|
||||
if (toSelect != null && toSelect.IsOutput == selectedBox.IsOutput)
|
||||
{
|
||||
Select(toSelect.ParentNode);
|
||||
toSelect.ParentNode.SelectBox(toSelect);
|
||||
}
|
||||
}
|
||||
|
||||
if (key == KeyboardKeys.Tab)
|
||||
{
|
||||
Box selectedBox = GetSelectedBox(SelectedNodes);
|
||||
if (selectedBox == null) return true;
|
||||
|
||||
int connectionCount = selectedBox.Connections.Count;
|
||||
if (connectionCount == 0) return true;
|
||||
|
||||
if (Root.GetKey(KeyboardKeys.Shift))
|
||||
{
|
||||
_selectedConnectionIndex = ((_selectedConnectionIndex - 1) % connectionCount + connectionCount) % connectionCount;
|
||||
}
|
||||
else
|
||||
{
|
||||
_selectedConnectionIndex = (_selectedConnectionIndex + 1) % connectionCount;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (key == KeyboardKeys.ArrowRight || key == KeyboardKeys.ArrowLeft)
|
||||
{
|
||||
Box selectedBox = GetSelectedBox(SelectedNodes);
|
||||
if (selectedBox == null) return true;
|
||||
|
||||
Box toSelect = null;
|
||||
|
||||
if ((key == KeyboardKeys.ArrowRight && selectedBox.IsOutput) || (key == KeyboardKeys.ArrowLeft && !selectedBox.IsOutput))
|
||||
{
|
||||
if (_selectedConnectionIndex < 0 || _selectedConnectionIndex >= selectedBox.Connections.Count)
|
||||
{
|
||||
_selectedConnectionIndex = 0;
|
||||
}
|
||||
toSelect = selectedBox.Connections[_selectedConnectionIndex];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Use the node with the closest Y-level
|
||||
// Since there are cases like 3 nodes on one side and only 1 node on the other side
|
||||
|
||||
var elements = selectedBox.ParentNode.Elements;
|
||||
float minDistance = float.PositiveInfinity;
|
||||
for (int i = 0; i < elements.Count; i++)
|
||||
{
|
||||
if (elements[i] is Box box && box.IsOutput != selectedBox.IsOutput && Mathf.Abs(box.Y - selectedBox.Y) < minDistance)
|
||||
{
|
||||
toSelect = box;
|
||||
minDistance = Mathf.Abs(box.Y - selectedBox.Y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (toSelect != null)
|
||||
{
|
||||
Select(toSelect.ParentNode);
|
||||
toSelect.ParentNode.SelectBox(toSelect);
|
||||
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -720,5 +830,31 @@ namespace FlaxEditor.Surface
|
||||
yLocation
|
||||
);
|
||||
}
|
||||
|
||||
private bool IntersectsConnection(Vector2 mousePosition, out InputBox inputBox, out OutputBox outputBox)
|
||||
{
|
||||
for (int i = 0; i < Nodes.Count; i++)
|
||||
{
|
||||
for (int j = 0; j < Nodes[i].Elements.Count; j++)
|
||||
{
|
||||
if (Nodes[i].Elements[j] is OutputBox ob)
|
||||
{
|
||||
for (int k = 0; k < ob.Connections.Count; k++)
|
||||
{
|
||||
if (ob.IntersectsConnection(ob.Connections[k], ref mousePosition))
|
||||
{
|
||||
outputBox = ob;
|
||||
inputBox = ob.Connections[k] as InputBox;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
outputBox = null;
|
||||
inputBox = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,6 +39,7 @@ namespace FlaxEditor.Surface
|
||||
private GroupArchetype _customNodesGroup;
|
||||
private List<NodeArchetype> _customNodes;
|
||||
private Action _onSave;
|
||||
private int _selectedConnectionIndex;
|
||||
|
||||
internal int _isUpdatingBoxTypes;
|
||||
|
||||
@@ -362,6 +363,8 @@ namespace FlaxEditor.Surface
|
||||
Context.ControlSpawned += OnSurfaceControlSpawned;
|
||||
Context.ControlDeleted += OnSurfaceControlDeleted;
|
||||
|
||||
SelectionChanged += () => { _selectedConnectionIndex = 0; };
|
||||
|
||||
// Init drag handlers
|
||||
DragHandlers.Add(_dragAssets = new DragAssets<DragDropEventArgs>(ValidateDragItem));
|
||||
DragHandlers.Add(_dragParameters = new DragNames<DragDropEventArgs>(SurfaceParameter.DragPrefix, ValidateDragParameter));
|
||||
|
||||
@@ -27,6 +27,7 @@ const Char* SplashScreenQuotes[] =
|
||||
TEXT("Removing 'C:\\Windows\\'"),
|
||||
#elif PLATFORM_LINUX
|
||||
TEXT("Time to switch to Windows?"),
|
||||
TEXT("Installing Windows 10..."),
|
||||
#endif
|
||||
TEXT("Kappa!"),
|
||||
TEXT("How you doin'?"),
|
||||
@@ -116,6 +117,9 @@ const Char* SplashScreenQuotes[] =
|
||||
TEXT("Compiling Shaders (93,788)"),
|
||||
TEXT("Hi There"),
|
||||
TEXT("BAGUETTE"),
|
||||
TEXT("All we had to do was follow the damn train, CJ"),
|
||||
TEXT("28 stab wounds"),
|
||||
TEXT("Here we go again"),
|
||||
};
|
||||
|
||||
SplashScreen::~SplashScreen()
|
||||
|
||||
Reference in New Issue
Block a user