Merge branch 'master' into add-content-tree-view
This commit is contained in:
@@ -5,7 +5,7 @@ body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Thanks for taking the time to fill out this bug report! Please attach any minimal reproduction projects!
|
||||
Thanks for taking the time to fill out this bug report! Please attach a minimal reproduction project if available!
|
||||
- type: textarea
|
||||
id: description-area
|
||||
attributes:
|
||||
@@ -17,19 +17,19 @@ body:
|
||||
id: steps-area
|
||||
attributes:
|
||||
label: Steps to reproduce
|
||||
description: Please provide reproduction steps if possible.
|
||||
description: Please provide reproduction steps if available.
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
id: version
|
||||
attributes:
|
||||
label: Version
|
||||
description: What version of Flax are you running?
|
||||
description: What version of Flax did you experience the bug in?
|
||||
options:
|
||||
- '1.8'
|
||||
- '1.9'
|
||||
- '1.10'
|
||||
- '1.11'
|
||||
- '1.12'
|
||||
- master branch
|
||||
default: 3
|
||||
validations:
|
||||
|
||||
@@ -5,7 +5,7 @@ body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Thanks for taking the time to fill out a feature request!
|
||||
Thank you for taking the time to submit this feature request!
|
||||
- type: textarea
|
||||
id: description-area
|
||||
attributes:
|
||||
@@ -17,6 +17,6 @@ body:
|
||||
id: benefits-area
|
||||
attributes:
|
||||
label: Benefits
|
||||
description: Please provide what benefits this feature would provide to the engine!
|
||||
description: Please list what benefits this feature would provide to the engine!
|
||||
validations:
|
||||
required: true
|
||||
@@ -4,6 +4,7 @@ on: [push, pull_request]
|
||||
env:
|
||||
DOTNET_NOLOGO: true
|
||||
DOTNET_CLI_TELEMETRY_OPTOUT: false
|
||||
DOTNET_ROLL_FORWARD: 'minor'
|
||||
|
||||
jobs:
|
||||
|
||||
@@ -19,7 +20,7 @@ jobs:
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v3
|
||||
with:
|
||||
dotnet-version: 8.0.x
|
||||
dotnet-version: 8.0.419
|
||||
- name: Setup .NET Workload
|
||||
run: |
|
||||
dotnet workload install android
|
||||
@@ -33,4 +34,7 @@ jobs:
|
||||
git lfs pull
|
||||
- name: Build
|
||||
run: |
|
||||
PowerShell "(Get-Content global.json).Replace('latestMajor', 'minor') | Set-Content global.json"
|
||||
PowerShell "(Get-Content Source/Tools/Flax.Build/global.json).Replace('latestMajor', 'minor') | Set-Content Source/Tools/Flax.Build/global.json"
|
||||
PowerShell "(Get-Content Source/Tools/Flax.Build/Flax.Build.csproj).Replace('LatestMajor', 'Minor') | Set-Content Source/Tools/Flax.Build/Flax.Build.csproj"
|
||||
.\Development\Scripts\Windows\CallBuildTool.bat -build -log -printSDKs -dotnet=8 -arch=ARM64 -platform=Android -configuration=Release -buildtargets=FlaxGame
|
||||
|
||||
@@ -4,6 +4,7 @@ on: [push, pull_request]
|
||||
env:
|
||||
DOTNET_NOLOGO: true
|
||||
DOTNET_CLI_TELEMETRY_OPTOUT: false
|
||||
DOTNET_ROLL_FORWARD: 'minor'
|
||||
|
||||
jobs:
|
||||
|
||||
@@ -19,7 +20,7 @@ jobs:
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v3
|
||||
with:
|
||||
dotnet-version: 8.0.x
|
||||
dotnet-version: 8.0.419
|
||||
- name: Print .NET info
|
||||
run: |
|
||||
dotnet --info
|
||||
@@ -30,6 +31,9 @@ jobs:
|
||||
git lfs pull
|
||||
- name: Build
|
||||
run: |
|
||||
PowerShell "(Get-Content global.json).Replace('latestMajor', 'minor') | Set-Content global.json"
|
||||
PowerShell "(Get-Content Source/Tools/Flax.Build/global.json).Replace('latestMajor', 'minor') | Set-Content Source/Tools/Flax.Build/global.json"
|
||||
PowerShell "(Get-Content Source/Tools/Flax.Build/Flax.Build.csproj).Replace('LatestMajor', 'Minor') | Set-Content Source/Tools/Flax.Build/Flax.Build.csproj"
|
||||
.\Development\Scripts\Windows\CallBuildTool.bat -build -log -printSDKs -dotnet=8 -arch=x64 -platform=Windows -configuration=Development -buildtargets=FlaxEditor
|
||||
|
||||
# Game
|
||||
@@ -44,7 +48,7 @@ jobs:
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v3
|
||||
with:
|
||||
dotnet-version: 8.0.x
|
||||
dotnet-version: 8.0.419
|
||||
- name: Print .NET info
|
||||
run: |
|
||||
dotnet --info
|
||||
@@ -55,4 +59,7 @@ jobs:
|
||||
git lfs pull
|
||||
- name: Build
|
||||
run: |
|
||||
PowerShell "(Get-Content global.json).Replace('latestMajor', 'minor') | Set-Content global.json"
|
||||
PowerShell "(Get-Content Source/Tools/Flax.Build/global.json).Replace('latestMajor', 'minor') | Set-Content Source/Tools/Flax.Build/global.json"
|
||||
PowerShell "(Get-Content Source/Tools/Flax.Build/Flax.Build.csproj).Replace('LatestMajor', 'Minor') | Set-Content Source/Tools/Flax.Build/Flax.Build.csproj"
|
||||
.\Development\Scripts\Windows\CallBuildTool.bat -build -log -printSDKs -dotnet=8 -arch=x64 -platform=Windows -configuration=Release -buildtargets=FlaxGame
|
||||
|
||||
@@ -7,6 +7,7 @@ on:
|
||||
env:
|
||||
DOTNET_NOLOGO: true
|
||||
DOTNET_CLI_TELEMETRY_OPTOUT: false
|
||||
DOTNET_ROLL_FORWARD: 'minor'
|
||||
GIT_LFS_PULL_OPTIONS: '-c lfs.concurrenttransfers=1 -c lfs.transfer.maxretries=2 -c http.version="HTTP/1.1" -c lfs.activitytimeout=60'
|
||||
|
||||
jobs:
|
||||
@@ -27,13 +28,16 @@ jobs:
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v3
|
||||
with:
|
||||
dotnet-version: 8.0.x
|
||||
dotnet-version: 8.0.419
|
||||
- name: Print .NET info
|
||||
run: |
|
||||
dotnet --info
|
||||
dotnet workload --info
|
||||
- name: Build
|
||||
run: |
|
||||
PowerShell "(Get-Content global.json).Replace('latestMajor', 'minor') | Set-Content global.json"
|
||||
PowerShell "(Get-Content Source/Tools/Flax.Build/global.json).Replace('latestMajor', 'minor') | Set-Content Source/Tools/Flax.Build/global.json"
|
||||
PowerShell "(Get-Content Source/Tools/Flax.Build/Flax.Build.csproj).Replace('LatestMajor', 'Minor') | Set-Content Source/Tools/Flax.Build/Flax.Build.csproj"
|
||||
.\PackageEditor.bat -arch=x64 -platform=Windows -deployOutput=Output -dotnet=8
|
||||
- name: Upload
|
||||
uses: actions/upload-artifact@v4
|
||||
@@ -60,13 +64,16 @@ jobs:
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v3
|
||||
with:
|
||||
dotnet-version: 8.0.x
|
||||
dotnet-version: 8.0.419
|
||||
- name: Print .NET info
|
||||
run: |
|
||||
dotnet --info
|
||||
dotnet workload --info
|
||||
- name: Build
|
||||
run: |
|
||||
PowerShell "(Get-Content global.json).Replace('latestMajor', 'minor') | Set-Content global.json"
|
||||
PowerShell "(Get-Content Source/Tools/Flax.Build/global.json).Replace('latestMajor', 'minor') | Set-Content Source/Tools/Flax.Build/global.json"
|
||||
PowerShell "(Get-Content Source/Tools/Flax.Build/Flax.Build.csproj).Replace('LatestMajor', 'Minor') | Set-Content Source/Tools/Flax.Build/Flax.Build.csproj"
|
||||
.\PackagePlatforms.bat -arch=x64 -platform=Windows -deployOutput=Output -dotnet=8
|
||||
- name: Upload
|
||||
uses: actions/upload-artifact@v4
|
||||
|
||||
@@ -4,6 +4,7 @@ on: [push, pull_request]
|
||||
env:
|
||||
DOTNET_NOLOGO: true
|
||||
DOTNET_CLI_TELEMETRY_OPTOUT: false
|
||||
DOTNET_ROLL_FORWARD: 'minor'
|
||||
|
||||
jobs:
|
||||
|
||||
@@ -59,7 +60,7 @@ jobs:
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v3
|
||||
with:
|
||||
dotnet-version: 8.0.x
|
||||
dotnet-version: 8.0.419
|
||||
- name: Print .NET info
|
||||
run: |
|
||||
dotnet --info
|
||||
@@ -70,6 +71,9 @@ jobs:
|
||||
git lfs pull
|
||||
- name: Build
|
||||
run: |
|
||||
PowerShell "(Get-Content global.json).Replace('latestMajor', 'minor') | Set-Content global.json"
|
||||
PowerShell "(Get-Content Source/Tools/Flax.Build/global.json).Replace('latestMajor', 'minor') | Set-Content Source/Tools/Flax.Build/global.json"
|
||||
PowerShell "(Get-Content Source/Tools/Flax.Build/Flax.Build.csproj).Replace('LatestMajor', 'Minor') | Set-Content Source/Tools/Flax.Build/Flax.Build.csproj"
|
||||
.\GenerateProjectFiles.bat -vs2022 -log -verbose -printSDKs -dotnet=8
|
||||
.\Development\Scripts\Windows\CallBuildTool.bat -build -log -dotnet=8 -arch=x64 -platform=Windows -configuration=Development -buildtargets=FlaxTestsTarget
|
||||
dotnet msbuild Source\Tools\Flax.Build.Tests\Flax.Build.Tests.csproj /m /t:Restore,Build /p:Configuration=Debug /p:Platform=AnyCPU /nologo
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
+1
-1
@@ -4,7 +4,7 @@
|
||||
"Major": 1,
|
||||
"Minor": 11,
|
||||
"Revision": 0,
|
||||
"Build": 6807
|
||||
"Build": 6809
|
||||
},
|
||||
"Company": "Flax",
|
||||
"Copyright": "Copyright (c) 2012-2026 Wojciech Figat. All rights reserved.",
|
||||
|
||||
@@ -184,15 +184,13 @@ public class ContentFolderTreeNode : TreeNode
|
||||
}
|
||||
}
|
||||
|
||||
bool isExpanded = isAnyChildVisible;
|
||||
|
||||
if (isExpanded)
|
||||
if (!noFilter)
|
||||
{
|
||||
Expand(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
Collapse(true);
|
||||
bool isExpanded = isAnyChildVisible;
|
||||
if (isExpanded)
|
||||
Expand(true);
|
||||
else
|
||||
Collapse(true);
|
||||
}
|
||||
|
||||
Visible = isThisVisible | isAnyChildVisible;
|
||||
|
||||
@@ -52,6 +52,7 @@ namespace FlaxEditor.CustomEditors
|
||||
private readonly List<CustomEditor> _children = new List<CustomEditor>();
|
||||
private ValueContainer _values;
|
||||
private bool _isSetBlocked;
|
||||
private bool _isRebuilding;
|
||||
private bool _skipChildrenRefresh;
|
||||
private bool _hasValueDirty;
|
||||
private bool _rebuildOnRefresh;
|
||||
@@ -178,7 +179,7 @@ namespace FlaxEditor.CustomEditors
|
||||
public void RebuildLayout()
|
||||
{
|
||||
// Skip rebuilding during init
|
||||
if (CurrentCustomEditor == this)
|
||||
if (CurrentCustomEditor == this || _isRebuilding)
|
||||
return;
|
||||
|
||||
// Special case for root objects to run normal layout build
|
||||
@@ -197,6 +198,7 @@ namespace FlaxEditor.CustomEditors
|
||||
_parent?.RebuildLayout();
|
||||
return;
|
||||
}
|
||||
_isRebuilding = true;
|
||||
var control = layout.ContainerControl;
|
||||
var parent = _parent;
|
||||
var parentScrollV = (_presenter?.Panel.Parent as Panel)?.VScrollBar?.Value ?? -1;
|
||||
@@ -216,6 +218,7 @@ namespace FlaxEditor.CustomEditors
|
||||
// Restore scroll value
|
||||
if (parentScrollV > -1 && _presenter != null && _presenter.Panel.Parent is Panel panel && panel.VScrollBar != null)
|
||||
panel.VScrollBar.Value = parentScrollV;
|
||||
_isRebuilding = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -103,11 +103,12 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
var actors = ScriptsEditor.ParentEditor.Values;
|
||||
foreach (var a in actors)
|
||||
{
|
||||
if (a.GetType() != requireActor.RequiredType)
|
||||
{
|
||||
item.Enabled = false;
|
||||
break;
|
||||
}
|
||||
if (a.GetType() == requireActor.RequiredType)
|
||||
continue;
|
||||
if (requireActor.IncludeInheritedTypes && a.GetType().IsSubclassOf(requireActor.RequiredType))
|
||||
continue;
|
||||
item.Enabled = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
cm.AddItem(item);
|
||||
@@ -739,6 +740,8 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
/// <inheritdoc />
|
||||
public override void Initialize(LayoutElementsContainer layout)
|
||||
{
|
||||
var style = FlaxEngine.GUI.Style.Current;
|
||||
|
||||
// Area for drag&drop scripts
|
||||
var dragArea = layout.CustomContainer<DragAreaControl>();
|
||||
dragArea.CustomControl.ScriptsEditor = this;
|
||||
@@ -800,17 +803,10 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
bool hasAllRequirements = true;
|
||||
if (scriptType.HasAttribute(typeof(RequireScriptAttribute), false))
|
||||
{
|
||||
RequireScriptAttribute scriptAttribute = null;
|
||||
foreach (var e in scriptType.GetAttributes(false))
|
||||
var attribute = (RequireScriptAttribute)scriptType.GetAttributes(false).FirstOrDefault(x => x is RequireScriptAttribute);
|
||||
if (attribute != null)
|
||||
{
|
||||
if (e is not RequireScriptAttribute requireScriptAttribute)
|
||||
continue;
|
||||
scriptAttribute = requireScriptAttribute;
|
||||
}
|
||||
|
||||
if (scriptAttribute != null)
|
||||
{
|
||||
foreach (var type in scriptAttribute.RequiredTypes)
|
||||
foreach (var type in attribute.RequiredTypes)
|
||||
{
|
||||
if (!type.IsSubclassOf(typeof(Script)))
|
||||
continue;
|
||||
@@ -825,19 +821,11 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
}
|
||||
if (scriptType.HasAttribute(typeof(RequireActorAttribute), false))
|
||||
{
|
||||
RequireActorAttribute attribute = null;
|
||||
foreach (var e in scriptType.GetAttributes(false))
|
||||
{
|
||||
if (e is not RequireActorAttribute requireActorAttribute)
|
||||
continue;
|
||||
attribute = requireActorAttribute;
|
||||
break;
|
||||
}
|
||||
|
||||
var attribute = (RequireActorAttribute)scriptType.GetAttributes(false).FirstOrDefault(x => x is RequireActorAttribute);
|
||||
if (attribute != null)
|
||||
{
|
||||
var actor = script.Actor;
|
||||
if (actor.GetType() != attribute.RequiredType && !actor.GetType().IsSubclassOf(attribute.RequiredType))
|
||||
if (actor.GetType() != attribute.RequiredType && (attribute.IncludeInheritedTypes && !actor.GetType().IsSubclassOf(attribute.RequiredType)))
|
||||
{
|
||||
Editor.LogWarning($"`{Utilities.Utils.GetPropertyNameUI(scriptType.Name)}` on `{script.Actor}` is missing a required Actor of type `{attribute.RequiredType}`.");
|
||||
hasAllRequirements = false;
|
||||
@@ -850,7 +838,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
var title = Utilities.Utils.GetPropertyNameUI(scriptType.Name);
|
||||
var group = layout.Group(title, editor);
|
||||
if (!hasAllRequirements)
|
||||
group.Panel.HeaderTextColor = FlaxEngine.GUI.Style.Current.Statusbar.Failed;
|
||||
group.Panel.HeaderTextColor = style.Statusbar.Failed;
|
||||
if ((Presenter.Features & FeatureFlags.CacheExpandedGroups) != 0)
|
||||
{
|
||||
if (Editor.Instance.ProjectCache.IsGroupToggled(title))
|
||||
@@ -863,9 +851,10 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
group.Panel.Open();
|
||||
|
||||
// Customize
|
||||
float totalHeaderButtonsOffset = 0f;
|
||||
group.Panel.TooltipText = Editor.Instance.CodeDocs.GetTooltip(scriptType);
|
||||
if (script.HasPrefabLink)
|
||||
group.Panel.HeaderTextColor = FlaxEngine.GUI.Style.Current.ProgressNormal;
|
||||
group.Panel.HeaderTextColor = style.ProgressNormal;
|
||||
|
||||
// Add toggle button to the group
|
||||
var headerHeight = group.Panel.HeaderHeight;
|
||||
@@ -889,7 +878,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
TooltipText = "Script reference.",
|
||||
AutoFocus = true,
|
||||
IsScrollable = false,
|
||||
Color = FlaxEngine.GUI.Style.Current.ForegroundGrey,
|
||||
Color = style.ForegroundGrey,
|
||||
Parent = group.Panel,
|
||||
Bounds = new Rectangle(scriptToggle.Right, 0.5f, headerHeight, headerHeight),
|
||||
Margin = new Margin(1),
|
||||
@@ -908,10 +897,35 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
var settingsButton = group.AddSettingsButton();
|
||||
settingsButton.Tag = script;
|
||||
settingsButton.Clicked += OnSettingsButtonClicked;
|
||||
totalHeaderButtonsOffset += settingsButton.Width + FlaxEditor.Utilities.Constants.UIMargin;
|
||||
|
||||
// Add script obsolete icon to the group
|
||||
if (scriptType.HasAttribute(typeof(ObsoleteAttribute), false))
|
||||
{
|
||||
var attribute = (ObsoleteAttribute)scriptType.GetAttributes(false).First(x => x is ObsoleteAttribute);
|
||||
var tooltip = "Script marked as obsolete." +
|
||||
(string.IsNullOrEmpty(attribute.Message) ? "" : $"\n{attribute.Message}") +
|
||||
(string.IsNullOrEmpty(attribute.DiagnosticId) ? "" : $"\n{attribute.DiagnosticId}");
|
||||
var obsoleteButton = group.AddHeaderButton(tooltip, totalHeaderButtonsOffset, Editor.Instance.Icons.Info32);
|
||||
obsoleteButton.Color = Color.Orange;
|
||||
obsoleteButton.MouseOverColor = Color.DarkOrange;
|
||||
totalHeaderButtonsOffset += obsoleteButton.Width;
|
||||
}
|
||||
|
||||
// Show visual indicator if script only exists in prefab instance and is not part of the prefab
|
||||
bool isPrefabActor = scripts.Any(s => s.Actor.HasPrefabLink);
|
||||
if (isPrefabActor && script.PrefabID == Guid.Empty)
|
||||
{
|
||||
var prefabInstanceButton = group.AddHeaderButton("Script only exists in this prefab instance.", totalHeaderButtonsOffset, Editor.Instance.Icons.Add32);
|
||||
prefabInstanceButton.Color = style.ProgressNormal;
|
||||
prefabInstanceButton.MouseOverColor = style.ProgressNormal * 0.9f;
|
||||
totalHeaderButtonsOffset += prefabInstanceButton.Width;
|
||||
}
|
||||
|
||||
// Adjust margin to not overlap with other ui elements in the header
|
||||
group.Panel.HeaderTextMargin = group.Panel.HeaderTextMargin with { Left = scriptDrag.Right - 12, Right = settingsButton.Width + Utilities.Constants.UIMargin };
|
||||
group.Object(values, editor);
|
||||
|
||||
// Remove drop down arrows and containment lines if no objects in the group
|
||||
if (group.Children.Count == 0)
|
||||
{
|
||||
|
||||
@@ -18,7 +18,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
public class SplineEditor : ActorEditor
|
||||
{
|
||||
/// <summary>
|
||||
/// Storage undo spline data
|
||||
/// Stores undo spline data.
|
||||
/// </summary>
|
||||
private struct UndoData
|
||||
{
|
||||
@@ -83,7 +83,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Edit curve options manipulate the curve as free mode
|
||||
/// Edit curve options manipulate the curve as free mode.
|
||||
/// </summary>
|
||||
private sealed class FreeTangentMode : EditTangentOptionBase
|
||||
{
|
||||
@@ -98,7 +98,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Edit curve options to set tangents to linear
|
||||
/// Edit curve options to set tangents to linear.
|
||||
/// </summary>
|
||||
private sealed class LinearTangentMode : EditTangentOptionBase
|
||||
{
|
||||
@@ -107,13 +107,13 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
{
|
||||
SetKeyframeLinear(spline, index);
|
||||
|
||||
// change the selection to tangent parent (a spline point / keyframe)
|
||||
// Change the selection to tangent parent (a spline point / keyframe)
|
||||
Editor.SetSelectSplinePointNode(spline, index);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Edit curve options to align tangents of selected spline
|
||||
/// Edit curve options to align tangents of selected spline.
|
||||
/// </summary>
|
||||
private sealed class AlignedTangentMode : EditTangentOptionBase
|
||||
{
|
||||
@@ -168,8 +168,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Edit curve options manipulate the curve setting selected point
|
||||
/// tangent in as smoothed but tangent out as linear
|
||||
/// Edit curve options manipulate the curve setting selected point tangent in as smoothed but tangent out as linear.
|
||||
/// </summary>
|
||||
private sealed class SmoothInTangentMode : EditTangentOptionBase
|
||||
{
|
||||
@@ -182,8 +181,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Edit curve options manipulate the curve setting selected point
|
||||
/// tangent in as linear but tangent out as smoothed
|
||||
/// Edit curve options manipulate the curve setting selected point tangent in as linear but tangent out as smoothed.
|
||||
/// </summary>
|
||||
private sealed class SmoothOutTangentMode : EditTangentOptionBase
|
||||
{
|
||||
@@ -219,7 +217,8 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
var enabled = EnabledInHierarchy && tab.EnabledInHierarchy;
|
||||
var style = FlaxEngine.GUI.Style.Current;
|
||||
var size = Size;
|
||||
var textHeight = 16.0f;
|
||||
var textHeight = 30.0f;
|
||||
// Make icons smaller when tabs get thinner
|
||||
var iconSize = size.Y - textHeight;
|
||||
var iconRect = new Rectangle((Width - iconSize) / 2, 0, iconSize, iconSize);
|
||||
if (tab._mirrorIcon)
|
||||
@@ -230,8 +229,11 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
var color = style.Foreground;
|
||||
if (!enabled)
|
||||
color *= 0.6f;
|
||||
var textRect = new Rectangle(0, size.Y - textHeight, size.X, textHeight);
|
||||
Render2D.PushClip(new Rectangle(Float2.Zero, Size));
|
||||
Render2D.DrawSprite(tab._customIcon, iconRect, color);
|
||||
Render2D.DrawText(style.FontMedium, tab._customText, new Rectangle(0, iconSize, size.X, textHeight), color, TextAlignment.Center, TextAlignment.Center);
|
||||
Render2D.DrawText(style.FontMedium, tab._customText, textRect, color, TextAlignment.Center, TextAlignment.Center, TextWrapping.WrapWords, 0.65f);
|
||||
Render2D.PopClip();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -291,18 +293,21 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
return;
|
||||
_selectedSpline = spline;
|
||||
|
||||
layout.Space(10);
|
||||
var tabSize = 46;
|
||||
//var tabSize = 46;
|
||||
var tabSize = 60;
|
||||
var icons = Editor.Instance.Icons;
|
||||
|
||||
layout.Header("Selected spline point");
|
||||
var group = layout.Group("Selected Point");
|
||||
_selectedPointsTabs = new Tabs
|
||||
{
|
||||
Height = tabSize,
|
||||
TabsSize = new Float2(tabSize),
|
||||
AutoTabsSize = true,
|
||||
Parent = layout.ContainerControl,
|
||||
Parent = group.ContainerControl,
|
||||
};
|
||||
// Move the group above the group containing spline points
|
||||
group.Control.IndexInParent = 3;
|
||||
|
||||
_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));
|
||||
@@ -310,13 +315,13 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
_smoothOutTangentTab = _selectedPointsTabs.AddTab(new IconTab(OnSetSelectedSmoothOut, "Smooth Out", icons.SplineSmoothIn64, true));
|
||||
_selectedPointsTabs.SelectedTabIndex = -1;
|
||||
|
||||
layout.Header("All spline points");
|
||||
group = layout.Group("All Points");
|
||||
_allPointsTabs = new Tabs
|
||||
{
|
||||
Height = tabSize,
|
||||
TabsSize = new Float2(tabSize),
|
||||
AutoTabsSize = true,
|
||||
Parent = layout.ContainerControl,
|
||||
Parent = group.ContainerControl,
|
||||
};
|
||||
_setLinearAllTangentsTab = _allPointsTabs.AddTab(new IconTab(OnSetTangentsLinear, "Set Linear Tangents", icons.SplineLinear64));
|
||||
_setSmoothAllTangentsTab = _allPointsTabs.AddTab(new IconTab(OnSetTangentsSmooth, "Set Smooth Tangents", icons.SplineAligned64));
|
||||
|
||||
@@ -690,7 +690,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
return grid;
|
||||
}
|
||||
|
||||
private CustomElementsContainer<UniformGridPanel> UniformPanelCapsuleForObjectWithText(LayoutElementsContainer el, string text, ValueContainer values, Color borderColor, out FloatValueBox valueBox)
|
||||
private CustomElementsContainer<UniformGridPanel> UniformPanelCapsuleForObjectWithText(LayoutElementsContainer el, string text, ValueContainer values, Color highlightColor, out FloatValueBox valueBox)
|
||||
{
|
||||
valueBox = null;
|
||||
var grid = UniformGridTwoByOne(el);
|
||||
@@ -701,8 +701,8 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
{
|
||||
valueBox = floatEditorElement.ValueBox;
|
||||
var back = FlaxEngine.GUI.Style.Current.TextBoxBackground;
|
||||
valueBox.BorderColor = Color.Lerp(borderColor, back, ActorTransformEditor.AxisGreyOutFactor);
|
||||
valueBox.BorderSelectedColor = borderColor;
|
||||
valueBox.HighlightColor = highlightColor;
|
||||
valueBox.BorderSelectedColor = highlightColor;
|
||||
}
|
||||
return grid;
|
||||
}
|
||||
|
||||
@@ -14,22 +14,17 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
/// <summary>
|
||||
/// The X axis color.
|
||||
/// </summary>
|
||||
public static Color AxisColorX = new Color(1.0f, 0.0f, 0.02745f, 1.0f);
|
||||
public static Color AxisColorX = new Color(0.8f, 0.0f, 0.027f, 1.0f);
|
||||
|
||||
/// <summary>
|
||||
/// The Y axis color.
|
||||
/// </summary>
|
||||
public static Color AxisColorY = new Color(0.239215f, 1.0f, 0.047058f, 1.0f);
|
||||
public static Color AxisColorY = new Color(0.239215f, 0.65f, 0.047058f, 1.0f);
|
||||
|
||||
/// <summary>
|
||||
/// The Z axis color.
|
||||
/// </summary>
|
||||
public static Color AxisColorZ = new Color(0.0f, 0.0235294f, 1.0f, 1.0f);
|
||||
|
||||
/// <summary>
|
||||
/// The axes colors grey out scale when input field is not focused.
|
||||
/// </summary>
|
||||
public static float AxisGreyOutFactor = 0.6f;
|
||||
public static Color AxisColorZ = new Color(0.0f, 0.42352f, 0.8f, 1.0f);
|
||||
|
||||
/// <summary>
|
||||
/// Custom editor for actor position property.
|
||||
@@ -43,18 +38,20 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
base.Initialize(layout);
|
||||
|
||||
if (XElement.ValueBox.Parent is UniformGridPanel ug)
|
||||
{
|
||||
ug.SlotPadding = new Margin(3.0f, 0.0f, 0.0f, 0.0f);
|
||||
CheckLayout(ug);
|
||||
}
|
||||
|
||||
// Override colors
|
||||
var back = FlaxEngine.GUI.Style.Current.TextBoxBackground;
|
||||
XElement.ValueBox.BorderColor = Color.Lerp(AxisColorX, back, AxisGreyOutFactor);
|
||||
XElement.ValueBox.BorderSelectedColor = AxisColorX;
|
||||
XElement.ValueBox.Category = Utils.ValueCategory.Distance;
|
||||
YElement.ValueBox.BorderColor = Color.Lerp(AxisColorY, back, AxisGreyOutFactor);
|
||||
YElement.ValueBox.BorderSelectedColor = AxisColorY;
|
||||
YElement.ValueBox.Category = Utils.ValueCategory.Distance;
|
||||
ZElement.ValueBox.BorderColor = Color.Lerp(AxisColorZ, back, AxisGreyOutFactor);
|
||||
ZElement.ValueBox.BorderSelectedColor = AxisColorZ;
|
||||
XElement.ValueBox.HighlightColor = AxisColorX;
|
||||
XElement.ValueBox.Category = Utils.ValueCategory.Distance;
|
||||
YElement.ValueBox.HighlightColor = AxisColorY;
|
||||
YElement.ValueBox.Category = Utils.ValueCategory.Distance;
|
||||
ZElement.ValueBox.HighlightColor = AxisColorZ;
|
||||
ZElement.ValueBox.Category = Utils.ValueCategory.Distance;
|
||||
}
|
||||
}
|
||||
@@ -71,18 +68,20 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
base.Initialize(layout);
|
||||
|
||||
if (XElement.ValueBox.Parent is UniformGridPanel ug)
|
||||
{
|
||||
ug.SlotPadding = new Margin(3.0f, 0.0f, 0.0f, 0.0f);
|
||||
CheckLayout(ug);
|
||||
}
|
||||
|
||||
// Override colors
|
||||
var back = FlaxEngine.GUI.Style.Current.TextBoxBackground;
|
||||
XElement.ValueBox.BorderColor = Color.Lerp(AxisColorX, back, AxisGreyOutFactor);
|
||||
XElement.ValueBox.BorderSelectedColor = AxisColorX;
|
||||
XElement.ValueBox.Category = Utils.ValueCategory.Angle;
|
||||
YElement.ValueBox.BorderColor = Color.Lerp(AxisColorY, back, AxisGreyOutFactor);
|
||||
YElement.ValueBox.BorderSelectedColor = AxisColorY;
|
||||
YElement.ValueBox.Category = Utils.ValueCategory.Angle;
|
||||
ZElement.ValueBox.BorderColor = Color.Lerp(AxisColorZ, back, AxisGreyOutFactor);
|
||||
ZElement.ValueBox.BorderSelectedColor = AxisColorZ;
|
||||
XElement.ValueBox.HighlightColor = AxisColorX;
|
||||
XElement.ValueBox.Category = Utils.ValueCategory.Angle;
|
||||
YElement.ValueBox.HighlightColor = AxisColorY;
|
||||
YElement.ValueBox.Category = Utils.ValueCategory.Angle;
|
||||
ZElement.ValueBox.HighlightColor = AxisColorZ;
|
||||
ZElement.ValueBox.Category = Utils.ValueCategory.Angle;
|
||||
}
|
||||
}
|
||||
@@ -129,17 +128,19 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
}
|
||||
|
||||
if (XElement.ValueBox.Parent is UniformGridPanel ug)
|
||||
{
|
||||
ug.SlotPadding = new Margin(3.0f, 0.0f, 0.0f, 0.0f);
|
||||
CheckLayout(ug);
|
||||
}
|
||||
|
||||
// Override colors
|
||||
var back = FlaxEngine.GUI.Style.Current.TextBoxBackground;
|
||||
var grayOutFactor = 0.6f;
|
||||
XElement.ValueBox.BorderColor = Color.Lerp(AxisColorX, back, grayOutFactor);
|
||||
XElement.ValueBox.BorderSelectedColor = AxisColorX;
|
||||
YElement.ValueBox.BorderColor = Color.Lerp(AxisColorY, back, grayOutFactor);
|
||||
YElement.ValueBox.BorderSelectedColor = AxisColorY;
|
||||
ZElement.ValueBox.BorderColor = Color.Lerp(AxisColorZ, back, grayOutFactor);
|
||||
ZElement.ValueBox.BorderSelectedColor = AxisColorZ;
|
||||
XElement.ValueBox.HighlightColor = AxisColorX;
|
||||
YElement.ValueBox.HighlightColor = AxisColorY;
|
||||
ZElement.ValueBox.HighlightColor = AxisColorZ;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -37,25 +37,35 @@ namespace FlaxEditor.CustomEditors.Elements
|
||||
public override ContainerControl ContainerControl => Panel;
|
||||
|
||||
/// <summary>
|
||||
/// Adds utility settings button to the group header.
|
||||
/// Add utility settings button to the group header.
|
||||
/// </summary>
|
||||
/// <returns>The created control.</returns>
|
||||
public Image AddSettingsButton()
|
||||
{
|
||||
return AddHeaderButton("Settings", 0, Style.Current.Settings);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a button to the group header.
|
||||
/// </summary>
|
||||
/// <returns>The created control.</returns>
|
||||
public Image AddHeaderButton(string tooltipText, float xOffset, SpriteHandle sprite)
|
||||
{
|
||||
var style = Style.Current;
|
||||
const float padding = 2.0f;
|
||||
var settingsButtonSize = Panel.HeaderHeight;
|
||||
Panel.HeaderTextMargin = Panel.HeaderTextMargin with { Right = settingsButtonSize + Utilities.Constants.UIMargin };
|
||||
; return new Image
|
||||
{
|
||||
TooltipText = "Settings",
|
||||
TooltipText = tooltipText,
|
||||
AutoFocus = true,
|
||||
AnchorPreset = AnchorPresets.TopRight,
|
||||
Parent = Panel,
|
||||
Bounds = new Rectangle(Panel.Width - settingsButtonSize, 0, settingsButtonSize, settingsButtonSize),
|
||||
Bounds = new Rectangle(Panel.Width - settingsButtonSize - xOffset, padding * 0.5f, settingsButtonSize - padding, settingsButtonSize - padding),
|
||||
IsScrollable = false,
|
||||
Color = style.ForegroundGrey,
|
||||
Margin = new Margin(1),
|
||||
Brush = new SpriteBrush(style.Settings),
|
||||
Brush = new SpriteBrush(sprite),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,9 +70,9 @@ namespace FlaxEditor.CustomEditors.GUI
|
||||
UpdateSplitRect();
|
||||
}
|
||||
|
||||
private void AutoSizeSplitter()
|
||||
private void AutoSizeSplitter(bool ignoreCustomSplitterValue = false)
|
||||
{
|
||||
if (_hasCustomSplitterValue || !Editor.Instance.Options.Options.Interface.AutoSizePropertiesPanelSplitter)
|
||||
if (_hasCustomSplitterValue && !ignoreCustomSplitterValue)
|
||||
return;
|
||||
|
||||
Font font = Style.Current.FontMedium;
|
||||
@@ -178,6 +178,21 @@ namespace FlaxEditor.CustomEditors.GUI
|
||||
return base.OnMouseDown(location, button);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool OnMouseDoubleClick(Float2 location, MouseButton button)
|
||||
{
|
||||
if (button == MouseButton.Left && _splitterRect.Contains(location))
|
||||
{
|
||||
if (_splitterClicked)
|
||||
EndTracking();
|
||||
|
||||
AutoSizeSplitter(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
return base.OnMouseDoubleClick(location, button);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool OnMouseUp(Float2 location, MouseButton button)
|
||||
{
|
||||
@@ -220,7 +235,8 @@ namespace FlaxEditor.CustomEditors.GUI
|
||||
// Refresh
|
||||
UpdateSplitRect();
|
||||
PerformLayout(true);
|
||||
AutoSizeSplitter();
|
||||
if (Editor.Instance.Options.Options.Interface.AutoSizePropertiesPanelSplitter)
|
||||
AutoSizeSplitter();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -61,6 +61,7 @@ public class Editor : EditorModule
|
||||
options.PrivateDependencies.Add("Renderer");
|
||||
options.PrivateDependencies.Add("TextureTool");
|
||||
options.PrivateDependencies.Add("Particles");
|
||||
options.PrivateDependencies.Add("Terrain");
|
||||
|
||||
var platformToolsRoot = Path.Combine(FolderPath, "Cooker", "Platform");
|
||||
var platformToolsRootExternal = Path.Combine(Globals.EngineRoot, "Source", "Platforms");
|
||||
|
||||
@@ -526,6 +526,23 @@ int32 Editor::LoadProduct()
|
||||
return 12;
|
||||
}
|
||||
|
||||
// Get the last opened project path
|
||||
String localCachePath;
|
||||
FileSystem::GetSpecialFolderPath(SpecialFolder::AppData, localCachePath);
|
||||
String editorConfigPath = localCachePath / TEXT("Flax");
|
||||
String lastProjectSettingPath = editorConfigPath / TEXT("LastProject.txt");
|
||||
if (!FileSystem::DirectoryExists(editorConfigPath))
|
||||
FileSystem::CreateDirectory(editorConfigPath);
|
||||
String lastProjectPath;
|
||||
if (FileSystem::FileExists(lastProjectSettingPath))
|
||||
File::ReadAllText(lastProjectSettingPath, lastProjectPath);
|
||||
if (!FileSystem::DirectoryExists(lastProjectPath))
|
||||
lastProjectPath = String::Empty;
|
||||
|
||||
// Try to open the last project when requested
|
||||
if (projectPath.IsEmpty() && CommandLine::Options.LastProject.IsTrue() && !lastProjectPath.IsEmpty())
|
||||
projectPath = lastProjectPath;
|
||||
|
||||
// Missing project case
|
||||
if (projectPath.IsEmpty())
|
||||
{
|
||||
@@ -541,7 +558,7 @@ int32 Editor::LoadProduct()
|
||||
Array<String> files;
|
||||
if (FileSystem::ShowOpenFileDialog(
|
||||
nullptr,
|
||||
StringView::Empty,
|
||||
lastProjectPath,
|
||||
TEXT("Project files (*.flaxproj)\0*.flaxproj\0All files (*.*)\0*.*\0"),
|
||||
false,
|
||||
TEXT("Select project to open in Editor"),
|
||||
@@ -625,6 +642,10 @@ int32 Editor::LoadProduct()
|
||||
}
|
||||
}
|
||||
|
||||
// Update the last opened project path
|
||||
if (lastProjectPath.Compare(Project->ProjectFolderPath) != 0)
|
||||
File::WriteAllText(lastProjectSettingPath, Project->ProjectFolderPath, Encoding::UTF8);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -69,6 +69,11 @@ namespace FlaxEditor
|
||||
/// </summary>
|
||||
public static string WindowIcon = "Editor/EditorIcon";
|
||||
|
||||
/// <summary>
|
||||
/// The material used for the HS color wheel.
|
||||
/// </summary>
|
||||
public static string HSWheelMaterial = "Editor/HSWheel";
|
||||
|
||||
/// <summary>
|
||||
/// The window icons font.
|
||||
/// </summary>
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using FlaxEditor.GUI.Input;
|
||||
using FlaxEditor.GUI.Tabs;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
using FlaxEngine.Json;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace FlaxEditor.GUI.Dialogs
|
||||
{
|
||||
@@ -25,15 +26,16 @@ namespace FlaxEditor.GUI.Dialogs
|
||||
/// <seealso cref="FlaxEditor.GUI.Dialogs.Dialog" />
|
||||
public class ColorPickerDialog : Dialog, IColorPickerDialog
|
||||
{
|
||||
private const float ButtonsWidth = 60.0f;
|
||||
private const float PickerMargin = 6.0f;
|
||||
private const float EyedropperMargin = 8.0f;
|
||||
private const float RGBAMargin = 12.0f;
|
||||
private const float HSVMargin = 0.0f;
|
||||
private const float ChannelsMargin = 4.0f;
|
||||
private const float ChannelTextWidth = 12.0f;
|
||||
private const float SavedColorButtonWidth = 20.0f;
|
||||
private const float SavedColorButtonHeight = 20.0f;
|
||||
private const float TabHeight = 20;
|
||||
private const float ValueBoxesWidth = 100.0f;
|
||||
private const float HSVRGBTextWidth = 15.0f;
|
||||
private const float ColorPreviewHeight = 50.0f;
|
||||
private const int SavedColorsAmount = 10;
|
||||
|
||||
private Color _initialValue;
|
||||
private Color _value;
|
||||
@@ -45,16 +47,19 @@ namespace FlaxEditor.GUI.Dialogs
|
||||
private ColorValueBox.ColorPickerClosedEvent _onClosed;
|
||||
|
||||
private ColorSelectorWithSliders _cSelector;
|
||||
private Tabs.Tabs _hsvRGBTabs;
|
||||
private Tab _RGBTab;
|
||||
private Panel _rgbPanel;
|
||||
private FloatValueBox _cRed;
|
||||
private FloatValueBox _cGreen;
|
||||
private FloatValueBox _cBlue;
|
||||
private FloatValueBox _cAlpha;
|
||||
private Tab _hsvTab;
|
||||
private Panel _hsvPanel;
|
||||
private FloatValueBox _cHue;
|
||||
private FloatValueBox _cSaturation;
|
||||
private FloatValueBox _cValue;
|
||||
private TextBox _cHex;
|
||||
private Button _cCancel;
|
||||
private Button _cOK;
|
||||
private FloatValueBox _cAlpha;
|
||||
private Button _cEyedropper;
|
||||
|
||||
private List<Color> _savedColors = new List<Color>();
|
||||
@@ -124,97 +129,110 @@ namespace FlaxEditor.GUI.Dialogs
|
||||
_savedColors = JsonSerializer.Deserialize<List<Color>>(savedColors);
|
||||
|
||||
// Selector
|
||||
_cSelector = new ColorSelectorWithSliders(180, 18)
|
||||
_cSelector = new ColorSelectorWithSliders(180, 21)
|
||||
{
|
||||
Location = new Float2(PickerMargin, PickerMargin),
|
||||
Parent = this
|
||||
};
|
||||
_cSelector.ColorChanged += x => SelectedColor = x;
|
||||
|
||||
// Red
|
||||
_cRed = new FloatValueBox(0, _cSelector.Right + PickerMargin + RGBAMargin + ChannelTextWidth, PickerMargin, 100, 0, float.MaxValue, 0.001f)
|
||||
_hsvRGBTabs = new Tabs.Tabs
|
||||
{
|
||||
Parent = this
|
||||
Location = new Float2(_cSelector.Right + 30.0f, PickerMargin),
|
||||
TabsTextHorizontalAlignment = TextAlignment.Center,
|
||||
Width = ValueBoxesWidth + HSVRGBTextWidth * 2.0f + ChannelsMargin * 2.0f,
|
||||
Height = (FloatValueBox.DefaultHeight + ChannelsMargin) * 4 + ChannelsMargin,
|
||||
Parent = this,
|
||||
};
|
||||
_hsvRGBTabs.TabsSize = new Float2(_hsvRGBTabs.Width * 0.5f, TabHeight);
|
||||
_hsvRGBTabs.SelectedTabChanged += SelectedTabChanged;
|
||||
|
||||
// RGB Tab
|
||||
_RGBTab = _hsvRGBTabs.AddTab(new Tab("RGB"));
|
||||
_rgbPanel = new Panel(ScrollBars.Vertical)
|
||||
{
|
||||
AnchorPreset = AnchorPresets.StretchAll,
|
||||
Offsets = Margin.Zero,
|
||||
Parent = _RGBTab,
|
||||
};
|
||||
|
||||
// HSV Tab
|
||||
_hsvTab = _hsvRGBTabs.AddTab(new Tab("HSV"));
|
||||
_hsvPanel = new Panel(ScrollBars.Vertical)
|
||||
{
|
||||
AnchorPreset = AnchorPresets.StretchAll,
|
||||
Offsets = Margin.Zero,
|
||||
Parent = _hsvTab,
|
||||
};
|
||||
|
||||
// Red
|
||||
_cRed = new FloatValueBox(0, HSVRGBTextWidth + ChannelsMargin, PickerMargin, ValueBoxesWidth, 0, float.MaxValue, 0.001f)
|
||||
{
|
||||
Parent = _rgbPanel,
|
||||
};
|
||||
_cRed.ValueChanged += OnRGBAChanged;
|
||||
|
||||
// Green
|
||||
_cGreen = new FloatValueBox(0, _cRed.X, _cRed.Bottom + ChannelsMargin, _cRed.Width, 0, float.MaxValue, 0.001f)
|
||||
{
|
||||
Parent = this
|
||||
Parent = _rgbPanel,
|
||||
};
|
||||
_cGreen.ValueChanged += OnRGBAChanged;
|
||||
|
||||
// Blue
|
||||
_cBlue = new FloatValueBox(0, _cRed.X, _cGreen.Bottom + ChannelsMargin, _cRed.Width, 0, float.MaxValue, 0.001f)
|
||||
{
|
||||
Parent = this
|
||||
Parent = _rgbPanel,
|
||||
};
|
||||
_cBlue.ValueChanged += OnRGBAChanged;
|
||||
|
||||
// Alpha
|
||||
_cAlpha = new FloatValueBox(0, _cRed.X, _cBlue.Bottom + ChannelsMargin, _cRed.Width, 0, float.MaxValue, 0.001f)
|
||||
{
|
||||
Parent = this
|
||||
};
|
||||
_cAlpha.ValueChanged += OnRGBAChanged;
|
||||
|
||||
// Hue
|
||||
_cHue = new FloatValueBox(0, PickerMargin + HSVMargin + ChannelTextWidth, _cSelector.Bottom + PickerMargin, 100, 0, 360)
|
||||
_cHue = new FloatValueBox(0, HSVRGBTextWidth + ChannelsMargin, PickerMargin, ValueBoxesWidth)
|
||||
{
|
||||
Parent = this
|
||||
Parent = _hsvPanel,
|
||||
Category = Utils.ValueCategory.Angle,
|
||||
};
|
||||
_cHue.ValueChanged += OnHSVChanged;
|
||||
|
||||
// Saturation
|
||||
_cSaturation = new FloatValueBox(0, _cHue.X, _cHue.Bottom + ChannelsMargin, _cHue.Width, 0, 100.0f, 0.1f)
|
||||
{
|
||||
Parent = this
|
||||
Parent = _hsvPanel,
|
||||
};
|
||||
_cSaturation.ValueChanged += OnHSVChanged;
|
||||
|
||||
// Value
|
||||
_cValue = new FloatValueBox(0, _cHue.X, _cSaturation.Bottom + ChannelsMargin, _cHue.Width, 0, float.MaxValue, 0.1f)
|
||||
{
|
||||
Parent = this
|
||||
Parent = _hsvPanel,
|
||||
};
|
||||
_cValue.ValueChanged += OnHSVChanged;
|
||||
|
||||
// Set valid dialog size based on UI content
|
||||
_dialogSize = Size = new Float2(_cRed.Right + PickerMargin, 300);
|
||||
// Alpha
|
||||
_cAlpha = new FloatValueBox(0, _hsvRGBTabs.Left + HSVRGBTextWidth + ChannelsMargin, _hsvRGBTabs.Bottom + ChannelsMargin * 4.0f, ValueBoxesWidth, 0, float.MaxValue, 0.001f)
|
||||
{
|
||||
Parent = this,
|
||||
};
|
||||
_cAlpha.ValueChanged += OnRGBAChanged;
|
||||
|
||||
// Hex
|
||||
const float hexTextBoxWidth = 80;
|
||||
_cHex = new TextBox(false, Width - hexTextBoxWidth - PickerMargin, _cSelector.Bottom + PickerMargin, hexTextBoxWidth)
|
||||
_cHex = new TextBox(false, _hsvRGBTabs.Left + HSVRGBTextWidth + ChannelsMargin, _cAlpha.Bottom + ChannelsMargin * 2.0f, ValueBoxesWidth)
|
||||
{
|
||||
Parent = this
|
||||
Parent = this,
|
||||
};
|
||||
_cHex.EditEnd += OnHexChanged;
|
||||
|
||||
// Cancel
|
||||
_cCancel = new Button(Width - ButtonsWidth - PickerMargin, Height - Button.DefaultHeight - PickerMargin, ButtonsWidth)
|
||||
{
|
||||
Text = "Cancel",
|
||||
Parent = this
|
||||
};
|
||||
_cCancel.Clicked += OnCancel;
|
||||
|
||||
// OK
|
||||
_cOK = new Button(_cCancel.Left - ButtonsWidth - PickerMargin, _cCancel.Y, ButtonsWidth)
|
||||
{
|
||||
Text = "Ok",
|
||||
Parent = this
|
||||
};
|
||||
_cOK.Clicked += OnSubmit;
|
||||
// Set valid dialog size based on UI content
|
||||
_dialogSize = Size = new Float2(_hsvRGBTabs.Right + PickerMargin, _cHex.Bottom + 40.0f + ColorPreviewHeight + PickerMargin);
|
||||
|
||||
// Create saved color buttons
|
||||
CreateAllSaveButtons();
|
||||
CreateAllSavedColorsButtons();
|
||||
|
||||
// Eyedropper button
|
||||
var style = Style.Current;
|
||||
_cEyedropper = new Button(_cOK.X - EyedropperMargin, _cHex.Bottom + PickerMargin)
|
||||
_cEyedropper = new Button(_cSelector.BottomLeft.X, _cSelector.BottomLeft.Y - 25.0f, 25.0f, 25.0f)
|
||||
{
|
||||
TooltipText = "Eyedropper tool to pick a color directly from the screen",
|
||||
TooltipText = "Eyedropper tool to pick a color directly from the screen.",
|
||||
BackgroundBrush = new SpriteBrush(Editor.Instance.Icons.Search32),
|
||||
BackgroundColor = style.Foreground,
|
||||
BackgroundColorHighlighted = style.Foreground.RGBMultiplied(0.9f),
|
||||
@@ -223,9 +241,6 @@ namespace FlaxEditor.GUI.Dialogs
|
||||
Parent = this,
|
||||
};
|
||||
_cEyedropper.Clicked += OnEyedropStart;
|
||||
_cEyedropper.Height = (_cValue.Bottom - _cEyedropper.Y) * 0.5f;
|
||||
_cEyedropper.Width = _cEyedropper.Height;
|
||||
_cEyedropper.X -= _cEyedropper.Width;
|
||||
|
||||
// Set initial color
|
||||
SelectedColor = initialValue;
|
||||
@@ -244,7 +259,7 @@ namespace FlaxEditor.GUI.Dialogs
|
||||
}
|
||||
}
|
||||
|
||||
// Set color of button to current value;
|
||||
// Set color of button to current value
|
||||
button.BackgroundColor = _value;
|
||||
button.BackgroundColorHighlighted = _value;
|
||||
button.BackgroundColorSelected = _value.RGBMultiplied(0.8f);
|
||||
@@ -256,10 +271,10 @@ namespace FlaxEditor.GUI.Dialogs
|
||||
var savedColors = JsonSerializer.Serialize(_savedColors, typeof(List<Color>));
|
||||
Editor.Instance.ProjectCache.SetCustomData("ColorPickerSavedColors", savedColors);
|
||||
|
||||
// create new + button
|
||||
if (_savedColorButtons.Count < 8)
|
||||
// Create new + button
|
||||
if (_savedColorButtons.Count < SavedColorsAmount)
|
||||
{
|
||||
var savedColorButton = new Button(PickerMargin * (_savedColorButtons.Count + 1) + SavedColorButtonWidth * _savedColorButtons.Count, Height - SavedColorButtonHeight - PickerMargin, SavedColorButtonWidth, SavedColorButtonHeight)
|
||||
var savedColorButton = new Button(PickerMargin * (_savedColorButtons.Count + 1) + SavedColorButtonWidth * _savedColorButtons.Count, _cHex.Bottom + 40.0f + ColorPreviewHeight * 0.5f, SavedColorButtonWidth, SavedColorButtonHeight)
|
||||
{
|
||||
Text = "+",
|
||||
Parent = this,
|
||||
@@ -276,11 +291,32 @@ namespace FlaxEditor.GUI.Dialogs
|
||||
}
|
||||
}
|
||||
|
||||
private void SelectedTabChanged(Tabs.Tabs tabs)
|
||||
{
|
||||
if (_rgbPanel == null || _hsvPanel == null)
|
||||
return;
|
||||
|
||||
switch (tabs.SelectedTabIndex)
|
||||
{
|
||||
// RGB
|
||||
case 0:
|
||||
_rgbPanel.Visible = true;
|
||||
_hsvPanel.Visible = false;
|
||||
break;
|
||||
// HSV
|
||||
case 1:
|
||||
_rgbPanel.Visible = false;
|
||||
_hsvPanel.Visible = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnColorPicked(Color32 colorPicked)
|
||||
{
|
||||
if (_activeEyedropper)
|
||||
{
|
||||
_activeEyedropper = false;
|
||||
_cEyedropper.BackgroundColor = _cEyedropper.BackgroundColorHighlighted = Style.Current.Foreground;
|
||||
SelectedColor = colorPicked;
|
||||
ScreenUtilities.PickColorDone -= OnColorPicked;
|
||||
}
|
||||
@@ -289,6 +325,7 @@ namespace FlaxEditor.GUI.Dialogs
|
||||
private void OnEyedropStart()
|
||||
{
|
||||
_activeEyedropper = true;
|
||||
_cEyedropper.BackgroundColor = _cEyedropper.BackgroundColorHighlighted = Style.Current.BackgroundHighlighted;
|
||||
ScreenUtilities.PickColor();
|
||||
ScreenUtilities.PickColorDone += OnColorPicked;
|
||||
}
|
||||
@@ -306,6 +343,7 @@ namespace FlaxEditor.GUI.Dialogs
|
||||
if (_disableEvents)
|
||||
return;
|
||||
|
||||
_cHue.Value = Mathf.Wrap(_cHue.Value, 0f, 360f);
|
||||
SelectedColor = Color.FromHSV(_cHue.Value, _cSaturation.Value / 100.0f, _cValue.Value / 100.0f, _cAlpha.Value);
|
||||
}
|
||||
|
||||
@@ -339,64 +377,76 @@ namespace FlaxEditor.GUI.Dialogs
|
||||
|
||||
base.Draw();
|
||||
|
||||
// RGBA
|
||||
var rgbaR = new Rectangle(_cRed.Left - ChannelTextWidth, _cRed.Y, 10000, _cRed.Height);
|
||||
Render2D.DrawText(style.FontMedium, "R", rgbaR, textColor, TextAlignment.Near, TextAlignment.Center);
|
||||
rgbaR.Location.Y = _cGreen.Y;
|
||||
Render2D.DrawText(style.FontMedium, "G", rgbaR, textColor, TextAlignment.Near, TextAlignment.Center);
|
||||
rgbaR.Location.Y = _cBlue.Y;
|
||||
Render2D.DrawText(style.FontMedium, "B", rgbaR, textColor, TextAlignment.Near, TextAlignment.Center);
|
||||
rgbaR.Location.Y = _cAlpha.Y;
|
||||
Render2D.DrawText(style.FontMedium, "A", rgbaR, textColor, TextAlignment.Near, TextAlignment.Center);
|
||||
switch (_hsvRGBTabs.SelectedTabIndex)
|
||||
{
|
||||
// RGB
|
||||
case 0:
|
||||
var rgbRect = new Rectangle(_hsvRGBTabs.Left + PickerMargin, _hsvRGBTabs.Top + TabHeight + PickerMargin, 10000, _cRed.Height);
|
||||
Render2D.DrawText(style.FontMedium, "R", rgbRect, textColor, TextAlignment.Near, TextAlignment.Center);
|
||||
rgbRect.Location.Y += _cRed.Height + ChannelsMargin;
|
||||
Render2D.DrawText(style.FontMedium, "G", rgbRect, textColor, TextAlignment.Near, TextAlignment.Center);
|
||||
rgbRect.Location.Y += _cRed.Height + ChannelsMargin;
|
||||
Render2D.DrawText(style.FontMedium, "B", rgbRect, textColor, TextAlignment.Near, TextAlignment.Center);
|
||||
break;
|
||||
// HSV
|
||||
case 1:
|
||||
// Left
|
||||
var hsvLeftRect = new Rectangle(_hsvRGBTabs.Left + PickerMargin, _hsvRGBTabs.Top + TabHeight + PickerMargin, 10000, _cHue.Height);
|
||||
Render2D.DrawText(style.FontMedium, "H", hsvLeftRect, textColor, TextAlignment.Near, TextAlignment.Center);
|
||||
hsvLeftRect.Location.Y += _cHue.Height + ChannelsMargin;
|
||||
Render2D.DrawText(style.FontMedium, "S", hsvLeftRect, textColor, TextAlignment.Near, TextAlignment.Center);
|
||||
hsvLeftRect.Location.Y += _cHue.Height + ChannelsMargin;
|
||||
Render2D.DrawText(style.FontMedium, "V", hsvLeftRect, textColor, TextAlignment.Near, TextAlignment.Center);
|
||||
|
||||
// HSV left
|
||||
var hsvHl = new Rectangle(_cHue.Left - ChannelTextWidth, _cHue.Y, 10000, _cHue.Height);
|
||||
Render2D.DrawText(style.FontMedium, "H", hsvHl, textColor, TextAlignment.Near, TextAlignment.Center);
|
||||
hsvHl.Location.Y = _cSaturation.Y;
|
||||
Render2D.DrawText(style.FontMedium, "S", hsvHl, textColor, TextAlignment.Near, TextAlignment.Center);
|
||||
hsvHl.Location.Y = _cValue.Y;
|
||||
Render2D.DrawText(style.FontMedium, "V", hsvHl, textColor, TextAlignment.Near, TextAlignment.Center);
|
||||
// Right
|
||||
var hsvRightRect = new Rectangle(_hsvRGBTabs.Right - HSVRGBTextWidth, _hsvRGBTabs.Top + TabHeight + PickerMargin, ChannelTextWidth, _cHue.Height);
|
||||
Render2D.DrawText(style.FontMedium, "°", hsvRightRect, textColor, TextAlignment.Near, TextAlignment.Center);
|
||||
hsvRightRect.Location.Y += _cHue.Height + ChannelsMargin;
|
||||
Render2D.DrawText(style.FontMedium, "%", hsvRightRect, textColor, TextAlignment.Near, TextAlignment.Center);
|
||||
hsvRightRect.Location.Y += _cHue.Height + ChannelsMargin;
|
||||
Render2D.DrawText(style.FontMedium, "%", hsvRightRect, textColor, TextAlignment.Near, TextAlignment.Center);
|
||||
break;
|
||||
}
|
||||
|
||||
// HSV right
|
||||
var hsvHr = new Rectangle(_cHue.Right + 2, _cHue.Y, 10000, _cHue.Height);
|
||||
Render2D.DrawText(style.FontMedium, "°", hsvHr, textColor, TextAlignment.Near, TextAlignment.Center);
|
||||
hsvHr.Location.Y = _cSaturation.Y;
|
||||
Render2D.DrawText(style.FontMedium, "%", hsvHr, textColor, TextAlignment.Near, TextAlignment.Center);
|
||||
hsvHr.Location.Y = _cValue.Y;
|
||||
Render2D.DrawText(style.FontMedium, "%", hsvHr, textColor, TextAlignment.Near, TextAlignment.Center);
|
||||
// A
|
||||
var alphaHexRect = new Rectangle(_hsvRGBTabs.Left + PickerMargin, _cAlpha.Top, ChannelTextWidth, _cAlpha.Height);
|
||||
Render2D.DrawText(style.FontMedium, "A", alphaHexRect, textColor, TextAlignment.Near, TextAlignment.Center);
|
||||
|
||||
// Hex
|
||||
var hex = new Rectangle(_cHex.Left - 26, _cHex.Y, 10000, _cHex.Height);
|
||||
Render2D.DrawText(style.FontMedium, "Hex", hex, textColor, TextAlignment.Near, TextAlignment.Center);
|
||||
alphaHexRect.Y += _cAlpha.Height + ChannelsMargin * 2.0f;
|
||||
alphaHexRect.X -= 5.0f; // "Hex" is two characters wider than the other labels so we need to adjust for that
|
||||
Render2D.DrawText(style.FontMedium, "Hex", alphaHexRect, textColor, TextAlignment.Far, TextAlignment.Center);
|
||||
|
||||
// Color difference
|
||||
var newRect = new Rectangle(_cOK.X - 3, _cHex.Bottom + PickerMargin, 130, 0);
|
||||
newRect.Size.Y = 50;
|
||||
Render2D.FillRectangle(newRect, Color.White);
|
||||
var smallRectSize = 10;
|
||||
var numHor = Mathf.FloorToInt(newRect.Width / smallRectSize);
|
||||
var numVer = Mathf.FloorToInt(newRect.Height / smallRectSize);
|
||||
var differenceRect = new Rectangle(_hsvRGBTabs.Left, _cHex.Bottom + 40.0f, _hsvRGBTabs.Width, ColorPreviewHeight);
|
||||
|
||||
// Draw checkerboard for background of color to help with transparency
|
||||
Render2D.FillRectangle(differenceRect, Color.White);
|
||||
var smallRectSize = 10;
|
||||
var numHor = Mathf.CeilToInt(differenceRect.Width / smallRectSize);
|
||||
var numVer = Mathf.CeilToInt(differenceRect.Height / smallRectSize);
|
||||
Render2D.PushClip(differenceRect);
|
||||
for (int i = 0; i < numHor; i++)
|
||||
{
|
||||
for (int j = 0; j < numVer; j++)
|
||||
{
|
||||
if ((i + j) % 2 == 0)
|
||||
{
|
||||
var rect = new Rectangle(newRect.X + smallRectSize * i, newRect.Y + smallRectSize * j, new Float2(smallRectSize));
|
||||
var rect = new Rectangle(differenceRect.X + smallRectSize * i, differenceRect.Y + smallRectSize * j, new Float2(smallRectSize));
|
||||
Render2D.FillRectangle(rect, Color.Gray);
|
||||
}
|
||||
}
|
||||
}
|
||||
Render2D.FillRectangle(newRect, _value);
|
||||
Render2D.PopClip();
|
||||
Render2D.FillRectangle(differenceRect, _value);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnShow()
|
||||
{
|
||||
// Auto cancel on lost focus
|
||||
// Apply changes on lost focus
|
||||
#if !PLATFORM_LINUX
|
||||
((WindowRootControl)Root).Window.LostFocus += OnWindowLostFocus;
|
||||
((WindowRootControl)Root).Window.LostFocus += OnSubmit;
|
||||
#endif
|
||||
|
||||
base.OnShow();
|
||||
@@ -409,6 +459,7 @@ namespace FlaxEditor.GUI.Dialogs
|
||||
{
|
||||
// Cancel eye dropping
|
||||
_activeEyedropper = false;
|
||||
_cEyedropper.BackgroundColor = _cEyedropper.BackgroundColorHighlighted = Style.Current.Foreground;
|
||||
ScreenUtilities.PickColorDone -= OnColorPicked;
|
||||
return true;
|
||||
}
|
||||
@@ -420,9 +471,7 @@ namespace FlaxEditor.GUI.Dialogs
|
||||
public override bool OnMouseUp(Float2 location, MouseButton button)
|
||||
{
|
||||
if (base.OnMouseUp(location, button))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
var child = GetChildAtRecursive(location);
|
||||
if (button == MouseButton.Right && child is Button b && b.Tag is Color c)
|
||||
@@ -484,20 +533,20 @@ namespace FlaxEditor.GUI.Dialogs
|
||||
}
|
||||
_savedColorButtons.Clear();
|
||||
|
||||
CreateAllSaveButtons();
|
||||
CreateAllSavedColorsButtons();
|
||||
|
||||
// Save new colors
|
||||
var savedColors = JsonSerializer.Serialize(_savedColors, typeof(List<Color>));
|
||||
Editor.Instance.ProjectCache.SetCustomData("ColorPickerSavedColors", savedColors);
|
||||
}
|
||||
|
||||
private void CreateAllSaveButtons()
|
||||
private void CreateAllSavedColorsButtons()
|
||||
{
|
||||
// Create saved color buttons
|
||||
for (int i = 0; i < _savedColors.Count; i++)
|
||||
{
|
||||
var savedColor = _savedColors[i];
|
||||
var savedColorButton = new Button(PickerMargin * (i + 1) + SavedColorButtonWidth * i, Height - SavedColorButtonHeight - PickerMargin, SavedColorButtonWidth, SavedColorButtonHeight)
|
||||
var savedColorButton = new Button(PickerMargin * (i + 1) + SavedColorButtonWidth * i, _cHex.Bottom + 40.0f + ColorPreviewHeight * 0.5f, SavedColorButtonWidth, SavedColorButtonHeight)
|
||||
{
|
||||
Parent = this,
|
||||
Tag = savedColor,
|
||||
@@ -508,9 +557,9 @@ namespace FlaxEditor.GUI.Dialogs
|
||||
savedColorButton.ButtonClicked += OnSavedColorButtonClicked;
|
||||
_savedColorButtons.Add(savedColorButton);
|
||||
}
|
||||
if (_savedColors.Count < 8)
|
||||
if (_savedColors.Count < SavedColorsAmount)
|
||||
{
|
||||
var savedColorButton = new Button(PickerMargin * (_savedColors.Count + 1) + SavedColorButtonWidth * _savedColors.Count, Height - SavedColorButtonHeight - PickerMargin, SavedColorButtonWidth, SavedColorButtonHeight)
|
||||
var savedColorButton = new Button(PickerMargin * (_savedColors.Count + 1) + SavedColorButtonWidth * _savedColors.Count, _cHex.Bottom + 40.0f + ColorPreviewHeight * 0.5f, SavedColorButtonWidth, SavedColorButtonHeight)
|
||||
{
|
||||
Text = "+",
|
||||
Parent = this,
|
||||
@@ -522,19 +571,6 @@ namespace FlaxEditor.GUI.Dialogs
|
||||
}
|
||||
}
|
||||
|
||||
private void OnWindowLostFocus()
|
||||
{
|
||||
// Auto apply color on defocus
|
||||
var autoAcceptColorPickerChange = Editor.Instance.Options.Options.Interface.AutoAcceptColorPickerChange;
|
||||
if (_useDynamicEditing && _initialValue != _value && _canPassLastChangeEvent && autoAcceptColorPickerChange)
|
||||
{
|
||||
_canPassLastChangeEvent = false;
|
||||
_onChanged?.Invoke(_value, false);
|
||||
}
|
||||
|
||||
OnCancel();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnSubmit()
|
||||
{
|
||||
@@ -542,6 +578,9 @@ namespace FlaxEditor.GUI.Dialogs
|
||||
return;
|
||||
_disableEvents = true;
|
||||
|
||||
// Ensure the cursor is restored
|
||||
Cursor = CursorType.Default;
|
||||
|
||||
// Send color event if modified
|
||||
if (_value != _initialValue)
|
||||
{
|
||||
@@ -558,6 +597,9 @@ namespace FlaxEditor.GUI.Dialogs
|
||||
return;
|
||||
_disableEvents = true;
|
||||
|
||||
// Ensure the cursor is restored
|
||||
Cursor = CursorType.Default;
|
||||
|
||||
// Restore color if modified
|
||||
if (_useDynamicEditing && _initialValue != _value && _canPassLastChangeEvent)
|
||||
{
|
||||
|
||||
@@ -12,6 +12,13 @@ namespace FlaxEditor.GUI.Dialogs
|
||||
/// <seealso cref="FlaxEngine.GUI.ContainerControl" />
|
||||
public class ColorSelector : ContainerControl
|
||||
{
|
||||
private const String GrayedOutParamName = "GrayedOut";
|
||||
|
||||
/// <summary>
|
||||
/// Offset value applied to mouse cursor position when the user lets go of wheel or sliders.
|
||||
/// </summary>
|
||||
protected const float MouseCursorOffset = 6.0f;
|
||||
|
||||
/// <summary>
|
||||
/// The color.
|
||||
/// </summary>
|
||||
@@ -22,9 +29,22 @@ namespace FlaxEditor.GUI.Dialogs
|
||||
/// </summary>
|
||||
protected Rectangle _wheelRect;
|
||||
|
||||
private readonly SpriteHandle _colorWheelSprite;
|
||||
private readonly MaterialBase _hsWheelMaterial;
|
||||
private bool _isMouseDownWheel;
|
||||
|
||||
private Rectangle wheelDragRect
|
||||
{
|
||||
get
|
||||
{
|
||||
var hsv = _color.ToHSV();
|
||||
float hAngle = hsv.X * Mathf.DegreesToRadians;
|
||||
float hRadius = hsv.Y * _wheelRect.Width * 0.5f;
|
||||
var hsPos = new Float2(hRadius * Mathf.Cos(hAngle), -hRadius * Mathf.Sin(hAngle));
|
||||
float wheelBoxSize = IsSliding ? 9.0f : 5.0f;
|
||||
return new Rectangle(hsPos - (wheelBoxSize * 0.5f) + _wheelRect.Center, new Float2(wheelBoxSize));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when selected color gets changed.
|
||||
/// </summary>
|
||||
@@ -78,7 +98,8 @@ namespace FlaxEditor.GUI.Dialogs
|
||||
{
|
||||
AutoFocus = true;
|
||||
|
||||
_colorWheelSprite = Editor.Instance.Icons.ColorWheel128;
|
||||
_hsWheelMaterial = FlaxEngine.Content.LoadAsyncInternal<MaterialBase>(EditorAssets.HSWheelMaterial);
|
||||
_hsWheelMaterial = _hsWheelMaterial.CreateVirtualInstance();
|
||||
_wheelRect = new Rectangle(0, 0, wheelSize, wheelSize);
|
||||
}
|
||||
|
||||
@@ -165,17 +186,19 @@ namespace FlaxEditor.GUI.Dialogs
|
||||
{
|
||||
base.Draw();
|
||||
|
||||
var hsv = _color.ToHSV();
|
||||
bool enabled = EnabledInHierarchy;
|
||||
|
||||
_hsWheelMaterial.SetParameterValue(GrayedOutParamName, enabled ? 1.0f : 0.5f);
|
||||
Render2D.DrawMaterial(_hsWheelMaterial, _wheelRect, enabled ? Color.White : Color.Gray);
|
||||
|
||||
// Wheel
|
||||
float boxExpand = (2.0f * 4.0f / 128.0f) * _wheelRect.Width;
|
||||
Render2D.DrawSprite(_colorWheelSprite, _wheelRect.MakeExpanded(boxExpand), enabled ? Color.White : Color.Gray);
|
||||
float hAngle = hsv.X * Mathf.DegreesToRadians;
|
||||
float hRadius = hsv.Y * _wheelRect.Width * 0.5f;
|
||||
var hsPos = new Float2(hRadius * Mathf.Cos(hAngle), -hRadius * Mathf.Sin(hAngle));
|
||||
const float wheelBoxSize = 4.0f;
|
||||
Render2D.DrawRectangle(new Rectangle(hsPos - (wheelBoxSize * 0.5f) + _wheelRect.Center, new Float2(wheelBoxSize)), _isMouseDownWheel ? Color.Gray : Color.Black);
|
||||
Render2D.DrawMaterial(_hsWheelMaterial, _wheelRect, enabled ? Color.White : Color.Gray);
|
||||
Float3 hsv = _color.ToHSV();
|
||||
Color hsColor = Color.FromHSV(new Float3(hsv.X, hsv.Y, 1));
|
||||
Rectangle wheelRect = wheelDragRect;
|
||||
Render2D.FillRectangle(wheelRect, hsColor);
|
||||
Render2D.DrawRectangle(wheelRect, Color.Black, _isMouseDownWheel ? 2.0f : 1.0f);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -202,6 +225,7 @@ namespace FlaxEditor.GUI.Dialogs
|
||||
if (!_isMouseDownWheel)
|
||||
{
|
||||
_isMouseDownWheel = true;
|
||||
Cursor = CursorType.Hidden;
|
||||
StartMouseCapture();
|
||||
SlidingStart?.Invoke();
|
||||
}
|
||||
@@ -218,6 +242,10 @@ namespace FlaxEditor.GUI.Dialogs
|
||||
if (button == MouseButton.Left && _isMouseDownWheel)
|
||||
{
|
||||
EndMouseCapture();
|
||||
// Make the cursor appear where the user expects it to be (position of selection rectangle)
|
||||
Rectangle dragRect = wheelDragRect;
|
||||
Root.MousePosition = dragRect.Center + MouseCursorOffset;
|
||||
Cursor = CursorType.Default;
|
||||
EndSliding();
|
||||
return true;
|
||||
}
|
||||
@@ -246,10 +274,10 @@ namespace FlaxEditor.GUI.Dialogs
|
||||
/// <seealso cref="ColorSelector" />
|
||||
public class ColorSelectorWithSliders : ColorSelector
|
||||
{
|
||||
private Rectangle _slider1Rect;
|
||||
private Rectangle _slider2Rect;
|
||||
private bool _isMouseDownSlider1;
|
||||
private bool _isMouseDownSlider2;
|
||||
private Rectangle _valueSliderRect;
|
||||
private Rectangle _alphaSliderRect;
|
||||
private bool _isMouseDownValueSlider;
|
||||
private bool _isMouseDownAlphaSlider;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ColorSelectorWithSliders"/> class.
|
||||
@@ -260,26 +288,26 @@ namespace FlaxEditor.GUI.Dialogs
|
||||
: base(wheelSize)
|
||||
{
|
||||
// Setup dimensions
|
||||
const float slidersMargin = 8.0f;
|
||||
_slider1Rect = new Rectangle(wheelSize + slidersMargin, 0, slidersThickness, wheelSize);
|
||||
_slider2Rect = new Rectangle(_slider1Rect.Right + slidersMargin, _slider1Rect.Y, slidersThickness, _slider1Rect.Height);
|
||||
Size = new Float2(_slider2Rect.Right, wheelSize);
|
||||
const float slidersMargin = 10.0f;
|
||||
_valueSliderRect = new Rectangle(wheelSize + slidersMargin, 0, slidersThickness, wheelSize);
|
||||
_alphaSliderRect = new Rectangle(_valueSliderRect.Right + slidersMargin * 1.5f, _valueSliderRect.Y, slidersThickness, _valueSliderRect.Height);
|
||||
Size = new Float2(_alphaSliderRect.Right, wheelSize);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void UpdateMouse(ref Float2 location)
|
||||
{
|
||||
if (_isMouseDownSlider1)
|
||||
if (_isMouseDownValueSlider)
|
||||
{
|
||||
var hsv = _color.ToHSV();
|
||||
hsv.Z = 1.0f - Mathf.Saturate((location.Y - _slider1Rect.Y) / _slider1Rect.Height);
|
||||
hsv.Z = 1.0f - Mathf.Saturate((location.Y - _valueSliderRect.Y) / _valueSliderRect.Height);
|
||||
|
||||
Color = Color.FromHSV(hsv, _color.A);
|
||||
}
|
||||
else if (_isMouseDownSlider2)
|
||||
else if (_isMouseDownAlphaSlider)
|
||||
{
|
||||
var color = _color;
|
||||
color.A = 1.0f - Mathf.Saturate((location.Y - _slider2Rect.Y) / _slider2Rect.Height);
|
||||
color.A = 1.0f - Mathf.Saturate((location.Y - _alphaSliderRect.Y) / _alphaSliderRect.Height);
|
||||
|
||||
Color = color;
|
||||
}
|
||||
@@ -300,32 +328,61 @@ namespace FlaxEditor.GUI.Dialogs
|
||||
var hs = hsv;
|
||||
hs.Z = 1.0f;
|
||||
Color hsC = Color.FromHSV(hs);
|
||||
const float slidersOffset = 3.0f;
|
||||
const float slidersThickness = 4.0f;
|
||||
|
||||
// Value
|
||||
float valueY = _slider2Rect.Height * (1 - hsv.Z);
|
||||
var valueR = new Rectangle(_slider1Rect.X - slidersOffset, _slider1Rect.Y + valueY - slidersThickness / 2, _slider1Rect.Width + slidersOffset * 2, slidersThickness);
|
||||
Render2D.FillRectangle(_slider1Rect, hsC, hsC, Color.Black, Color.Black);
|
||||
Render2D.DrawRectangle(_slider1Rect, _isMouseDownSlider1 ? style.BackgroundSelected : Color.Black);
|
||||
Render2D.DrawRectangle(valueR, _isMouseDownSlider1 ? Color.White : Color.Gray);
|
||||
// Value slider
|
||||
float valueKnobExpand = _isMouseDownValueSlider ? 10.0f : 4.0f;
|
||||
float valueY = _valueSliderRect.Height * (1 - hsv.Z);
|
||||
float valueKnobWidth = _valueSliderRect.Width + valueKnobExpand;
|
||||
float valueKnobHeight = _isMouseDownValueSlider ? 7.0f : 4.0f;
|
||||
float valueKnobX = _valueSliderRect.X - valueKnobExpand * 0.5f;
|
||||
float valueKnobY = _valueSliderRect.Y + valueY - valueKnobHeight * 0.5f;
|
||||
Rectangle valueKnobRect = new Rectangle(valueKnobX, valueKnobY, valueKnobWidth, valueKnobHeight);
|
||||
Render2D.FillRectangle(_valueSliderRect, hsC, hsC, Color.Black, Color.Black);
|
||||
// Draw one black and one white border to make the knob visible at any saturation level
|
||||
Render2D.DrawRectangle(valueKnobRect, Color.White, _isMouseDownValueSlider ? 3.0f : 2.0f);
|
||||
Render2D.DrawRectangle(valueKnobRect, Color.Black, _isMouseDownValueSlider ? 2.0f : 1.0f);
|
||||
|
||||
// Alpha
|
||||
float alphaY = _slider2Rect.Height * (1 - _color.A);
|
||||
var alphaR = new Rectangle(_slider2Rect.X - slidersOffset, _slider2Rect.Y + alphaY - slidersThickness / 2, _slider2Rect.Width + slidersOffset * 2, slidersThickness);
|
||||
// Draw checkerboard pattern as background of alpha slider
|
||||
Render2D.FillRectangle(_alphaSliderRect, Color.White);
|
||||
var smallRectSize = _alphaSliderRect.Width / 2.0f;
|
||||
var numHor = Mathf.CeilToInt(_alphaSliderRect.Width / smallRectSize);
|
||||
var numVer = Mathf.CeilToInt(_alphaSliderRect.Height / smallRectSize);
|
||||
Render2D.PushClip(_alphaSliderRect);
|
||||
for (int i = 0; i < numHor; i++)
|
||||
{
|
||||
for (int j = 0; j < numVer; j++)
|
||||
{
|
||||
if ((i + j) % 2 == 0)
|
||||
{
|
||||
var rect = new Rectangle(_alphaSliderRect.X + smallRectSize * i, _alphaSliderRect.Y + smallRectSize * j, new Float2(smallRectSize));
|
||||
Render2D.FillRectangle(rect, Color.Gray);
|
||||
}
|
||||
}
|
||||
}
|
||||
Render2D.PopClip();
|
||||
|
||||
// Alpha slider
|
||||
float alphaKnobExpand = _isMouseDownAlphaSlider ? 10.0f : 4.0f;
|
||||
float alphaY = _alphaSliderRect.Height * (1 - _color.A);
|
||||
float alphaKnobWidth = _alphaSliderRect.Width + alphaKnobExpand;
|
||||
float alphaKnobHeight = _isMouseDownAlphaSlider ? 7.0f : 4.0f;
|
||||
float alphaKnobX = _alphaSliderRect.X - alphaKnobExpand * 0.5f;
|
||||
float alphaKnobY = _alphaSliderRect.Y + alphaY - alphaKnobExpand * 0.5f;
|
||||
Rectangle alphaKnobRect = new Rectangle(alphaKnobX, alphaKnobY, alphaKnobWidth, alphaKnobHeight);
|
||||
var color = _color;
|
||||
color.A = 1; // Keep slider 2 fill rect from changing color alpha while selecting.
|
||||
Render2D.FillRectangle(_slider2Rect, color, color, Color.Transparent, Color.Transparent);
|
||||
Render2D.DrawRectangle(_slider2Rect, _isMouseDownSlider2 ? style.BackgroundSelected : Color.Black);
|
||||
Render2D.DrawRectangle(alphaR, _isMouseDownSlider2 ? Color.White : Color.Gray);
|
||||
color.A = 1; // Prevent alpha slider fill from becoming transparent
|
||||
Render2D.FillRectangle(_alphaSliderRect, color, color, Color.Transparent, Color.Transparent);
|
||||
// Draw one black and one white border to make the knob visible at any saturation level
|
||||
Render2D.DrawRectangle(alphaKnobRect, Color.White, _isMouseDownAlphaSlider ? 3.0f : 2.0f);
|
||||
Render2D.DrawRectangle(alphaKnobRect, Color.Black, _isMouseDownAlphaSlider ? 2.0f : 1.0f);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnLostFocus()
|
||||
{
|
||||
// Clear flags
|
||||
_isMouseDownSlider1 = false;
|
||||
_isMouseDownSlider2 = false;
|
||||
_isMouseDownValueSlider = false;
|
||||
_isMouseDownAlphaSlider = false;
|
||||
|
||||
base.OnLostFocus();
|
||||
}
|
||||
@@ -333,15 +390,17 @@ namespace FlaxEditor.GUI.Dialogs
|
||||
/// <inheritdoc />
|
||||
public override bool OnMouseDown(Float2 location, MouseButton button)
|
||||
{
|
||||
if (button == MouseButton.Left && _slider1Rect.Contains(location))
|
||||
if (button == MouseButton.Left && _valueSliderRect.Contains(location))
|
||||
{
|
||||
_isMouseDownSlider1 = true;
|
||||
_isMouseDownValueSlider = true;
|
||||
Cursor = CursorType.Hidden;
|
||||
StartMouseCapture();
|
||||
UpdateMouse(ref location);
|
||||
}
|
||||
if (button == MouseButton.Left && _slider2Rect.Contains(location))
|
||||
if (button == MouseButton.Left && _alphaSliderRect.Contains(location))
|
||||
{
|
||||
_isMouseDownSlider2 = true;
|
||||
_isMouseDownAlphaSlider = true;
|
||||
Cursor = CursorType.Hidden;
|
||||
StartMouseCapture();
|
||||
UpdateMouse(ref location);
|
||||
}
|
||||
@@ -352,10 +411,16 @@ namespace FlaxEditor.GUI.Dialogs
|
||||
/// <inheritdoc />
|
||||
public override bool OnMouseUp(Float2 location, MouseButton button)
|
||||
{
|
||||
if (button == MouseButton.Left && (_isMouseDownSlider1 || _isMouseDownSlider2))
|
||||
if (button == MouseButton.Left && (_isMouseDownValueSlider || _isMouseDownAlphaSlider))
|
||||
{
|
||||
_isMouseDownSlider1 = false;
|
||||
_isMouseDownSlider2 = false;
|
||||
// Make the cursor appear where the user expects it to be (center of slider horizontally and slider knob position vertically)
|
||||
float sliderCenter = _isMouseDownValueSlider ? _valueSliderRect.Center.X : _alphaSliderRect.Center.X;
|
||||
// Calculate y position based on the slider knob to avoid incrementing value by a small amount when the user repeatedly clicks the slider (f.e. when moving in small steps)
|
||||
float mouseSliderPosition = _isMouseDownValueSlider ? _valueSliderRect.Y + _valueSliderRect.Height * (1 - _color.ToHSV().Z) + MouseCursorOffset : _alphaSliderRect.Y + _alphaSliderRect.Height * (1 - _color.A) + MouseCursorOffset;
|
||||
Root.MousePosition = new Float2(sliderCenter + MouseCursorOffset, Mathf.Clamp(mouseSliderPosition, _valueSliderRect.Top, _valueSliderRect.Bottom));
|
||||
Cursor = CursorType.Default;
|
||||
_isMouseDownValueSlider = false;
|
||||
_isMouseDownAlphaSlider = false;
|
||||
EndMouseCapture();
|
||||
return true;
|
||||
}
|
||||
@@ -367,8 +432,8 @@ namespace FlaxEditor.GUI.Dialogs
|
||||
public override void OnEndMouseCapture()
|
||||
{
|
||||
// Clear flags
|
||||
_isMouseDownSlider1 = false;
|
||||
_isMouseDownSlider2 = false;
|
||||
_isMouseDownValueSlider = false;
|
||||
_isMouseDownAlphaSlider = false;
|
||||
|
||||
base.OnEndMouseCapture();
|
||||
}
|
||||
|
||||
@@ -89,6 +89,11 @@ namespace FlaxEditor.GUI.Input
|
||||
/// </summary>
|
||||
public bool IsSliding => _isSliding;
|
||||
|
||||
/// <summary>
|
||||
/// The color of the highlight to the left of the value box.
|
||||
/// </summary>
|
||||
public Color HighlightColor;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when sliding starts.
|
||||
/// </summary>
|
||||
@@ -211,6 +216,12 @@ namespace FlaxEditor.GUI.Input
|
||||
Render2D.DrawRectangle(bounds, style.SelectionBorder);
|
||||
}
|
||||
}
|
||||
|
||||
if (HighlightColor != Color.Transparent)
|
||||
{
|
||||
var highlightRect = new Rectangle(-3.0f, 0.0f, 3.0f, Height);
|
||||
Render2D.FillRectangle(highlightRect, HighlightColor);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -2148,10 +2148,9 @@ namespace FlaxEditor.GUI.Timeline
|
||||
/// </summary>
|
||||
public void ShowWholeTimeline()
|
||||
{
|
||||
var viewWidth = Width;
|
||||
var timelineWidth = Duration * UnitsPerSecond * Zoom + 8 * StartOffset;
|
||||
_backgroundArea.ViewOffset = Float2.Zero;
|
||||
Zoom = viewWidth / timelineWidth;
|
||||
const float padding = 40f;
|
||||
Zoom = (_backgroundArea.Width - padding * 2f) / (Duration * UnitsPerSecond);
|
||||
_backgroundArea.ViewOffset = new Float2(-_leftEdge.X + padding, _backgroundArea.ViewOffset.Y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -0,0 +1,289 @@
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using FlaxEditor.Options;
|
||||
using FlaxEditor.Viewport;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
|
||||
namespace FlaxEditor.Gizmo;
|
||||
|
||||
[HideInEditor]
|
||||
internal class DirectionGizmo : ContainerControl
|
||||
{
|
||||
public const float DefaultGizmoSize = 112.5f;
|
||||
|
||||
private const float AxisLength = 71.25f;
|
||||
private const float SpriteRadius = 10.85f;
|
||||
|
||||
private IGizmoOwner _owner;
|
||||
private ViewportProjection _viewportProjection;
|
||||
private EditorViewport _viewport;
|
||||
private Vector3 _gizmoCenter;
|
||||
private float _gizmoBrightness;
|
||||
private float _gizmoOpacity;
|
||||
private float _backgroundOpacity;
|
||||
private float _axisLength;
|
||||
private float _spriteRadius;
|
||||
|
||||
private AxisData _xAxisData;
|
||||
private AxisData _yAxisData;
|
||||
private AxisData _zAxisData;
|
||||
private AxisData _negXAxisData;
|
||||
private AxisData _negYAxisData;
|
||||
private AxisData _negZAxisData;
|
||||
|
||||
private List<AxisData> _axisData = new List<AxisData>();
|
||||
private int _hoveredAxisIndex = -1;
|
||||
|
||||
private SpriteHandle _posHandle;
|
||||
private SpriteHandle _negHandle;
|
||||
|
||||
private FontReference _fontReference;
|
||||
|
||||
// Store sprite positions for hover detection
|
||||
private List<(Float2 position, AxisDirection direction)> _spritePositions = new List<(Float2, AxisDirection)>();
|
||||
|
||||
private struct ViewportProjection
|
||||
{
|
||||
private Matrix _viewProjection;
|
||||
private BoundingFrustum _frustum;
|
||||
private FlaxEngine.Viewport _viewport;
|
||||
private Vector3 _origin;
|
||||
|
||||
public void Init(EditorViewport editorViewport)
|
||||
{
|
||||
// Inline EditorViewport.ProjectPoint to save on calculation for large set of points
|
||||
_viewport = new FlaxEngine.Viewport(0, 0, editorViewport.Width, editorViewport.Height);
|
||||
_frustum = editorViewport.ViewFrustum;
|
||||
_viewProjection = _frustum.Matrix;
|
||||
_origin = editorViewport.Task.View.Origin;
|
||||
}
|
||||
|
||||
public void ProjectPoint(Vector3 worldSpaceLocation, out Float2 viewportSpaceLocation)
|
||||
{
|
||||
worldSpaceLocation -= _origin;
|
||||
_viewport.Project(ref worldSpaceLocation, ref _viewProjection, out var projected);
|
||||
viewportSpaceLocation = new Float2((float)projected.X, (float)projected.Y);
|
||||
}
|
||||
}
|
||||
|
||||
private struct AxisData
|
||||
{
|
||||
public Float2 Delta;
|
||||
public float Distance;
|
||||
public string Label;
|
||||
public Color AxisColor;
|
||||
public bool Negative;
|
||||
public AxisDirection Direction;
|
||||
}
|
||||
|
||||
private enum AxisDirection
|
||||
{
|
||||
PosX,
|
||||
PosY,
|
||||
PosZ,
|
||||
NegX,
|
||||
NegY,
|
||||
NegZ
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor of the Direction Gizmo
|
||||
/// </summary>
|
||||
/// <param name="owner">The owner of this object.</param>
|
||||
public DirectionGizmo(IGizmoOwner owner)
|
||||
{
|
||||
_owner = owner;
|
||||
_viewport = owner.Viewport;
|
||||
_viewportProjection.Init(owner.Viewport);
|
||||
|
||||
_xAxisData = new AxisData { Delta = new Float2(0, 0), Distance = 0, Label = "X", AxisColor = new Color(1.0f, 0.0f, 0.02745f, 1.0f), Negative = false, Direction = AxisDirection.PosX };
|
||||
_yAxisData = new AxisData { Delta = new Float2(0, 0), Distance = 0, Label = "Y", AxisColor = new Color(0.239215f, 1.0f, 0.047058f, 1.0f), Negative = false, Direction = AxisDirection.PosY };
|
||||
_zAxisData = new AxisData { Delta = new Float2(0, 0), Distance = 0, Label = "Z", AxisColor = new Color(0.0f, 0.3607f, 0.9f, 1.0f), Negative = false, Direction = AxisDirection.PosZ };
|
||||
|
||||
_negXAxisData = new AxisData { Delta = new Float2(0, 0), Distance = 0, Label = "-X", AxisColor = new Color(1.0f, 0.0f, 0.02745f, 1.0f), Negative = true, Direction = AxisDirection.NegX };
|
||||
_negYAxisData = new AxisData { Delta = new Float2(0, 0), Distance = 0, Label = "-Y", AxisColor = new Color(0.239215f, 1.0f, 0.047058f, 1.0f), Negative = true, Direction = AxisDirection.NegY };
|
||||
_negZAxisData = new AxisData { Delta = new Float2(0, 0), Distance = 0, Label = "-Z", AxisColor = new Color(0.0f, 0.3607f, 0.9f, 1.0f), Negative = true, Direction = AxisDirection.NegZ };
|
||||
_axisData.EnsureCapacity(6);
|
||||
_spritePositions.EnsureCapacity(6);
|
||||
|
||||
var editor = Editor.Instance;
|
||||
_posHandle = editor.Icons.VisjectBoxClosed32;
|
||||
_negHandle = editor.Icons.VisjectBoxOpen32;
|
||||
|
||||
_fontReference = new FontReference(Style.Current.FontSmall);
|
||||
|
||||
editor.Options.OptionsChanged += OnEditorOptionsChanged;
|
||||
OnEditorOptionsChanged(editor.Options.Options);
|
||||
}
|
||||
|
||||
private void OnEditorOptionsChanged(EditorOptions options)
|
||||
{
|
||||
float gizmoScale = options.Viewport.DirectionGizmoScale;
|
||||
_axisLength = AxisLength * gizmoScale;
|
||||
_spriteRadius = SpriteRadius * gizmoScale;
|
||||
_gizmoBrightness = options.Viewport.DirectionGizmoBrightness;
|
||||
_gizmoOpacity = options.Viewport.DirectionGizmoOpacity;
|
||||
_backgroundOpacity = options.Viewport.DirectionGizmoBackgroundOpacity;
|
||||
|
||||
_fontReference.Size = 8.25f * gizmoScale;
|
||||
}
|
||||
|
||||
private bool IsPointInSprite(Float2 point, Float2 spriteCenter)
|
||||
{
|
||||
Float2 delta = point - spriteCenter;
|
||||
float distanceSq = delta.LengthSquared;
|
||||
float radiusSq = _spriteRadius * _spriteRadius;
|
||||
return distanceSq <= radiusSq;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnMouseMove(Float2 location)
|
||||
{
|
||||
_hoveredAxisIndex = -1;
|
||||
|
||||
// Check which axis is being hovered - check from closest to farthest for proper layering
|
||||
for (int i = _spritePositions.Count - 1; i >= 0; i--)
|
||||
{
|
||||
if (IsPointInSprite(location, _spritePositions[i].position))
|
||||
{
|
||||
_hoveredAxisIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
base.OnMouseMove(location);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool OnMouseDown(Float2 location, MouseButton button)
|
||||
{
|
||||
if (base.OnMouseDown(location, button))
|
||||
return true;
|
||||
|
||||
// Check which axis is being clicked - check from closest to farthest for proper layering
|
||||
for (int i = _spritePositions.Count - 1; i >= 0; i--)
|
||||
{
|
||||
if (IsPointInSprite(location, _spritePositions[i].position))
|
||||
{
|
||||
OrientViewToAxis(_spritePositions[i].direction);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void OrientViewToAxis(AxisDirection direction)
|
||||
{
|
||||
Quaternion orientation = direction switch
|
||||
{
|
||||
AxisDirection.PosX => Quaternion.Euler(0, -90, 0),
|
||||
AxisDirection.NegX => Quaternion.Euler(0, 90, 0),
|
||||
AxisDirection.PosY => Quaternion.Euler(90, 0, 0),
|
||||
AxisDirection.NegY => Quaternion.Euler(-90, 0, 0),
|
||||
AxisDirection.PosZ => Quaternion.Euler(0, 180, 0),
|
||||
AxisDirection.NegZ => Quaternion.Euler(0, 0, 0),
|
||||
_ => Quaternion.Identity
|
||||
};
|
||||
_viewport.OrientViewport(ref orientation);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used to Draw the gizmo.
|
||||
/// </summary>
|
||||
public override void DrawSelf()
|
||||
{
|
||||
base.DrawSelf();
|
||||
|
||||
var features = Render2D.Features;
|
||||
Render2D.Features = features & ~Render2D.RenderingFeatures.VertexSnapping;
|
||||
_viewportProjection.Init(_owner.Viewport);
|
||||
_gizmoCenter = _viewport.Task.View.WorldPosition + _viewport.Task.View.Direction * 1500;
|
||||
_viewportProjection.ProjectPoint(_gizmoCenter, out var gizmoCenterScreen);
|
||||
|
||||
var relativeCenter = Size * 0.5f;
|
||||
|
||||
// Project unit vectors
|
||||
_viewportProjection.ProjectPoint(_gizmoCenter + Vector3.Right, out var xProjected);
|
||||
_viewportProjection.ProjectPoint(_gizmoCenter + Vector3.Up, out var yProjected);
|
||||
_viewportProjection.ProjectPoint(_gizmoCenter + Vector3.Forward, out var zProjected);
|
||||
_viewportProjection.ProjectPoint(_gizmoCenter - Vector3.Right, out var negXProjected);
|
||||
_viewportProjection.ProjectPoint(_gizmoCenter - Vector3.Up, out var negYProjected);
|
||||
_viewportProjection.ProjectPoint(_gizmoCenter - Vector3.Forward, out var negZProjected);
|
||||
|
||||
// Normalize by viewport height to keep size independent of FOV and viewport dimensions
|
||||
float heightNormalization = _viewport.Height / 720.0f; // 720 = reference height
|
||||
if (_owner.Viewport.UseOrthographicProjection)
|
||||
heightNormalization /= _owner.Viewport.OrthographicScale * 0.5f; // Fix in ortho view to keep consistent size regardless of zoom level
|
||||
|
||||
Float2 xDelta = (xProjected - gizmoCenterScreen) / heightNormalization;
|
||||
Float2 yDelta = (yProjected - gizmoCenterScreen) / heightNormalization;
|
||||
Float2 zDelta = (zProjected - gizmoCenterScreen) / heightNormalization;
|
||||
Float2 negXDelta = (negXProjected - gizmoCenterScreen) / heightNormalization;
|
||||
Float2 negYDelta = (negYProjected - gizmoCenterScreen) / heightNormalization;
|
||||
Float2 negZDelta = (negZProjected - gizmoCenterScreen) / heightNormalization;
|
||||
|
||||
// Calculate distances from camera to determine draw order
|
||||
Vector3 cameraPosition = _viewport.Task.View.Position;
|
||||
float xDistance = (float)Vector3.Distance(cameraPosition, _gizmoCenter + Vector3.Right);
|
||||
float yDistance = (float)Vector3.Distance(cameraPosition, _gizmoCenter + Vector3.Up);
|
||||
float zDistance = (float)Vector3.Distance(cameraPosition, _gizmoCenter + Vector3.Forward);
|
||||
float negXDistance = (float)Vector3.Distance(cameraPosition, _gizmoCenter - Vector3.Right);
|
||||
float negYDistance = (float)Vector3.Distance(cameraPosition, _gizmoCenter - Vector3.Up);
|
||||
float negZDistance = (float)Vector3.Distance(cameraPosition, _gizmoCenter - Vector3.Forward);
|
||||
|
||||
_xAxisData.Delta = xDelta;
|
||||
_xAxisData.Distance = xDistance;
|
||||
_yAxisData.Delta = yDelta;
|
||||
_yAxisData.Distance = yDistance;
|
||||
_zAxisData.Delta = zDelta;
|
||||
_zAxisData.Distance = zDistance;
|
||||
_negXAxisData.Delta = negXDelta;
|
||||
_negXAxisData.Distance = negXDistance;
|
||||
_negYAxisData.Delta = negYDelta;
|
||||
_negYAxisData.Distance = negYDistance;
|
||||
_negZAxisData.Delta = negZDelta;
|
||||
_negZAxisData.Distance = negZDistance;
|
||||
|
||||
// Sort for correct draw order.
|
||||
_axisData.Clear();
|
||||
_axisData.AddRange([_xAxisData, _yAxisData, _zAxisData, _negXAxisData, _negYAxisData, _negZAxisData]);
|
||||
_axisData.Sort((a, b) => -a.Distance.CompareTo(b.Distance));
|
||||
|
||||
// Rebuild sprite positions list for hover detection
|
||||
_spritePositions.Clear();
|
||||
|
||||
Render2D.DrawSprite(_posHandle, new Rectangle(0, 0, Size), Color.Black.AlphaMultiplied(_backgroundOpacity));
|
||||
|
||||
// Draw in order from farthest to closest
|
||||
for (int i = 0; i < _axisData.Count; i++)
|
||||
{
|
||||
var axis = _axisData[i];
|
||||
Float2 tipScreen = relativeCenter + axis.Delta * _axisLength;
|
||||
bool isHovered = _hoveredAxisIndex == i;
|
||||
|
||||
// Store sprite position for hover detection
|
||||
_spritePositions.Add((tipScreen, axis.Direction));
|
||||
|
||||
var axisColor = isHovered ? new Color(1.0f, 0.8980392f, 0.039215688f) : axis.AxisColor;
|
||||
axisColor = axisColor.RGBMultiplied(_gizmoBrightness).AlphaMultiplied(_gizmoOpacity);
|
||||
var font = _fontReference.GetFont();
|
||||
if (!axis.Negative)
|
||||
{
|
||||
Render2D.DrawLine(relativeCenter, tipScreen, axisColor, 1.5f);
|
||||
Render2D.DrawSprite(_posHandle, new Rectangle(tipScreen - new Float2(_spriteRadius), new Float2(_spriteRadius * 2)), axisColor);
|
||||
Render2D.DrawText(font, axis.Label, Color.Black, tipScreen - font.MeasureText(axis.Label) * 0.5f);
|
||||
}
|
||||
else
|
||||
{
|
||||
Render2D.DrawSprite(_posHandle, new Rectangle(tipScreen - new Float2(_spriteRadius), new Float2(_spriteRadius * 2)), axisColor.RGBMultiplied(0.85f).AlphaMultiplied(0.8f));
|
||||
if (isHovered)
|
||||
Render2D.DrawText(font, axis.Label, Color.Black, tipScreen - font.MeasureText(axis.Label) * 0.5f);
|
||||
}
|
||||
}
|
||||
|
||||
Render2D.Features = features;
|
||||
}
|
||||
}
|
||||
@@ -180,6 +180,11 @@ namespace FlaxEditor
|
||||
/// </summary>
|
||||
public bool EnableCamera => _view != null && EnableBackground;
|
||||
|
||||
/// <summary>
|
||||
/// True if enable grid drawing.
|
||||
/// </summary>
|
||||
public bool ShowGrid { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Transform gizmo to use sync with (selection, snapping, transformation settings).
|
||||
/// </summary>
|
||||
@@ -387,19 +392,53 @@ namespace FlaxEditor
|
||||
if (_mouseMovesWidget && _activeWidget.UIControl)
|
||||
{
|
||||
// Calculate transform delta
|
||||
var resizeAxisAbs = _activeWidget.ResizeAxis.Absolute;
|
||||
var resizeAxisPos = Float2.Clamp(_activeWidget.ResizeAxis, Float2.Zero, Float2.One);
|
||||
var resizeAxisNeg = Float2.Clamp(-_activeWidget.ResizeAxis, Float2.Zero, Float2.One);
|
||||
var delta = location - _mouseMovesPos;
|
||||
// TODO: scale/size snapping?
|
||||
delta *= resizeAxisAbs;
|
||||
|
||||
// Resize control via widget
|
||||
var moveLocation = _mouseMovesPos + delta;
|
||||
var control = _activeWidget.UIControl.Control;
|
||||
var uiControlDelta = GetControlDelta(control, ref _mouseMovesPos, ref moveLocation);
|
||||
control.LocalLocation += uiControlDelta * resizeAxisNeg;
|
||||
control.Size += uiControlDelta * resizeAxisPos - uiControlDelta * resizeAxisNeg;
|
||||
|
||||
// Transform delta to control local space
|
||||
var rotation = GetTotalRotation(control) * Mathf.DegreesToRadians;
|
||||
var cos = Mathf.Cos(rotation);
|
||||
var sin = Mathf.Sin(rotation);
|
||||
var localDeltaX = uiControlDelta.X * cos + uiControlDelta.Y * sin;
|
||||
var localDeltaY = uiControlDelta.Y * cos - uiControlDelta.X * sin;
|
||||
var localDelta = new Float2(localDeltaX, localDeltaY);
|
||||
localDelta *= _activeWidget.ResizeAxis.Absolute;
|
||||
|
||||
// Calculate size change
|
||||
var resizeAxisPos = Float2.Clamp(_activeWidget.ResizeAxis, Float2.Zero, Float2.One);
|
||||
var resizeAxisNeg = Float2.Clamp(-_activeWidget.ResizeAxis, Float2.Zero, Float2.One);
|
||||
var dSizeScaled = localDelta * resizeAxisPos - localDelta * resizeAxisNeg;
|
||||
var scale = control.Scale;
|
||||
var dSize = new Float2(
|
||||
Mathf.Abs(scale.X) > Mathf.Epsilon ? dSizeScaled.X / scale.X : 0,
|
||||
Mathf.Abs(scale.Y) > Mathf.Epsilon ? dSizeScaled.Y / scale.Y : 0);
|
||||
|
||||
// Apply size change
|
||||
control.Size += dSize;
|
||||
|
||||
// Calculate location offset to keep the opposite edge stationary
|
||||
// When PivotRelative is false, resizing keeps Top-Left (Location) constant,
|
||||
// so we only need to slide back if we are resizing Left or Top edges.
|
||||
if (!control.PivotRelative)
|
||||
{
|
||||
var pivotOffset = Float2.Zero;
|
||||
if (_activeWidget.ResizeAxis.X < 0 && Mathf.Abs(dSize.X) > Mathf.Epsilon)
|
||||
pivotOffset.X = -dSize.X * scale.X;
|
||||
if (_activeWidget.ResizeAxis.Y < 0 && Mathf.Abs(dSize.Y) > Mathf.Epsilon)
|
||||
pivotOffset.Y = -dSize.Y * scale.Y;
|
||||
|
||||
// Transform offset back to parent space and apply
|
||||
var dLocationX = pivotOffset.X * cos - pivotOffset.Y * sin;
|
||||
var dLocationY = pivotOffset.X * sin + pivotOffset.Y * cos;
|
||||
var dLocation = new Float2(dLocationX, dLocationY);
|
||||
|
||||
control.LocalLocation += dLocation;
|
||||
}
|
||||
|
||||
// Don't move if layout doesn't allow it
|
||||
if (control.Parent != null)
|
||||
@@ -492,17 +531,20 @@ namespace FlaxEditor
|
||||
// Draw background
|
||||
Surface.VisjectSurface.DrawBackgroundDefault(Editor.Instance.UI.VisjectSurfaceBackground, Width, Height);
|
||||
|
||||
// Draw grid
|
||||
var viewRect = GetClientArea();
|
||||
var upperLeft = _view.PointFromParent(viewRect.Location);
|
||||
var bottomRight = _view.PointFromParent(viewRect.Size);
|
||||
var min = Float2.Min(upperLeft, bottomRight);
|
||||
var max = Float2.Max(upperLeft, bottomRight);
|
||||
var pixelRange = (max - min) * ViewScale;
|
||||
Render2D.PushClip(ref viewRect);
|
||||
DrawAxis(Float2.UnitX, viewRect, min.X, max.X, pixelRange.X);
|
||||
DrawAxis(Float2.UnitY, viewRect, min.Y, max.Y, pixelRange.Y);
|
||||
Render2D.PopClip();
|
||||
if (ShowGrid)
|
||||
{
|
||||
// Draw grid
|
||||
var viewRect = GetClientArea();
|
||||
var upperLeft = _view.PointFromParent(viewRect.Location);
|
||||
var bottomRight = _view.PointFromParent(viewRect.Size);
|
||||
var min = Float2.Min(upperLeft, bottomRight);
|
||||
var max = Float2.Max(upperLeft, bottomRight);
|
||||
var pixelRange = (max - min) * ViewScale;
|
||||
Render2D.PushClip(ref viewRect);
|
||||
DrawAxis(Float2.UnitX, viewRect, min.X, max.X, pixelRange.X);
|
||||
DrawAxis(Float2.UnitY, viewRect, min.Y, max.Y, pixelRange.Y);
|
||||
Render2D.PopClip();
|
||||
}
|
||||
}
|
||||
|
||||
base.Draw();
|
||||
@@ -634,7 +676,7 @@ namespace FlaxEditor
|
||||
// Draw sizing widgets
|
||||
if (_widgets == null)
|
||||
_widgets = new List<Widget>();
|
||||
var widgetSize = 10.0f;
|
||||
var widgetSize = 8.0f;
|
||||
var viewScale = ViewScale;
|
||||
if (viewScale < 0.7f)
|
||||
widgetSize *= viewScale;
|
||||
@@ -685,7 +727,7 @@ namespace FlaxEditor
|
||||
anchorRectSize *= viewScale;
|
||||
|
||||
// Make anchor rects and rotate if parent is rotated.
|
||||
var parentRotation = controlParent.Rotation * Mathf.DegreesToRadians;
|
||||
var parentRotation = GetTotalRotation(controlParent) * Mathf.DegreesToRadians;
|
||||
|
||||
var rect1Axis = new Float2(-1, -1);
|
||||
var rect1 = new Rectangle(anchorUpperLeft +
|
||||
@@ -717,17 +759,25 @@ namespace FlaxEditor
|
||||
}
|
||||
}
|
||||
|
||||
private float GetTotalRotation(Control control)
|
||||
{
|
||||
if (control.Parent != null)
|
||||
return control.Rotation + GetTotalRotation(control.Parent);
|
||||
return control.Rotation;
|
||||
}
|
||||
|
||||
private void DrawControlWidget(UIControl uiControl, ref Float2 pos, ref Float2 mousePos, ref Float2 size, float scale, Float2 resizeAxis, CursorType cursor)
|
||||
{
|
||||
var style = Style.Current;
|
||||
var control = uiControl.Control;
|
||||
var rotation = control.Rotation;
|
||||
var rotation = GetTotalRotation(control);
|
||||
var rotationInRadians = rotation * Mathf.DegreesToRadians;
|
||||
var rect = new Rectangle((pos +
|
||||
new Float2(resizeAxis.X * Mathf.Cos(rotationInRadians) - resizeAxis.Y * Mathf.Sin(rotationInRadians),
|
||||
resizeAxis.Y * Mathf.Cos(rotationInRadians) + resizeAxis.X * Mathf.Sin(rotationInRadians)) * 10 * scale) - size * 0.5f,
|
||||
size);
|
||||
|
||||
var position = (pos + new Float2(resizeAxis.X * Mathf.Cos(rotationInRadians) - resizeAxis.Y * Mathf.Sin(rotationInRadians),
|
||||
resizeAxis.Y * Mathf.Cos(rotationInRadians) + resizeAxis.X * Mathf.Sin(rotationInRadians)) * 4 * (scale < 0.7f ? scale : 1));
|
||||
var halfSize = size * 0.5f;
|
||||
// Keep at 0, 0 rect position until later to correctly render rotation.
|
||||
var rect = new Rectangle(0, 0, size);
|
||||
|
||||
// Find more correct cursor at different angles
|
||||
var unwindRotation = Mathf.UnwindDegrees(rotation);
|
||||
if (unwindRotation is (>= 45 and < 135) or (> -135 and <= -45) )
|
||||
@@ -749,6 +799,10 @@ namespace FlaxEditor
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
Render2D.PushTransform(Matrix3x3.Translation2D(position));
|
||||
Render2D.PushTransform(Matrix3x3.RotationZ(rotationInRadians));
|
||||
Render2D.PushTransform(Matrix3x3.Translation2D(-1 * halfSize));
|
||||
if (rect.Contains(ref mousePos))
|
||||
{
|
||||
Render2D.FillRectangle(rect, style.Foreground);
|
||||
@@ -759,9 +813,14 @@ namespace FlaxEditor
|
||||
Render2D.FillRectangle(rect, style.ForegroundGrey);
|
||||
Render2D.DrawRectangle(rect, style.Foreground);
|
||||
}
|
||||
Render2D.PopTransform();
|
||||
Render2D.PopTransform();
|
||||
Render2D.PopTransform();
|
||||
|
||||
if (!_mouseMovesWidget && uiControl != null)
|
||||
{
|
||||
// Collect widget
|
||||
rect.Location = position - halfSize;
|
||||
_widgets.Add(new Widget
|
||||
{
|
||||
UIControl = uiControl,
|
||||
|
||||
@@ -325,7 +325,7 @@ namespace FlaxEditor.Modules.SourceCodeEditing
|
||||
var codeEditor = options.SourceCode.SourceCodeEditor;
|
||||
if (codeEditor != "None")
|
||||
{
|
||||
foreach (var e in Editor.Instance.CodeEditing.Editors)
|
||||
foreach (var e in _editors)
|
||||
{
|
||||
if (string.Equals(codeEditor, e.Name, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
@@ -334,7 +334,10 @@ namespace FlaxEditor.Modules.SourceCodeEditing
|
||||
}
|
||||
}
|
||||
}
|
||||
Editor.Instance.CodeEditing.SelectedEditor = editor;
|
||||
if (editor == null && _editors.Count != 0)
|
||||
editor = _editors[0];
|
||||
|
||||
SelectedEditor = editor;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -41,9 +41,9 @@ namespace FlaxEditor.Modules.SourceCodeEditing
|
||||
var vsCode = codeEditing.GetInBuildEditor(CodeEditorTypes.VSCode);
|
||||
var rider = codeEditing.GetInBuildEditor(CodeEditorTypes.Rider);
|
||||
|
||||
#if PLATFORM_WINDOW
|
||||
#if PLATFORM_WINDOWS
|
||||
// Favor the newest Visual Studio
|
||||
for (int i = (int)CodeEditorTypes.VS2019; i >= (int)CodeEditorTypes.VS2008; i--)
|
||||
for (int i = (int)CodeEditorTypes.VS2026; i >= (int)CodeEditorTypes.VS2008; i--)
|
||||
{
|
||||
var visualStudio = codeEditing.GetInBuildEditor((CodeEditorTypes)i);
|
||||
if (visualStudio != null)
|
||||
@@ -74,7 +74,7 @@ namespace FlaxEditor.Modules.SourceCodeEditing
|
||||
public string Name => "Default";
|
||||
|
||||
/// <inheritdoc />
|
||||
public string GenerateProjectCustomArgs => null;
|
||||
public string GenerateProjectCustomArgs => _currentEditor?.GenerateProjectCustomArgs;
|
||||
|
||||
/// <inheritdoc />
|
||||
public void OpenSolution()
|
||||
|
||||
@@ -22,71 +22,14 @@ namespace FlaxEditor.Modules.SourceCodeEditing
|
||||
public InBuildSourceCodeEditor(CodeEditorTypes type)
|
||||
{
|
||||
Type = type;
|
||||
switch (type)
|
||||
{
|
||||
case CodeEditorTypes.Custom:
|
||||
Name = "Custom";
|
||||
break;
|
||||
case CodeEditorTypes.SystemDefault:
|
||||
Name = "System Default";
|
||||
break;
|
||||
case CodeEditorTypes.VS2008:
|
||||
Name = "Visual Studio 2008";
|
||||
break;
|
||||
case CodeEditorTypes.VS2010:
|
||||
Name = "Visual Studio 2010";
|
||||
break;
|
||||
case CodeEditorTypes.VS2012:
|
||||
Name = "Visual Studio 2012";
|
||||
break;
|
||||
case CodeEditorTypes.VS2013:
|
||||
Name = "Visual Studio 2013";
|
||||
break;
|
||||
case CodeEditorTypes.VS2015:
|
||||
Name = "Visual Studio 2015";
|
||||
break;
|
||||
case CodeEditorTypes.VS2017:
|
||||
Name = "Visual Studio 2017";
|
||||
break;
|
||||
case CodeEditorTypes.VS2019:
|
||||
Name = "Visual Studio 2019";
|
||||
break;
|
||||
case CodeEditorTypes.VS2022:
|
||||
Name = "Visual Studio 2022";
|
||||
break;
|
||||
case CodeEditorTypes.VS2026:
|
||||
Name = "Visual Studio 2026";
|
||||
break;
|
||||
case CodeEditorTypes.VSCode:
|
||||
Name = "Visual Studio Code";
|
||||
break;
|
||||
case CodeEditorTypes.VSCodeInsiders:
|
||||
Name = "Visual Studio Code - Insiders";
|
||||
break;
|
||||
case CodeEditorTypes.Rider:
|
||||
Name = "Rider";
|
||||
break;
|
||||
default: throw new ArgumentOutOfRangeException(nameof(type), type, null);
|
||||
}
|
||||
Name = CodeEditingManager.GetName(type);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public string GenerateProjectCustomArgs
|
||||
{
|
||||
get
|
||||
{
|
||||
switch (Type)
|
||||
{
|
||||
case CodeEditorTypes.VSCodeInsiders:
|
||||
case CodeEditorTypes.VSCode: return "-vscode -vs2022";
|
||||
case CodeEditorTypes.Rider: return "-vs2022";
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
public string GenerateProjectCustomArgs => CodeEditingManager.GetGenerateProjectCustomArgs(Type);
|
||||
|
||||
/// <inheritdoc />
|
||||
public void OpenSolution()
|
||||
|
||||
@@ -710,6 +710,10 @@ namespace FlaxEditor.Options
|
||||
[EditorDisplay("Node Editors"), EditorOrder(4580)]
|
||||
public InputBinding NodesDistributeVertical = new InputBinding(KeyboardKeys.A, KeyboardKeys.Alt);
|
||||
|
||||
[DefaultValue(typeof(InputBinding), "Shift+F")]
|
||||
[EditorDisplay("Node Editors"), EditorOrder(4590)]
|
||||
public InputBinding FocusSelectedNodes = new InputBinding(KeyboardKeys.F, KeyboardKeys.Shift);
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -159,7 +159,7 @@ namespace FlaxEditor.Options
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Options focus Game Window behaviour when play mode is entered.
|
||||
/// Options for focus Game Window behaviour when play mode is entered.
|
||||
/// </summary>
|
||||
public enum PlayModeFocus
|
||||
{
|
||||
@@ -179,6 +179,22 @@ namespace FlaxEditor.Options
|
||||
GameWindowThenRestore,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generic options for a disabled or hidden state. Used for example in create content button.
|
||||
/// </summary>
|
||||
public enum DisabledHidden
|
||||
{
|
||||
/// <summary>
|
||||
/// Disabled state.
|
||||
/// </summary>
|
||||
Disabled,
|
||||
|
||||
/// <summary>
|
||||
/// Hidden state.
|
||||
/// </summary>
|
||||
Hidden,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Editor User Interface scale. Applied to all UI elements, windows and text. Can be used to scale the interface up on a bigger display. Editor restart required.
|
||||
/// </summary>
|
||||
@@ -207,13 +223,6 @@ namespace FlaxEditor.Options
|
||||
[EditorDisplay("Interface"), EditorOrder(280), Tooltip("Editor content window orientation.")]
|
||||
public FlaxEngine.GUI.Orientation ContentWindowOrientation { get; set; } = FlaxEngine.GUI.Orientation.Horizontal;
|
||||
|
||||
/// <summary>
|
||||
/// If checked, color pickers will always modify the color unless 'Cancel' if pressed, otherwise color won't change unless 'Ok' is pressed.
|
||||
/// </summary>
|
||||
[DefaultValue(true)]
|
||||
[EditorDisplay("Interface"), EditorOrder(290)]
|
||||
public bool AutoAcceptColorPickerChange { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the formatting option for numeric values in the editor.
|
||||
/// </summary>
|
||||
@@ -525,6 +534,13 @@ namespace FlaxEditor.Options
|
||||
[EditorDisplay("Visject", "Warn when deleting used parameter"), EditorOrder(552)]
|
||||
public bool WarnOnDeletingUsedVisjectParameter { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating what should happen to unavaliable options in the content create menu.
|
||||
/// </summary>
|
||||
[DefaultValue(DisabledHidden.Hidden)]
|
||||
[EditorDisplay("Content"), EditorOrder(600)]
|
||||
public DisabledHidden UnavaliableContentCreateOptions { get; set; } = DisabledHidden.Hidden;
|
||||
|
||||
private static FontAsset DefaultFont => FlaxEngine.Content.LoadAsyncInternal<FontAsset>(EditorAssets.PrimaryFont);
|
||||
private static FontAsset ConsoleFont => FlaxEngine.Content.LoadAsyncInternal<FontAsset>(EditorAssets.InconsolataRegularFont);
|
||||
|
||||
|
||||
@@ -171,5 +171,40 @@ namespace FlaxEditor.Options
|
||||
[DefaultValue(1000.0f), Limit(0.0f, 20000.0f, 5.0f)]
|
||||
[EditorDisplay("Viewport Icons"), EditorOrder(410)]
|
||||
public float MaxSizeDistance { get; set; } = 1000.0f;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value that indicates whether the main viewports <see cref="Gizmo.DirectionGizmo"/> is visible.
|
||||
/// </summary>
|
||||
[DefaultValue(true)]
|
||||
[EditorDisplay("Direction Gizmo"), EditorOrder(500), Tooltip("Sets the visibility of the direction gizmo in the main editor viewport.")]
|
||||
public bool ShowDirectionGizmo { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value by which the main viewports <see cref="Gizmo.DirectionGizmo"/> size is multiplied with.
|
||||
/// </summary>
|
||||
[DefaultValue(1f), Limit(0.0f, 2.0f)]
|
||||
[EditorDisplay("Direction Gizmo"), EditorOrder(501), Tooltip("The scale of the direction gizmo in the main viewport.")]
|
||||
public float DirectionGizmoScale { get; set; } = 1f;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value for the opacity of the main viewports <see cref="Gizmo.DirectionGizmo"/> background.
|
||||
/// </summary>
|
||||
[DefaultValue(0.1f), Limit(0.0f, 1.0f)]
|
||||
[EditorDisplay("Direction Gizmo"), EditorOrder(502), Tooltip("The background opacity of the of the direction gizmo in the main viewport.")]
|
||||
public float DirectionGizmoBackgroundOpacity { get; set; } = 0.1f;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value for the opacity of the main viewports <see cref="Gizmo.DirectionGizmo"/>.
|
||||
/// </summary>
|
||||
[DefaultValue(0.6f), Limit(0.0f, 1.0f)]
|
||||
[EditorDisplay("Direction Gizmo"), EditorOrder(503), Tooltip("The opacity of the of the direction gizmo in the main viewport.")]
|
||||
public float DirectionGizmoOpacity { get; set; } = 0.6f;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value for the opacity of the main viewports <see cref="Gizmo.DirectionGizmo"/>.
|
||||
/// </summary>
|
||||
[DefaultValue(1f), Limit(0.0f, 2.0f)]
|
||||
[EditorDisplay("Direction Gizmo"), EditorOrder(504), Tooltip("The brightness of the of the direction gizmo in the main viewport.")]
|
||||
public float DirectionGizmoBrightness{ get; set; } = 1f;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.SceneGraph.Actors
|
||||
{
|
||||
/// <summary>
|
||||
/// Scene tree node for <see cref="Cloth"/> actor type.
|
||||
/// </summary>
|
||||
[HideInEditor]
|
||||
public sealed class ClothNode : ActorNode
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public ClothNode(Actor actor)
|
||||
: base(actor)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void PostSpawn()
|
||||
{
|
||||
base.PostSpawn();
|
||||
|
||||
// Snap to the parent
|
||||
if (!(ParentNode is SceneNode))
|
||||
Actor.LocalTransform = Transform.Identity;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.SceneGraph.Actors
|
||||
{
|
||||
/// <summary>
|
||||
/// Scene tree node for <see cref="RigidBody"/> actor type.
|
||||
/// </summary>
|
||||
[HideInEditor]
|
||||
public sealed class RigidBodyNode : ActorNode
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public RigidBodyNode(Actor actor)
|
||||
: base(actor)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void PostSpawn()
|
||||
{
|
||||
base.PostSpawn();
|
||||
|
||||
if (HasPrefabLink)
|
||||
return;
|
||||
Actor.StaticFlags = StaticFlags.None;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -324,13 +324,12 @@ namespace FlaxEditor.SceneGraph.GUI
|
||||
isExpanded = Editor.Instance.ProjectCache.IsExpandedActor(ref id);
|
||||
}
|
||||
|
||||
if (isExpanded)
|
||||
if (!noFilter)
|
||||
{
|
||||
Expand(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
Collapse(true);
|
||||
if (isExpanded)
|
||||
Expand(true);
|
||||
else
|
||||
Collapse(true);
|
||||
}
|
||||
|
||||
Visible = isThisVisible | isAnyChildVisible;
|
||||
|
||||
@@ -51,6 +51,7 @@ namespace FlaxEditor.SceneGraph
|
||||
CustomNodesTypes.Add(typeof(AudioSource), typeof(AudioSourceNode));
|
||||
CustomNodesTypes.Add(typeof(BoneSocket), typeof(BoneSocketNode));
|
||||
CustomNodesTypes.Add(typeof(Decal), typeof(DecalNode));
|
||||
CustomNodesTypes.Add(typeof(Cloth), typeof(ClothNode));
|
||||
CustomNodesTypes.Add(typeof(BoxCollider), typeof(BoxColliderNode));
|
||||
CustomNodesTypes.Add(typeof(SphereCollider), typeof(ColliderNode));
|
||||
CustomNodesTypes.Add(typeof(CapsuleCollider), typeof(ColliderNode));
|
||||
@@ -73,6 +74,7 @@ namespace FlaxEditor.SceneGraph
|
||||
CustomNodesTypes.Add(typeof(NavMesh), typeof(ActorNode));
|
||||
CustomNodesTypes.Add(typeof(SpriteRender), typeof(SpriteRenderNode));
|
||||
CustomNodesTypes.Add(typeof(Joint), typeof(JointNode));
|
||||
CustomNodesTypes.Add(typeof(RigidBody), typeof(RigidBodyNode));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -139,6 +139,34 @@ CodeEditor* CodeEditingManager::GetCodeEditor(CodeEditorTypes editorType)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
String CodeEditingManager::GetName(CodeEditorTypes editorType)
|
||||
{
|
||||
const auto editor = GetCodeEditor(editorType);
|
||||
if (editor)
|
||||
{
|
||||
return editor->GetName();
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG(Warning, "Missing code editor type {0}", (int32)editorType);
|
||||
return String::Empty;
|
||||
}
|
||||
}
|
||||
|
||||
String CodeEditingManager::GetGenerateProjectCustomArgs(CodeEditorTypes editorType)
|
||||
{
|
||||
const auto editor = GetCodeEditor(editorType);
|
||||
if (editor)
|
||||
{
|
||||
return editor->GetGenerateProjectCustomArgs();
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG(Warning, "Missing code editor type {0}", (int32)editorType);
|
||||
return String::Empty;
|
||||
}
|
||||
}
|
||||
|
||||
void CodeEditingManager::OpenFile(CodeEditorTypes editorType, const String& path, int32 line)
|
||||
{
|
||||
const auto editor = GetCodeEditor(editorType);
|
||||
|
||||
@@ -109,9 +109,18 @@ public:
|
||||
/// <summary>
|
||||
/// Gets the name of the editor.
|
||||
/// </summary>
|
||||
/// <returns>The name</returns>
|
||||
/// <returns>The name.</returns>
|
||||
virtual String GetName() const = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the custom arguments for the Flax.Build tool to add when generating project files for this code editor.
|
||||
/// </summary>
|
||||
/// <returns>The custom arguments to generate project files.</returns>
|
||||
virtual String GetGenerateProjectCustomArgs() const
|
||||
{
|
||||
return String::Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Opens the file.
|
||||
/// </summary>
|
||||
@@ -169,6 +178,20 @@ public:
|
||||
/// <returns>The editor object or null if not found.</returns>
|
||||
static CodeEditor* GetCodeEditor(CodeEditorTypes editorType);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the editor.
|
||||
/// </summary>
|
||||
/// <param name="editorType">The code editor type.</param>
|
||||
/// <returns>The name.</returns>
|
||||
API_FUNCTION() static String GetName(CodeEditorTypes editorType);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the custom arguments for the Flax.Build tool to add when generating project files for this code editor.
|
||||
/// </summary>
|
||||
/// <param name="editorType">The code editor type.</param>
|
||||
/// <returns>The custom arguments to generate project files.</returns>
|
||||
API_FUNCTION() static String GetGenerateProjectCustomArgs(CodeEditorTypes editorType);
|
||||
|
||||
/// <summary>
|
||||
/// Opens the file. Handles async opening.
|
||||
/// </summary>
|
||||
|
||||
@@ -257,12 +257,17 @@ String RiderCodeEditor::GetName() const
|
||||
return TEXT("Rider");
|
||||
}
|
||||
|
||||
String RiderCodeEditor::GetGenerateProjectCustomArgs() const
|
||||
{
|
||||
return TEXT("-vs2022");
|
||||
}
|
||||
|
||||
void RiderCodeEditor::OpenFile(const String& path, int32 line)
|
||||
{
|
||||
// Generate project files if solution is missing
|
||||
if (!FileSystem::FileExists(_solutionPath))
|
||||
{
|
||||
ScriptsBuilder::GenerateProject(TEXT("-vs2022"));
|
||||
ScriptsBuilder::GenerateProject(GetGenerateProjectCustomArgs());
|
||||
}
|
||||
|
||||
// Open file
|
||||
@@ -290,7 +295,7 @@ void RiderCodeEditor::OpenSolution()
|
||||
// Generate project files if solution is missing
|
||||
if (!FileSystem::FileExists(_solutionPath))
|
||||
{
|
||||
ScriptsBuilder::GenerateProject(TEXT("-vs2022"));
|
||||
ScriptsBuilder::GenerateProject(GetGenerateProjectCustomArgs());
|
||||
}
|
||||
|
||||
// Open solution
|
||||
@@ -312,5 +317,5 @@ void RiderCodeEditor::OpenSolution()
|
||||
|
||||
void RiderCodeEditor::OnFileAdded(const String& path)
|
||||
{
|
||||
ScriptsBuilder::GenerateProject();
|
||||
ScriptsBuilder::GenerateProject(GetGenerateProjectCustomArgs());
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@ public:
|
||||
// [CodeEditor]
|
||||
CodeEditorTypes GetType() const override;
|
||||
String GetName() const override;
|
||||
String GetGenerateProjectCustomArgs() const override;
|
||||
void OpenFile(const String& path, int32 line) override;
|
||||
void OpenSolution() override;
|
||||
void OnFileAdded(const String& path) override;
|
||||
|
||||
@@ -145,7 +145,46 @@ CodeEditorTypes VisualStudioEditor::GetType() const
|
||||
|
||||
String VisualStudioEditor::GetName() const
|
||||
{
|
||||
return String(ToString(_version));
|
||||
const Char* name;
|
||||
switch (_version)
|
||||
{
|
||||
case VisualStudioVersion::VS2008:
|
||||
name = TEXT("Visual Studio 2008");
|
||||
break;
|
||||
case VisualStudioVersion::VS2010:
|
||||
name = TEXT("Visual Studio 2010");
|
||||
break;
|
||||
case VisualStudioVersion::VS2012:
|
||||
name = TEXT("Visual Studio 2012");
|
||||
break;
|
||||
case VisualStudioVersion::VS2013:
|
||||
name = TEXT("Visual Studio 2013");
|
||||
break;
|
||||
case VisualStudioVersion::VS2015:
|
||||
name = TEXT("Visual Studio 2015");
|
||||
break;
|
||||
case VisualStudioVersion::VS2017:
|
||||
name = TEXT("Visual Studio 2017");
|
||||
break;
|
||||
case VisualStudioVersion::VS2019:
|
||||
name = TEXT("Visual Studio 2019");
|
||||
break;
|
||||
case VisualStudioVersion::VS2022:
|
||||
name = TEXT("Visual Studio 2022");
|
||||
break;
|
||||
case VisualStudioVersion::VS2026:
|
||||
name = TEXT("Visual Studio 2026");
|
||||
break;
|
||||
default:
|
||||
name = ToString(_version);
|
||||
break;
|
||||
}
|
||||
return String(name);
|
||||
}
|
||||
|
||||
String VisualStudioEditor::GetGenerateProjectCustomArgs() const
|
||||
{
|
||||
return String::Format(TEXT("-{0}"), String(ToString(_version)).ToLower());
|
||||
}
|
||||
|
||||
void VisualStudioEditor::OpenFile(const String& path, int32 line)
|
||||
@@ -153,7 +192,7 @@ void VisualStudioEditor::OpenFile(const String& path, int32 line)
|
||||
// Generate project files if solution is missing
|
||||
if (!FileSystem::FileExists(_solutionPath))
|
||||
{
|
||||
ScriptsBuilder::GenerateProject();
|
||||
ScriptsBuilder::GenerateProject(GetGenerateProjectCustomArgs());
|
||||
}
|
||||
|
||||
// Open file
|
||||
@@ -172,7 +211,7 @@ void VisualStudioEditor::OpenSolution()
|
||||
// Generate project files if solution is missing
|
||||
if (!FileSystem::FileExists(_solutionPath))
|
||||
{
|
||||
ScriptsBuilder::GenerateProject();
|
||||
ScriptsBuilder::GenerateProject(GetGenerateProjectCustomArgs());
|
||||
}
|
||||
|
||||
// Open solution
|
||||
@@ -187,7 +226,7 @@ void VisualStudioEditor::OpenSolution()
|
||||
void VisualStudioEditor::OnFileAdded(const String& path)
|
||||
{
|
||||
// TODO: finish dynamic files adding to the project - for now just regenerate it
|
||||
ScriptsBuilder::GenerateProject();
|
||||
ScriptsBuilder::GenerateProject(GetGenerateProjectCustomArgs());
|
||||
return;
|
||||
if (!FileSystem::FileExists(_solutionPath))
|
||||
{
|
||||
|
||||
@@ -56,6 +56,7 @@ public:
|
||||
// [CodeEditor]
|
||||
CodeEditorTypes GetType() const override;
|
||||
String GetName() const override;
|
||||
String GetGenerateProjectCustomArgs() const override;
|
||||
void OpenFile(const String& path, int32 line) override;
|
||||
void OpenSolution() override;
|
||||
void OnFileAdded(const String& path) override;
|
||||
|
||||
@@ -128,6 +128,11 @@ String VisualStudioCodeEditor::GetName() const
|
||||
return _isInsiders ? TEXT("Visual Studio Code - Insiders") : TEXT("Visual Studio Code");
|
||||
}
|
||||
|
||||
String VisualStudioCodeEditor::GetGenerateProjectCustomArgs() const
|
||||
{
|
||||
return TEXT("-vs2022 -vscode");
|
||||
}
|
||||
|
||||
void VisualStudioCodeEditor::OpenFile(const String& path, int32 line)
|
||||
{
|
||||
// Generate VS solution files for intellisense
|
||||
|
||||
@@ -37,6 +37,7 @@ public:
|
||||
// [CodeEditor]
|
||||
CodeEditorTypes GetType() const override;
|
||||
String GetName() const override;
|
||||
String GetGenerateProjectCustomArgs() const override;
|
||||
void OpenFile(const String& path, int32 line) override;
|
||||
void OpenSolution() override;
|
||||
bool UseAsyncForOpen() const override;
|
||||
|
||||
@@ -52,6 +52,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
},
|
||||
new NodeArchetype
|
||||
{
|
||||
// [Deprecated]
|
||||
TypeID = 3,
|
||||
Title = "Pack Material Layer",
|
||||
Description = "Pack material properties",
|
||||
@@ -75,6 +76,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
},
|
||||
new NodeArchetype
|
||||
{
|
||||
// [Deprecated]
|
||||
TypeID = 4,
|
||||
Title = "Unpack Material Layer",
|
||||
Description = "Unpack material properties",
|
||||
@@ -120,6 +122,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
TypeID = 6,
|
||||
Title = "Pack Material Layer",
|
||||
Description = "Pack material properties",
|
||||
AlternativeTitles = new[] { "Make Material Layer", "Construct Material Layer", "Compose Material Layer" },
|
||||
Flags = NodeFlags.MaterialGraph,
|
||||
Size = new Float2(200, 280),
|
||||
Elements = new[]
|
||||
@@ -146,6 +149,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
TypeID = 7,
|
||||
Title = "Unpack Material Layer",
|
||||
Description = "Unpack material properties",
|
||||
AlternativeTitles = new[] { "Break Material Layer", "Deconstruct Material Layer", "Decompose Material Layer", "Split Material Layer" },
|
||||
Flags = NodeFlags.MaterialGraph,
|
||||
Size = new Float2(210, 280),
|
||||
Elements = new[]
|
||||
|
||||
@@ -586,8 +586,9 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
TypeID = 13,
|
||||
Title = "Pre-skinned Local Position",
|
||||
Description = "Per vertex local position (before skinning)",
|
||||
AlternativeTitles = new[] { "Vertex Position", "Pre skinning Local Vertex Position" },
|
||||
Flags = NodeFlags.MaterialGraph,
|
||||
Size = new Float2(230, 40),
|
||||
Size = new Float2(270, 40),
|
||||
Elements = new[]
|
||||
{
|
||||
NodeElementArchetype.Factory.Output(0, string.Empty, typeof(Float3), 0),
|
||||
@@ -598,8 +599,9 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
TypeID = 14,
|
||||
Title = "Pre-skinned Local Normal",
|
||||
Description = "Per vertex local normal (before skinning)",
|
||||
AlternativeTitles = new[] { "Vertex Normal", "Pre skinning Local Normal" },
|
||||
Flags = NodeFlags.MaterialGraph,
|
||||
Size = new Float2(230, 40),
|
||||
Size = new Float2(270, 40),
|
||||
Elements = new[]
|
||||
{
|
||||
NodeElementArchetype.Factory.Output(0, string.Empty, typeof(Float3), 0),
|
||||
|
||||
@@ -342,6 +342,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
TypeID = 20,
|
||||
Title = "Pack Float2",
|
||||
Description = "Pack components to Float2",
|
||||
AlternativeTitles = new[] { "Make Float2", "Construct Float2", "Compose Float2" },
|
||||
Flags = NodeFlags.AllGraphs,
|
||||
Size = new Float2(150, 40),
|
||||
DefaultValues = new object[]
|
||||
@@ -361,6 +362,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
TypeID = 21,
|
||||
Title = "Pack Float3",
|
||||
Description = "Pack components to Float3",
|
||||
AlternativeTitles = new[] { "Make Float3", "Construct Float3", "Compose Float3" },
|
||||
Flags = NodeFlags.AllGraphs,
|
||||
Size = new Float2(150, 60),
|
||||
DefaultValues = new object[]
|
||||
@@ -382,6 +384,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
TypeID = 22,
|
||||
Title = "Pack Float4",
|
||||
Description = "Pack components to Float4",
|
||||
AlternativeTitles = new[] { "Make Float4", "Construct Float4", "Compose Float4" },
|
||||
Flags = NodeFlags.AllGraphs,
|
||||
Size = new Float2(150, 80),
|
||||
DefaultValues = new object[]
|
||||
@@ -405,6 +408,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
TypeID = 23,
|
||||
Title = "Pack Rotation",
|
||||
Description = "Pack components to Rotation",
|
||||
AlternativeTitles = new[] { "Make Rotation", "Construct Rotation", "Compose Rotation" },
|
||||
Flags = NodeFlags.AllGraphs,
|
||||
Size = new Float2(150, 60),
|
||||
DefaultValues = new object[]
|
||||
@@ -426,6 +430,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
TypeID = 24,
|
||||
Title = "Pack Transform",
|
||||
Description = "Pack components to Transform",
|
||||
AlternativeTitles = new[] { "Make Transform", "Construct Transform", "Compose Transform" },
|
||||
Flags = NodeFlags.AllGraphs,
|
||||
Size = new Float2(150, 80),
|
||||
Elements = new[]
|
||||
@@ -441,6 +446,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
TypeID = 25,
|
||||
Title = "Pack Box",
|
||||
Description = "Pack components to BoundingBox",
|
||||
AlternativeTitles = new[] { "Make Box", "Construct Box", "Compose Box" },
|
||||
Flags = NodeFlags.AllGraphs,
|
||||
Size = new Float2(150, 40),
|
||||
Elements = new[]
|
||||
@@ -454,6 +460,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
{
|
||||
TypeID = 26,
|
||||
Title = "Pack Structure",
|
||||
AlternativeTitles = new[] { "Make Structure", "Construct Structure", "Compose Structure" },
|
||||
Create = (id, context, arch, groupArch) => new PackStructureNode(id, context, arch, groupArch),
|
||||
IsInputCompatible = PackStructureNode.IsInputCompatible,
|
||||
IsOutputCompatible = PackStructureNode.IsOutputCompatible,
|
||||
@@ -479,6 +486,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
TypeID = 30,
|
||||
Title = "Unpack Float2",
|
||||
Description = "Unpack components from Float2",
|
||||
AlternativeTitles = new[] { "Break Float2", "Deconstruct Float2", "Decompose Float2", "Split Float2" },
|
||||
Flags = NodeFlags.AllGraphs,
|
||||
Size = new Float2(150, 40),
|
||||
Elements = new[]
|
||||
@@ -493,6 +501,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
TypeID = 31,
|
||||
Title = "Unpack Float3",
|
||||
Description = "Unpack components from Float3",
|
||||
AlternativeTitles = new[] { "Break Float3", "Deconstruct Float3", "Decompose Float3", "Split Float3" },
|
||||
Flags = NodeFlags.AllGraphs,
|
||||
Size = new Float2(150, 60),
|
||||
Elements = new[]
|
||||
@@ -508,6 +517,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
TypeID = 32,
|
||||
Title = "Unpack Float4",
|
||||
Description = "Unpack components from Float4",
|
||||
AlternativeTitles = new[] { "Break Float4", "Deconstruct Float4", "Decompose Float4", "Split Float4" },
|
||||
Flags = NodeFlags.AllGraphs,
|
||||
Size = new Float2(150, 80),
|
||||
Elements = new[]
|
||||
@@ -524,6 +534,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
TypeID = 33,
|
||||
Title = "Unpack Rotation",
|
||||
Description = "Unpack components from Rotation",
|
||||
AlternativeTitles = new[] { "Break Rotation", "Deconstruct Rotation", "Decompose Rotation", "Split Rotation" },
|
||||
Flags = NodeFlags.AllGraphs,
|
||||
Size = new Float2(170, 60),
|
||||
Elements = new[]
|
||||
@@ -539,6 +550,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
TypeID = 34,
|
||||
Title = "Unpack Transform",
|
||||
Description = "Unpack components from Transform",
|
||||
AlternativeTitles = new[] { "Break Transform", "Deconstruct Transform", "Decompose Transform", "Split Transform" },
|
||||
Flags = NodeFlags.AllGraphs,
|
||||
Size = new Float2(170, 60),
|
||||
Elements = new[]
|
||||
@@ -554,6 +566,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
TypeID = 35,
|
||||
Title = "Unpack Box",
|
||||
Description = "Unpack components from BoundingBox",
|
||||
AlternativeTitles = new[] { "Break BoundingBox", "Deconstruct BoundingBox", "Decompose BoundingBox", "Split BoundingBox" },
|
||||
Flags = NodeFlags.AllGraphs,
|
||||
Size = new Float2(170, 40),
|
||||
Elements = new[]
|
||||
@@ -572,6 +585,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
IsOutputCompatible = UnpackStructureNode.IsOutputCompatible,
|
||||
GetInputOutputDescription = UnpackStructureNode.GetInputOutputDescription,
|
||||
Description = "Breaks the structure data to allow extracting components from it.",
|
||||
AlternativeTitles = new[] { "Break Structure", "Deconstruct Structure", "Decompose Structure", "Split Structure" },
|
||||
Flags = NodeFlags.VisualScriptGraph | NodeFlags.AnimGraph | NodeFlags.NoSpawnViaGUI,
|
||||
Size = new Float2(180, 20),
|
||||
DefaultValues = new object[]
|
||||
|
||||
@@ -22,15 +22,97 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
[HideInEditor]
|
||||
public static class Parameters
|
||||
{
|
||||
/// <summary>
|
||||
/// Surface node type for parameters group Get/Set nodes.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEditor.Surface.SurfaceNode" />
|
||||
public abstract class SurfaceNodeParamsBase : SurfaceNode
|
||||
{
|
||||
/// <summary>
|
||||
/// The combobox for picking parameter.
|
||||
/// </summary>
|
||||
protected ComboBoxElement _combobox;
|
||||
|
||||
/// <summary>
|
||||
/// Fag used to block layout updated when updating node.
|
||||
/// </summary>
|
||||
protected bool _isUpdateLocked;
|
||||
|
||||
/// <inheritdoc />
|
||||
protected SurfaceNodeParamsBase(uint id, VisjectSurfaceContext context, NodeArchetype nodeArch, GroupArchetype groupArch)
|
||||
: base(id, context, nodeArch, groupArch)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the selected parameter.
|
||||
/// </summary>
|
||||
/// <returns>Surface parameter object or null if nothing selected or cannot find it.</returns>
|
||||
protected SurfaceParameter GetSelected()
|
||||
{
|
||||
if (Surface != null)
|
||||
return Surface.GetParameter(_combobox.SelectedItem);
|
||||
return Context.GetParameter((Guid)Values[0]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the combo box.
|
||||
/// </summary>
|
||||
protected void UpdateCombo()
|
||||
{
|
||||
if (_isUpdateLocked)
|
||||
return;
|
||||
_isUpdateLocked = true;
|
||||
if (_combobox == null)
|
||||
{
|
||||
_combobox = GetChild<ComboBoxElement>();
|
||||
_combobox.SelectedIndexChanged += OnSelectedChanged;
|
||||
}
|
||||
string toSelect = null;
|
||||
Guid loadedSelected = (Guid)Values[0];
|
||||
_combobox.ClearItems();
|
||||
var parameters = Surface.Parameters;
|
||||
for (int i = 0; i < parameters.Count; i++)
|
||||
{
|
||||
var param = parameters[i];
|
||||
if (!param.IsPublic && !Surface.CanShowPrivateParameters)
|
||||
continue;
|
||||
_combobox.AddItem(param.Name);
|
||||
if (param.ID == loadedSelected)
|
||||
toSelect = param.Name;
|
||||
}
|
||||
_combobox.SelectedItem = toSelect;
|
||||
_isUpdateLocked = false;
|
||||
}
|
||||
|
||||
private void OnSelectedChanged(ComboBox cb)
|
||||
{
|
||||
if (_isUpdateLocked)
|
||||
return;
|
||||
var selected = GetSelected();
|
||||
var selectedID = selected?.ID ?? Guid.Empty;
|
||||
if (selectedID != (Guid)Values[0])
|
||||
Set(selected, ref selectedID);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the selected parameter.
|
||||
/// </summary>
|
||||
/// <param name="selected">Parameter.</param>
|
||||
/// <param name="selectedID">Parameter identifier.</param>
|
||||
protected virtual void Set(SurfaceParameter selected, ref Guid selectedID)
|
||||
{
|
||||
SetValue(0, selectedID);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Surface node type for parameters group Get node.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEditor.Surface.SurfaceNode" />
|
||||
public class SurfaceNodeParamsGet : SurfaceNode, IParametersDependantNode
|
||||
public class SurfaceNodeParamsGet : SurfaceNodeParamsBase, IParametersDependantNode
|
||||
{
|
||||
private ComboBoxElement _combobox;
|
||||
private readonly List<ISurfaceNodeElement> _dynamicChildren = new List<ISurfaceNodeElement>();
|
||||
private bool _isUpdateLocked;
|
||||
private ScriptType _layoutType;
|
||||
private NodeElementArchetype[] _layoutElements;
|
||||
|
||||
@@ -306,49 +388,6 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
UpdateTitle();
|
||||
}
|
||||
|
||||
private void UpdateCombo()
|
||||
{
|
||||
if (_isUpdateLocked)
|
||||
return;
|
||||
_isUpdateLocked = true;
|
||||
if (_combobox == null)
|
||||
{
|
||||
_combobox = (ComboBoxElement)_children[0];
|
||||
_combobox.SelectedIndexChanged += OnSelectedChanged;
|
||||
}
|
||||
int toSelect = -1;
|
||||
Guid loadedSelected = (Guid)Values[0];
|
||||
_combobox.ClearItems();
|
||||
for (int i = 0; i < Surface.Parameters.Count; i++)
|
||||
{
|
||||
var param = Surface.Parameters[i];
|
||||
_combobox.AddItem(param.Name);
|
||||
if (param.ID == loadedSelected)
|
||||
toSelect = i;
|
||||
}
|
||||
_combobox.SelectedIndex = toSelect;
|
||||
_isUpdateLocked = false;
|
||||
}
|
||||
|
||||
private void OnSelectedChanged(ComboBox cb)
|
||||
{
|
||||
if (_isUpdateLocked)
|
||||
return;
|
||||
var selected = GetSelected();
|
||||
var selectedID = selected?.ID ?? Guid.Empty;
|
||||
SetValue(0, selectedID);
|
||||
}
|
||||
|
||||
private SurfaceParameter GetSelected()
|
||||
{
|
||||
if (Surface != null)
|
||||
{
|
||||
var selectedIndex = _combobox.SelectedIndex;
|
||||
return selectedIndex >= 0 && selectedIndex < Surface.Parameters.Count ? Surface.Parameters[selectedIndex] : null;
|
||||
}
|
||||
return Context.GetParameter((Guid)Values[0]);
|
||||
}
|
||||
|
||||
private void ClearDynamicElements()
|
||||
{
|
||||
for (int i = 0; i < _dynamicChildren.Count; i++)
|
||||
@@ -463,15 +502,19 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
else if (!_isUpdateLocked)
|
||||
{
|
||||
_isUpdateLocked = true;
|
||||
int toSelect = -1;
|
||||
string toSelect = null;
|
||||
Guid loadedSelected = (Guid)Values[0];
|
||||
for (int i = 0; i < Surface.Parameters.Count; i++)
|
||||
var parameters = Surface.Parameters;
|
||||
for (int i = 0; i < parameters.Count; i++)
|
||||
{
|
||||
var param = Surface.Parameters[i];
|
||||
var param = parameters[i];
|
||||
if (param.ID == loadedSelected)
|
||||
toSelect = i;
|
||||
{
|
||||
toSelect = param.Name;
|
||||
break;
|
||||
}
|
||||
}
|
||||
_combobox.SelectedIndex = toSelect;
|
||||
_combobox.SelectedItem = toSelect;
|
||||
_isUpdateLocked = false;
|
||||
}
|
||||
UpdateLayout();
|
||||
@@ -817,66 +860,23 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
/// Surface node type for parameters group Set node.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEditor.Surface.SurfaceNode" />
|
||||
public class SurfaceNodeParamsSet : SurfaceNode, IParametersDependantNode
|
||||
public class SurfaceNodeParamsSet : SurfaceNodeParamsBase, IParametersDependantNode
|
||||
{
|
||||
private ComboBoxElement _combobox;
|
||||
private bool _isUpdateLocked;
|
||||
|
||||
/// <inheritdoc />
|
||||
public SurfaceNodeParamsSet(uint id, VisjectSurfaceContext context, NodeArchetype nodeArch, GroupArchetype groupArch)
|
||||
: base(id, context, nodeArch, groupArch)
|
||||
{
|
||||
}
|
||||
|
||||
private void UpdateCombo()
|
||||
/// <inheritdoc />
|
||||
protected override void Set(SurfaceParameter selected, ref Guid selectedID)
|
||||
{
|
||||
if (_isUpdateLocked)
|
||||
return;
|
||||
_isUpdateLocked = true;
|
||||
if (_combobox == null)
|
||||
SetValues(new[]
|
||||
{
|
||||
_combobox = GetChild<ComboBoxElement>();
|
||||
_combobox.SelectedIndexChanged += OnSelectedChanged;
|
||||
}
|
||||
int toSelect = -1;
|
||||
Guid loadedSelected = (Guid)Values[0];
|
||||
_combobox.ClearItems();
|
||||
for (int i = 0; i < Surface.Parameters.Count; i++)
|
||||
{
|
||||
var param = Surface.Parameters[i];
|
||||
_combobox.AddItem(param.Name);
|
||||
if (param.ID == loadedSelected)
|
||||
toSelect = i;
|
||||
}
|
||||
_combobox.SelectedIndex = toSelect;
|
||||
_isUpdateLocked = false;
|
||||
}
|
||||
|
||||
private void OnSelectedChanged(ComboBox cb)
|
||||
{
|
||||
if (_isUpdateLocked)
|
||||
return;
|
||||
var selected = GetSelected();
|
||||
var selectedID = selected?.ID ?? Guid.Empty;
|
||||
if (selectedID != (Guid)Values[0])
|
||||
{
|
||||
SetValues(new[]
|
||||
{
|
||||
selectedID,
|
||||
selected != null ? TypeUtils.GetDefaultValue(selected.Type) : null,
|
||||
});
|
||||
UpdateUI();
|
||||
}
|
||||
}
|
||||
|
||||
private SurfaceParameter GetSelected()
|
||||
{
|
||||
if (Surface != null)
|
||||
{
|
||||
var selectedIndex = _combobox.SelectedIndex;
|
||||
return selectedIndex >= 0 && selectedIndex < Surface.Parameters.Count ? Surface.Parameters[selectedIndex] : null;
|
||||
}
|
||||
return Context.GetParameter((Guid)Values[0]);
|
||||
selectedID,
|
||||
selected != null ? TypeUtils.GetDefaultValue(selected.Type) : null,
|
||||
});
|
||||
UpdateUI();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -436,10 +436,11 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
new NodeArchetype
|
||||
{
|
||||
TypeID = 102,
|
||||
Title = "Particle Lifetime",
|
||||
Description = "Particle lifetime (in seconds).",
|
||||
Title = "Particle Total Lifetime",
|
||||
Description = "Total particle lifetime (in seconds) at the time when the particle was created. Always the same, no matter the particles age.",
|
||||
AlternativeTitles = new[] { "Age" },
|
||||
Flags = NodeFlags.MaterialGraph | NodeFlags.ParticleEmitterGraph,
|
||||
Size = new Float2(200, 30),
|
||||
Size = new Float2(250, 30),
|
||||
Elements = new[]
|
||||
{
|
||||
NodeElementArchetype.Factory.Output(0, string.Empty, typeof(float), 0),
|
||||
@@ -449,9 +450,10 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
{
|
||||
TypeID = 103,
|
||||
Title = "Particle Age",
|
||||
Description = "Particle age (in seconds).",
|
||||
Description = "Particle age (in seconds). How long the particle has been alive since it was created.",
|
||||
AlternativeTitles = new[] { "Lifetime" },
|
||||
Flags = NodeFlags.MaterialGraph | NodeFlags.ParticleEmitterGraph,
|
||||
Size = new Float2(200, 30),
|
||||
Size = new Float2(170, 30),
|
||||
Elements = new[]
|
||||
{
|
||||
NodeElementArchetype.Factory.Output(0, string.Empty, typeof(float), 0),
|
||||
@@ -533,9 +535,10 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
{
|
||||
TypeID = 110,
|
||||
Title = "Particle Normalized Age",
|
||||
Description = "Particle normalized age to range 0-1 (age divided by lifetime).",
|
||||
Description = "The normalized age of the particle, represented as 0 (max lifetime) to 1 (max age). (Same as age divided by lifetime.)",
|
||||
AlternativeTitles = new[] { "Lifetime" },
|
||||
Flags = NodeFlags.MaterialGraph | NodeFlags.ParticleEmitterGraph,
|
||||
Size = new Float2(230, 30),
|
||||
Size = new Float2(250, 30),
|
||||
Elements = new[]
|
||||
{
|
||||
NodeElementArchetype.Factory.Output(0, string.Empty, typeof(float), 0),
|
||||
|
||||
@@ -585,7 +585,7 @@ namespace FlaxEditor.Surface.ContextMenu
|
||||
|
||||
private void UpdateFilters()
|
||||
{
|
||||
if (string.IsNullOrEmpty(_searchBox.Text) && _selectedBoxes[0] == null)
|
||||
if (string.IsNullOrEmpty(_searchBox.Text) && _selectedBoxes.Count == 0)
|
||||
{
|
||||
ResetView();
|
||||
Profiler.EndEvent();
|
||||
|
||||
@@ -34,11 +34,6 @@ namespace FlaxEditor.Surface.Elements
|
||||
/// </summary>
|
||||
public const float DefaultConnectionOffset = 24f;
|
||||
|
||||
/// <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 Float2(parentNode.Archetype.Size.X, 0))
|
||||
@@ -109,12 +104,13 @@ namespace FlaxEditor.Surface.Elements
|
||||
/// </summary>
|
||||
/// <param name="targetBox">The other box.</param>
|
||||
/// <param name="mousePosition">The mouse position</param>
|
||||
public bool IntersectsConnection(Box targetBox, ref Float2 mousePosition)
|
||||
/// <param name="distance">Distance at which its an intersection</param>
|
||||
public bool IntersectsConnection(Box targetBox, ref Float2 mousePosition, float distance)
|
||||
{
|
||||
float connectionOffset = Mathf.Max(0f, DefaultConnectionOffset * (1 - Editor.Instance.Options.Options.Interface.ConnectionCurvature));
|
||||
Float2 start = new Float2(ConnectionOrigin.X + connectionOffset, ConnectionOrigin.Y);
|
||||
Float2 end = new Float2(targetBox.ConnectionOrigin.X - connectionOffset, targetBox.ConnectionOrigin.Y);
|
||||
return IntersectsConnection(ref start, ref end, ref mousePosition, MouseOverConnectionDistance);
|
||||
return IntersectsConnection(ref start, ref end, ref mousePosition, distance);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -182,7 +178,7 @@ namespace FlaxEditor.Surface.Elements
|
||||
{
|
||||
// Draw all the connections
|
||||
var style = Surface.Style;
|
||||
var mouseOverDistance = MouseOverConnectionDistance;
|
||||
var mouseOverDistance = Surface.MouseOverConnectionDistance;
|
||||
var startPos = ConnectionOrigin;
|
||||
var startHighlight = ConnectionsHighlightIntensity;
|
||||
for (int i = 0; i < Connections.Count; i++)
|
||||
|
||||
@@ -573,13 +573,13 @@ namespace FlaxEditor.Surface
|
||||
var showSearch = () => editor.ContentFinding.ShowSearch(window);
|
||||
|
||||
// Toolstrip
|
||||
saveButton = toolStrip.AddButton(editor.Icons.Save64, window.Save).LinkTooltip("Save", ref inputOptions.Save);
|
||||
saveButton = toolStrip.AddButton(editor.Icons.Save64, window.Save).LinkTooltip("Save.", ref inputOptions.Save);
|
||||
toolStrip.AddSeparator();
|
||||
undoButton = toolStrip.AddButton(editor.Icons.Undo64, undo.PerformUndo).LinkTooltip("Undo", ref inputOptions.Undo);
|
||||
redoButton = toolStrip.AddButton(editor.Icons.Redo64, undo.PerformRedo).LinkTooltip("Redo", ref inputOptions.Redo);
|
||||
undoButton = toolStrip.AddButton(editor.Icons.Undo64, undo.PerformUndo).LinkTooltip("Undo.", ref inputOptions.Undo);
|
||||
redoButton = toolStrip.AddButton(editor.Icons.Redo64, undo.PerformRedo).LinkTooltip("Redo.", ref inputOptions.Redo);
|
||||
toolStrip.AddSeparator();
|
||||
toolStrip.AddButton(editor.Icons.Search64, showSearch).LinkTooltip("Open content search tool", ref inputOptions.Search);
|
||||
toolStrip.AddButton(editor.Icons.CenterView64, surface.ShowWholeGraph).LinkTooltip("Show whole graph");
|
||||
toolStrip.AddButton(editor.Icons.Search64, showSearch).LinkTooltip("Open content search tool.", ref inputOptions.Search);
|
||||
toolStrip.AddButton(editor.Icons.CenterView64, surface.ShowWholeGraph).LinkTooltip("Show whole graph.");
|
||||
var gridSnapButton = toolStrip.AddButton(editor.Icons.Grid32, surface.ToggleGridSnapping);
|
||||
gridSnapButton.LinkTooltip("Toggle grid snapping for nodes.");
|
||||
gridSnapButton.AutoCheck = true;
|
||||
|
||||
@@ -410,8 +410,11 @@ namespace FlaxEditor.Surface
|
||||
}
|
||||
menu.AddSeparator();
|
||||
|
||||
_cmFormatNodesMenu = menu.AddChildMenu("Format node(s)");
|
||||
_cmFormatNodesMenu.Enabled = CanEdit && HasNodesSelection;
|
||||
bool allNodesNoMove = SelectedNodes.All(n => n.Archetype.Flags.HasFlag(NodeFlags.NoMove));
|
||||
bool clickedNodeNoMove = ((SelectedNodes.Count == 1 && controlUnderMouse is SurfaceNode n && n.Archetype.Flags.HasFlag(NodeFlags.NoMove)));
|
||||
|
||||
_cmFormatNodesMenu = menu.AddChildMenu("Format nodes");
|
||||
_cmFormatNodesMenu.Enabled = CanEdit && HasNodesSelection && !(allNodesNoMove || clickedNodeNoMove);
|
||||
|
||||
_cmFormatNodesConnectionButton = _cmFormatNodesMenu.ContextMenu.AddButton("Auto format", Editor.Instance.Options.Options.Input.NodesAutoFormat, () => { FormatGraph(SelectedNodes); });
|
||||
_cmFormatNodesConnectionButton = _cmFormatNodesMenu.ContextMenu.AddButton("Straighten connections", Editor.Instance.Options.Options.Input.NodesStraightenConnections, () => { StraightenGraphConnections(SelectedNodes); });
|
||||
|
||||
@@ -213,6 +213,44 @@ namespace FlaxEditor.Surface
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draw connection hints for lazy connect feature.
|
||||
/// </summary>
|
||||
protected virtual void DrawLazyConnect()
|
||||
{
|
||||
var style = FlaxEngine.GUI.Style.Current;
|
||||
|
||||
if (_lazyConnectStartNode != null)
|
||||
{
|
||||
Float2 upperLeft = _rootControl.PointToParent(_lazyConnectStartNode.UpperLeft);
|
||||
Rectangle startNodeOutline = new Rectangle(upperLeft + 1f, _lazyConnectStartNode.Size - 1f);
|
||||
startNodeOutline.Size *= ViewScale;
|
||||
Render2D.DrawRectangle(startNodeOutline.MakeExpanded(4f), style.BackgroundSelected, 4f);
|
||||
}
|
||||
|
||||
if (_lazyConnectEndNode != null)
|
||||
{
|
||||
Float2 upperLeft = _rootControl.PointToParent(_lazyConnectEndNode.UpperLeft);
|
||||
Rectangle startNodeOutline = new Rectangle(upperLeft + 1f, _lazyConnectEndNode.Size - 1f);
|
||||
startNodeOutline.Size *= ViewScale;
|
||||
Render2D.DrawRectangle(startNodeOutline.MakeExpanded(4f), style.BackgroundSelected, 4f);
|
||||
}
|
||||
|
||||
Rectangle startRect = new Rectangle(_rightMouseDownPos - 6f, new Float2(12f));
|
||||
Rectangle endRect = new Rectangle(_mousePos - 6f, new Float2(12f));
|
||||
|
||||
// Start and end shadows/ outlines
|
||||
Render2D.FillRectangle(startRect.MakeExpanded(2.5f), Color.Black);
|
||||
Render2D.FillRectangle(endRect.MakeExpanded(2.5f), Color.Black);
|
||||
|
||||
Render2D.DrawLine(_rightMouseDownPos, _mousePos, Color.Black, 7.5f);
|
||||
Render2D.DrawLine(_rightMouseDownPos, _mousePos, style.ForegroundGrey, 5f);
|
||||
|
||||
// Draw start and end boxes over the lines to hide ugly artifacts at the ends
|
||||
Render2D.FillRectangle(startRect, style.ForegroundGrey);
|
||||
Render2D.FillRectangle(endRect, style.ForegroundGrey);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draws the contents of the surface (nodes, connections, comments, etc.).
|
||||
/// </summary>
|
||||
@@ -260,6 +298,9 @@ namespace FlaxEditor.Surface
|
||||
|
||||
DrawContents();
|
||||
|
||||
if (_isLazyConnecting)
|
||||
DrawLazyConnect();
|
||||
|
||||
//Render2D.DrawText(style.FontTitle, string.Format("Scale: {0}", _rootControl.Scale), rect, Enabled ? Color.Red : Color.Black);
|
||||
|
||||
// Draw border
|
||||
|
||||
@@ -39,6 +39,8 @@ namespace FlaxEditor.Surface
|
||||
if (nodes.Count <= 1)
|
||||
return;
|
||||
|
||||
List<MoveNodesAction> undoActions = new List<MoveNodesAction>();
|
||||
|
||||
var nodesToVisit = new HashSet<SurfaceNode>(nodes);
|
||||
|
||||
// While we haven't formatted every node
|
||||
@@ -73,18 +75,23 @@ namespace FlaxEditor.Surface
|
||||
}
|
||||
}
|
||||
|
||||
FormatConnectedGraph(connectedNodes);
|
||||
undoActions.AddRange(FormatConnectedGraph(connectedNodes));
|
||||
}
|
||||
|
||||
Undo?.AddAction(new MultiUndoAction(undoActions, "Format nodes"));
|
||||
MarkAsEdited(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Formats a graph where all nodes are connected.
|
||||
/// </summary>
|
||||
/// <param name="nodes">List of connected nodes.</param>
|
||||
protected void FormatConnectedGraph(List<SurfaceNode> nodes)
|
||||
private List<MoveNodesAction> FormatConnectedGraph(List<SurfaceNode> nodes)
|
||||
{
|
||||
List<MoveNodesAction> undoActions = new List<MoveNodesAction>();
|
||||
|
||||
if (nodes.Count <= 1)
|
||||
return;
|
||||
return undoActions;
|
||||
|
||||
var boundingBox = GetNodesBounds(nodes);
|
||||
|
||||
@@ -140,7 +147,6 @@ namespace FlaxEditor.Surface
|
||||
}
|
||||
|
||||
// Set the node positions
|
||||
var undoActions = new List<MoveNodesAction>();
|
||||
var topRightPosition = endNodes[0].Location;
|
||||
for (int i = 0; i < nodes.Count; i++)
|
||||
{
|
||||
@@ -155,16 +161,18 @@ namespace FlaxEditor.Surface
|
||||
}
|
||||
}
|
||||
|
||||
MarkAsEdited(false);
|
||||
Undo?.AddAction(new MultiUndoAction(undoActions, "Format nodes"));
|
||||
return undoActions;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Straightens every connection between nodes in <paramref name="nodes"/>.
|
||||
/// </summary>
|
||||
/// <param name="nodes">List of nodes.</param>
|
||||
/// <returns>List of undo actions.</returns>
|
||||
public void StraightenGraphConnections(List<SurfaceNode> nodes)
|
||||
{
|
||||
{
|
||||
nodes = nodes.Where(n => !n.Archetype.Flags.HasFlag(NodeFlags.NoMove)).ToList();
|
||||
|
||||
if (nodes.Count <= 1)
|
||||
return;
|
||||
|
||||
@@ -350,8 +358,10 @@ namespace FlaxEditor.Surface
|
||||
/// <param name="nodes">List of nodes.</param>
|
||||
/// <param name="alignmentType">Alignemnt type.</param>
|
||||
public void AlignNodes(List<SurfaceNode> nodes, NodeAlignmentType alignmentType)
|
||||
{
|
||||
if(nodes.Count <= 1)
|
||||
{
|
||||
nodes = nodes.Where(n => !n.Archetype.Flags.HasFlag(NodeFlags.NoMove)).ToList();
|
||||
|
||||
if (nodes.Count <= 1)
|
||||
return;
|
||||
|
||||
var undoActions = new List<MoveNodesAction>();
|
||||
@@ -392,6 +402,8 @@ namespace FlaxEditor.Surface
|
||||
/// <param name="vertically">If false will be done horizontally, if true will be done vertically.</param>
|
||||
public void DistributeNodes(List<SurfaceNode> nodes, bool vertically)
|
||||
{
|
||||
nodes = nodes.Where(n => !n.Archetype.Flags.HasFlag(NodeFlags.NoMove)).ToList();
|
||||
|
||||
if(nodes.Count <= 1)
|
||||
return;
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using static FlaxEditor.Surface.Archetypes.Particles;
|
||||
using FlaxEditor.Options;
|
||||
using FlaxEditor.Surface.Elements;
|
||||
using FlaxEditor.Surface.Undo;
|
||||
@@ -23,12 +24,26 @@ namespace FlaxEditor.Surface
|
||||
/// </summary>
|
||||
public bool PanWithMiddleMouse = false;
|
||||
|
||||
/// <summary>
|
||||
/// Distance for the mouse to be considered above the connection.
|
||||
/// </summary>
|
||||
public float MouseOverConnectionDistance => 100f / ViewScale;
|
||||
|
||||
/// <summary>
|
||||
/// Distance of a node from which it is able to be slotted into an existing connection.
|
||||
/// </summary>
|
||||
public float SlotNodeIntoConnectionDistance => 250f / ViewScale;
|
||||
|
||||
private string _currentInputText = string.Empty;
|
||||
private Float2 _movingNodesDelta;
|
||||
private Float2 _gridRoundingDelta;
|
||||
private HashSet<SurfaceNode> _movingNodes;
|
||||
private HashSet<SurfaceNode> _temporarySelectedNodes;
|
||||
private readonly Stack<InputBracket> _inputBrackets = new Stack<InputBracket>();
|
||||
private bool _isLazyConnecting;
|
||||
private SurfaceNode _lazyConnectStartNode;
|
||||
private SurfaceNode _lazyConnectEndNode;
|
||||
private InputBinding _focusSelectedNodeBinding;
|
||||
|
||||
private class InputBracket
|
||||
{
|
||||
@@ -250,8 +265,13 @@ namespace FlaxEditor.Surface
|
||||
// Cache mouse location
|
||||
_mousePos = location;
|
||||
|
||||
if (_isLazyConnecting && GetControlUnderMouse() is SurfaceNode nodeUnderMouse && !(nodeUnderMouse is SurfaceComment || nodeUnderMouse is ParticleEmitterNode))
|
||||
_lazyConnectEndNode = nodeUnderMouse;
|
||||
else if (_isLazyConnecting && Nodes.Count > 0)
|
||||
_lazyConnectEndNode = GetClosestNodeAtLocation(location);
|
||||
|
||||
// Moving around surface with mouse
|
||||
if (_rightMouseDown)
|
||||
if (_rightMouseDown && !_isLazyConnecting)
|
||||
{
|
||||
// Calculate delta
|
||||
var delta = location - _rightMouseDownPos;
|
||||
@@ -321,6 +341,33 @@ namespace FlaxEditor.Surface
|
||||
|
||||
foreach (var node in _movingNodes)
|
||||
{
|
||||
// Allow ripping the node from its current connection
|
||||
if (RootWindow.GetKey(KeyboardKeys.Alt))
|
||||
{
|
||||
InputBox nodeConnectedInput = null;
|
||||
OutputBox nodeConnectedOuput = null;
|
||||
|
||||
var boxes = node.GetBoxes();
|
||||
foreach (var box in boxes)
|
||||
{
|
||||
if (!box.IsOutput && box.Connections.Count > 0)
|
||||
{
|
||||
nodeConnectedInput = (InputBox)box;
|
||||
continue;
|
||||
}
|
||||
if (box.IsOutput && box.Connections.Count > 0)
|
||||
{
|
||||
nodeConnectedOuput = (OutputBox)box;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (nodeConnectedInput != null && nodeConnectedOuput != null)
|
||||
TryConnect(nodeConnectedOuput.Connections[0], nodeConnectedInput.Connections[0]);
|
||||
|
||||
node.RemoveConnections();
|
||||
}
|
||||
|
||||
if (gridSnap)
|
||||
{
|
||||
Float2 unroundedLocation = node.Location;
|
||||
@@ -420,7 +467,7 @@ namespace FlaxEditor.Surface
|
||||
if (!handled && CanEdit && CanUseNodeType(7, 29))
|
||||
{
|
||||
var mousePos = _rootControl.PointFromParent(ref _mousePos);
|
||||
if (IntersectsConnection(mousePos, out InputBox inputBox, out OutputBox outputBox) && GetControlUnderMouse() == null)
|
||||
if (IntersectsConnection(mousePos, out InputBox inputBox, out OutputBox outputBox, MouseOverConnectionDistance) && GetControlUnderMouse() == null)
|
||||
{
|
||||
if (Undo != null)
|
||||
{
|
||||
@@ -515,11 +562,17 @@ namespace FlaxEditor.Surface
|
||||
_middleMouseDownPos = location;
|
||||
}
|
||||
|
||||
if (root.GetKey(KeyboardKeys.Alt) && button == MouseButton.Right)
|
||||
_isLazyConnecting = true;
|
||||
|
||||
// Check if any node is under the mouse
|
||||
SurfaceControl controlUnderMouse = GetControlUnderMouse();
|
||||
var cLocation = _rootControl.PointFromParent(ref location);
|
||||
if (controlUnderMouse != null)
|
||||
{
|
||||
if (controlUnderMouse is SurfaceNode node && _isLazyConnecting && !(controlUnderMouse is SurfaceComment || controlUnderMouse is ParticleEmitterNode))
|
||||
_lazyConnectStartNode = node;
|
||||
|
||||
// Check if mouse is over header and user is pressing mouse left button
|
||||
if (_leftMouseDown && controlUnderMouse.CanSelect(ref cLocation))
|
||||
{
|
||||
@@ -554,6 +607,9 @@ namespace FlaxEditor.Surface
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_isLazyConnecting && Nodes.Count > 0)
|
||||
_lazyConnectStartNode = GetClosestNodeAtLocation(location);
|
||||
|
||||
// Cache flags and state
|
||||
if (_leftMouseDown)
|
||||
{
|
||||
@@ -602,8 +658,71 @@ namespace FlaxEditor.Surface
|
||||
{
|
||||
if (_movingNodes != null && _movingNodes.Count > 0)
|
||||
{
|
||||
// Allow dropping a single node onto an existing connection and connect it
|
||||
if (_movingNodes.Count == 1)
|
||||
{
|
||||
var mousePos = _rootControl.PointFromParent(ref _mousePos);
|
||||
InputBox intersectedConnectionInputBox;
|
||||
OutputBox intersectedConnectionOutputBox;
|
||||
if (IntersectsConnection(mousePos, out intersectedConnectionInputBox, out intersectedConnectionOutputBox, SlotNodeIntoConnectionDistance))
|
||||
{
|
||||
SurfaceNode node = _movingNodes.First();
|
||||
InputBox nodeInputBox = (InputBox)node.GetBoxes().First(b => !b.IsOutput);
|
||||
OutputBox nodeOutputBox = (OutputBox)node.GetBoxes().First(b => b.IsOutput);
|
||||
TryConnect(intersectedConnectionOutputBox, nodeInputBox);
|
||||
TryConnect(nodeOutputBox, intersectedConnectionInputBox);
|
||||
|
||||
float intersectedConnectionNodesXDistance = intersectedConnectionInputBox.ParentNode.Left - intersectedConnectionOutputBox.ParentNode.Right;
|
||||
float paddedNodeWidth = node.Width + 2f;
|
||||
if (intersectedConnectionNodesXDistance < paddedNodeWidth)
|
||||
{
|
||||
List<SurfaceNode> visitedNodes = new List<SurfaceNode>{ node };
|
||||
List<SurfaceNode> movedNodes = new List<SurfaceNode>();
|
||||
Float2 locationDelta = new Float2(paddedNodeWidth, 0f);
|
||||
|
||||
MoveConnectedNodes(intersectedConnectionInputBox.ParentNode);
|
||||
|
||||
void MoveConnectedNodes(SurfaceNode node)
|
||||
{
|
||||
// Only move node if it is to the right of the node we have connected the moved node to
|
||||
if (node.Right > intersectedConnectionInputBox.ParentNode.Left + 15f && !node.Archetype.Flags.HasFlag(NodeFlags.NoMove))
|
||||
{
|
||||
node.Location += locationDelta;
|
||||
movedNodes.Add(node);
|
||||
}
|
||||
|
||||
visitedNodes.Add(node);
|
||||
|
||||
foreach (var box in node.GetBoxes())
|
||||
{
|
||||
if (!box.HasAnyConnection || box == intersectedConnectionInputBox)
|
||||
continue;
|
||||
|
||||
foreach (var connectedBox in box.Connections)
|
||||
{
|
||||
SurfaceNode nextNode = connectedBox.ParentNode;
|
||||
if (visitedNodes.Contains(nextNode))
|
||||
continue;
|
||||
|
||||
MoveConnectedNodes(nextNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Float2 nodeMoveOffset = new Float2(node.Width * 0.5f, 0f);
|
||||
node.Location += nodeMoveOffset;
|
||||
|
||||
var moveNodesAction = new MoveNodesAction(Context, movedNodes.Select(n => n.ID).ToArray(), locationDelta);
|
||||
var moveNodeAction = new MoveNodesAction(Context, [node.ID], nodeMoveOffset);
|
||||
var multiAction = new MultiUndoAction(moveNodeAction, moveNodesAction);
|
||||
|
||||
AddBatchedUndoAction(multiAction);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Undo != null && !_movingNodesDelta.IsZero && CanEdit)
|
||||
Undo.AddAction(new MoveNodesAction(Context, _movingNodes.Select(x => x.ID).ToArray(), _movingNodesDelta));
|
||||
AddBatchedUndoAction(new MoveNodesAction(Context, _movingNodes.Select(x => x.ID).ToArray(), _movingNodesDelta));
|
||||
_movingNodes.Clear();
|
||||
}
|
||||
_movingNodesDelta = Float2.Zero;
|
||||
@@ -630,12 +749,36 @@ namespace FlaxEditor.Surface
|
||||
{
|
||||
// Check if any control is under the mouse
|
||||
_cmStartPos = location;
|
||||
if (controlUnderMouse == null)
|
||||
if (controlUnderMouse == null && !_isLazyConnecting)
|
||||
{
|
||||
showPrimaryMenu = true;
|
||||
}
|
||||
}
|
||||
_mouseMoveAmount = 0;
|
||||
|
||||
if (_isLazyConnecting)
|
||||
{
|
||||
if (_lazyConnectStartNode != null && _lazyConnectEndNode != null && _lazyConnectStartNode != _lazyConnectEndNode)
|
||||
{
|
||||
// First check if there is a type matching input and output where input
|
||||
OutputBox startNodeOutput = (OutputBox)_lazyConnectStartNode.GetBoxes().FirstOrDefault(b => b.IsOutput, null);
|
||||
InputBox endNodeInput = null;
|
||||
|
||||
if (startNodeOutput != null)
|
||||
endNodeInput = (InputBox)_lazyConnectEndNode.GetBoxes().FirstOrDefault(b => !b.IsOutput && b.CurrentType == startNodeOutput.CurrentType && !b.HasAnyConnection && b.IsActive && b.CanConnectWith(startNodeOutput), null);
|
||||
|
||||
// Perform less strict checks (less ideal conditions for connection but still good) if the first checks failed
|
||||
if (endNodeInput == null)
|
||||
endNodeInput = (InputBox)_lazyConnectEndNode.GetBoxes().FirstOrDefault(b => !b.IsOutput && !b.HasAnyConnection && b.CanConnectWith(startNodeOutput), null);
|
||||
|
||||
if (startNodeOutput != null && endNodeInput != null)
|
||||
TryConnect(startNodeOutput, endNodeInput);
|
||||
}
|
||||
|
||||
_isLazyConnecting = false;
|
||||
_lazyConnectStartNode = null;
|
||||
_lazyConnectEndNode = null;
|
||||
}
|
||||
}
|
||||
if (_middleMouseDown && button == MouseButton.Middle)
|
||||
{
|
||||
@@ -651,7 +794,7 @@ namespace FlaxEditor.Surface
|
||||
{
|
||||
// Surface was not moved with MMB so try to remove connection underneath
|
||||
var mousePos = _rootControl.PointFromParent(ref location);
|
||||
if (IntersectsConnection(mousePos, out InputBox inputBox, out OutputBox outputBox))
|
||||
if (IntersectsConnection(mousePos, out InputBox inputBox, out OutputBox outputBox, MouseOverConnectionDistance))
|
||||
{
|
||||
var action = new EditNodeConnections(inputBox.ParentNode.Context, inputBox.ParentNode);
|
||||
inputBox.BreakConnection(outputBox);
|
||||
@@ -704,13 +847,21 @@ namespace FlaxEditor.Surface
|
||||
|
||||
private void MoveSelectedNodes(Float2 delta)
|
||||
{
|
||||
// TODO: undo
|
||||
List<MoveNodesAction> undoActions = new List<MoveNodesAction>();
|
||||
|
||||
delta /= _targetScale;
|
||||
OnGetNodesToMove();
|
||||
foreach (var node in _movingNodes)
|
||||
{
|
||||
node.Location += delta;
|
||||
if (Undo != null)
|
||||
undoActions.Add(new MoveNodesAction(Context, new[] { node.ID }, delta));
|
||||
}
|
||||
_isMovingSelection = false;
|
||||
MarkAsEdited(false);
|
||||
|
||||
if (undoActions.Count > 0)
|
||||
Undo?.AddAction(new MultiUndoAction(undoActions, "Moved "));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -837,6 +988,29 @@ namespace FlaxEditor.Surface
|
||||
return false;
|
||||
}
|
||||
|
||||
private SurfaceNode GetClosestNodeAtLocation(Float2 location)
|
||||
{
|
||||
SurfaceNode currentClosestNode = null;
|
||||
float currentClosestDistanceSquared = float.MaxValue;
|
||||
|
||||
foreach (var node in Nodes)
|
||||
{
|
||||
if (node is SurfaceComment || node is ParticleEmitterNode)
|
||||
continue;
|
||||
|
||||
Float2 nodeSurfaceLocation = _rootControl.PointToParent(node.Center);
|
||||
|
||||
float distanceSquared = Float2.DistanceSquared(location, nodeSurfaceLocation);
|
||||
if (distanceSquared < currentClosestDistanceSquared)
|
||||
{
|
||||
currentClosestNode = node;
|
||||
currentClosestDistanceSquared = distanceSquared;
|
||||
}
|
||||
}
|
||||
|
||||
return currentClosestNode;
|
||||
}
|
||||
|
||||
private void ResetInput()
|
||||
{
|
||||
InputText = "";
|
||||
@@ -845,7 +1019,8 @@ namespace FlaxEditor.Surface
|
||||
|
||||
private void CurrentInputTextChanged(string currentInputText)
|
||||
{
|
||||
if (string.IsNullOrEmpty(currentInputText))
|
||||
// Check if focus selected nodes binding is being pressed to prevent it triggering primary menu
|
||||
if (string.IsNullOrEmpty(currentInputText) || _focusSelectedNodeBinding.Process(RootWindow))
|
||||
return;
|
||||
if (IsPrimaryMenuOpened || !CanEdit)
|
||||
{
|
||||
@@ -1025,7 +1200,7 @@ namespace FlaxEditor.Surface
|
||||
return new Float2(xLocation, yLocation);
|
||||
}
|
||||
|
||||
private bool IntersectsConnection(Float2 mousePosition, out InputBox inputBox, out OutputBox outputBox)
|
||||
private bool IntersectsConnection(Float2 mousePosition, out InputBox inputBox, out OutputBox outputBox, float distance)
|
||||
{
|
||||
for (int i = 0; i < Nodes.Count; i++)
|
||||
{
|
||||
@@ -1035,7 +1210,7 @@ namespace FlaxEditor.Surface
|
||||
{
|
||||
for (int k = 0; k < ob.Connections.Count; k++)
|
||||
{
|
||||
if (ob.IntersectsConnection(ob.Connections[k], ref mousePosition))
|
||||
if (ob.IntersectsConnection(ob.Connections[k], ref mousePosition, distance))
|
||||
{
|
||||
outputBox = ob;
|
||||
inputBox = ob.Connections[k] as InputBox;
|
||||
|
||||
@@ -423,8 +423,9 @@ namespace FlaxEditor.Surface
|
||||
new InputActionsContainer.Binding(options => options.NodesAlignLeft, () => { AlignNodes(SelectedNodes, NodeAlignmentType.Left); }),
|
||||
new InputActionsContainer.Binding(options => options.NodesAlignCenter, () => { AlignNodes(SelectedNodes, NodeAlignmentType.Center); }),
|
||||
new InputActionsContainer.Binding(options => options.NodesAlignRight, () => { AlignNodes(SelectedNodes, NodeAlignmentType.Right); }),
|
||||
new InputActionsContainer.Binding(options => options.NodesDistributeHorizontal, () => { DistributeNodes(SelectedNodes, false); }),
|
||||
new InputActionsContainer.Binding(options => options.NodesDistributeVertical, () => { DistributeNodes(SelectedNodes, true); }),
|
||||
new InputActionsContainer.Binding(options => options.NodesDistributeHorizontal, () => { DistributeNodes(SelectedNodes, false); }),
|
||||
new InputActionsContainer.Binding(options => options.NodesDistributeVertical, () => { DistributeNodes(SelectedNodes, true); }),
|
||||
new InputActionsContainer.Binding(options => options.FocusSelectedNodes, () => { FocusSelectionOrWholeGraph(); }),
|
||||
});
|
||||
|
||||
Context.ControlSpawned += OnSurfaceControlSpawned;
|
||||
@@ -436,7 +437,10 @@ namespace FlaxEditor.Surface
|
||||
DragHandlers.Add(_dragAssets = new DragAssets<DragDropEventArgs>(ValidateDragItem));
|
||||
DragHandlers.Add(_dragParameters = new DragNames<DragDropEventArgs>(SurfaceParameter.DragPrefix, ValidateDragParameter));
|
||||
|
||||
OnEditorOptionsChanged(Editor.Instance.Options.Options);
|
||||
|
||||
ScriptsBuilder.ScriptsReloadBegin += OnScriptsReloadBegin;
|
||||
Editor.Instance.Options.OptionsChanged += OnEditorOptionsChanged;
|
||||
}
|
||||
|
||||
private void OnScriptsReloadBegin()
|
||||
@@ -446,6 +450,11 @@ namespace FlaxEditor.Surface
|
||||
_cmPrimaryMenu = null;
|
||||
}
|
||||
|
||||
private void OnEditorOptionsChanged(EditorOptions options)
|
||||
{
|
||||
_focusSelectedNodeBinding = options.Input.FocusSelectedNodes;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the display name of the connection type used in the surface.
|
||||
/// </summary>
|
||||
@@ -583,6 +592,11 @@ namespace FlaxEditor.Surface
|
||||
/// </summary>
|
||||
public virtual bool CanSetParameters => false;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether surface private parameters can be used, otherwise they will remain hidden.
|
||||
/// </summary>
|
||||
public virtual bool CanShowPrivateParameters => false;
|
||||
|
||||
/// <summary>
|
||||
/// True of the context menu should make use of a description panel drawn at the bottom of the menu
|
||||
/// </summary>
|
||||
@@ -643,6 +657,37 @@ namespace FlaxEditor.Surface
|
||||
ViewCenterPosition = areaRect.Center;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adjusts the view to focus on the currently selected nodes, or the entire graph if no nodes are selected.
|
||||
/// </summary>
|
||||
public void FocusSelectionOrWholeGraph()
|
||||
{
|
||||
if (SelectedNodes.Count > 0)
|
||||
ShowSelection();
|
||||
else
|
||||
ShowWholeGraph();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Shows the selected controls by changing the view scale and the position.
|
||||
/// </summary>
|
||||
public void ShowSelection()
|
||||
{
|
||||
var selection = SelectedControls;
|
||||
if (selection.Count == 0)
|
||||
return;
|
||||
|
||||
// Calculate the bounds of all selected controls
|
||||
Rectangle bounds = selection[0].Bounds;
|
||||
for (int i = 1; i < selection.Count; i++)
|
||||
bounds = Rectangle.Union(bounds, selection[i].Bounds);
|
||||
|
||||
// Add margin
|
||||
bounds = bounds.MakeExpanded(250.0f);
|
||||
|
||||
ShowArea(bounds);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Shows the given surface node by changing the view scale and the position and focuses the node.
|
||||
/// </summary>
|
||||
@@ -1066,6 +1111,7 @@ namespace FlaxEditor.Surface
|
||||
_cmPrimaryMenu?.Dispose();
|
||||
|
||||
ScriptsBuilder.ScriptsReloadBegin -= OnScriptsReloadBegin;
|
||||
Editor.Instance.Options.OptionsChanged += OnEditorOptionsChanged;
|
||||
|
||||
base.OnDestroy();
|
||||
}
|
||||
|
||||
@@ -254,9 +254,10 @@ namespace FlaxEditor.Surface
|
||||
public SurfaceParameter GetParameter(Guid id)
|
||||
{
|
||||
SurfaceParameter result = null;
|
||||
for (int i = 0; i < Parameters.Count; i++)
|
||||
var parameters = Parameters;
|
||||
for (int i = 0; i < parameters.Count; i++)
|
||||
{
|
||||
var parameter = Parameters[i];
|
||||
var parameter = parameters[i];
|
||||
if (parameter.ID == id)
|
||||
{
|
||||
result = parameter;
|
||||
@@ -274,9 +275,10 @@ namespace FlaxEditor.Surface
|
||||
public SurfaceParameter GetParameter(string name)
|
||||
{
|
||||
SurfaceParameter result = null;
|
||||
for (int i = 0; i < Parameters.Count; i++)
|
||||
var parameters = Parameters;
|
||||
for (int i = 0; i < parameters.Count; i++)
|
||||
{
|
||||
var parameter = Parameters[i];
|
||||
var parameter = parameters[i];
|
||||
if (parameter.Name == name)
|
||||
{
|
||||
result = parameter;
|
||||
|
||||
@@ -708,12 +708,30 @@ namespace FlaxEditor.Surface
|
||||
{
|
||||
var index = (int)label.Tag;
|
||||
menu.AddSeparator();
|
||||
menu.AddButton("Copy name", () => Clipboard.Text = ((IVisjectSurfaceWindow)Values[0]).VisjectSurface.Parameters[index].Name);
|
||||
// TODO: move 'Copy all names' to context menu of the Properties category (as it's not item-specific)
|
||||
menu.AddButton("Copy all names", CopyAllParameterNamesAsConstantCSharpCode);
|
||||
menu.AddSeparator();
|
||||
menu.AddButton("Rename", () => StartParameterRenaming(index, label));
|
||||
menu.AddButton("Edit attributes...", () => EditAttributesParameter(index, label));
|
||||
menu.AddButton("Delete", () => DeleteParameter(index));
|
||||
OnParamContextMenu(index, menu);
|
||||
}
|
||||
|
||||
private void CopyAllParameterNamesAsConstantCSharpCode()
|
||||
{
|
||||
string allParamNames = "";
|
||||
foreach (var param in ((IVisjectSurfaceWindow)Values[0]).VisjectSurface.Parameters)
|
||||
{
|
||||
string cleanParamName = param.Name.Replace(" ", "");
|
||||
// Filter out headers and other non-parameter entries that can be present in the parameters list
|
||||
if (string.IsNullOrEmpty(cleanParamName))
|
||||
continue;
|
||||
allParamNames += $"private const string {cleanParamName}ParameterName = \"{param.Name}\";\n";
|
||||
}
|
||||
Clipboard.Text = allParamNames;
|
||||
}
|
||||
|
||||
private void StartParameterRenaming(int index, Control label)
|
||||
{
|
||||
var window = (IVisjectSurfaceWindow)Values[0];
|
||||
|
||||
@@ -187,6 +187,9 @@ namespace FlaxEditor.Surface
|
||||
/// <inheritdoc />
|
||||
public override bool CanSetParameters => true;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanShowPrivateParameters => true;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool UseContextMenuDescriptionPanel => true;
|
||||
|
||||
|
||||
@@ -89,7 +89,7 @@ namespace FlaxEditor.Tools.Terrain
|
||||
if (!terrain.HasPatch(ref patchCoord) && _planeModel)
|
||||
{
|
||||
var planeSize = 100.0f;
|
||||
var patchSize = terrain.ChunkSize * FlaxEngine.Terrain.UnitsPerVertex * FlaxEngine.Terrain.PatchEdgeChunksCount;
|
||||
var patchSize = terrain.PatchSize;
|
||||
Matrix world = Matrix.RotationX(-Mathf.PiOverTwo) *
|
||||
Matrix.Scaling(patchSize / planeSize) *
|
||||
Matrix.Translation(patchSize * (0.5f + patchCoord.X), 0, patchSize * (0.5f + patchCoord.Y)) *
|
||||
|
||||
@@ -69,9 +69,9 @@ namespace FlaxEditor.Tools.Terrain.Paint
|
||||
var splatmapIndex = ActiveSplatmapIndex;
|
||||
var splatmapIndexOther = (splatmapIndex + 1) % 2;
|
||||
var chunkSize = terrain.ChunkSize;
|
||||
var heightmapSize = chunkSize * FlaxEngine.Terrain.PatchEdgeChunksCount + 1;
|
||||
var heightmapSize = terrain.HeightmapSize;
|
||||
var heightmapLength = heightmapSize * heightmapSize;
|
||||
var patchSize = chunkSize * FlaxEngine.Terrain.UnitsPerVertex * FlaxEngine.Terrain.PatchEdgeChunksCount;
|
||||
var patchSize = terrain.PatchSize;
|
||||
var tempBuffer = (Color32*)gizmo.GetSplatmapTempBuffer(heightmapLength * Color32.SizeInBytes, splatmapIndex).ToPointer();
|
||||
var tempBufferOther = (Color32*)gizmo.GetSplatmapTempBuffer(heightmapLength * Color32.SizeInBytes, (splatmapIndex + 1) % 2).ToPointer();
|
||||
var unitsPerVertexInv = 1.0f / FlaxEngine.Terrain.UnitsPerVertex;
|
||||
|
||||
@@ -70,9 +70,9 @@ namespace FlaxEditor.Tools.Terrain.Sculpt
|
||||
|
||||
// Prepare
|
||||
var chunkSize = terrain.ChunkSize;
|
||||
var heightmapSize = chunkSize * FlaxEngine.Terrain.PatchEdgeChunksCount + 1;
|
||||
var heightmapSize = terrain.HeightmapSize;
|
||||
var heightmapLength = heightmapSize * heightmapSize;
|
||||
var patchSize = chunkSize * FlaxEngine.Terrain.UnitsPerVertex * FlaxEngine.Terrain.PatchEdgeChunksCount;
|
||||
var patchSize = terrain.PatchSize;
|
||||
var tempBuffer = (float*)gizmo.GetHeightmapTempBuffer(heightmapLength * sizeof(float)).ToPointer();
|
||||
var unitsPerVertexInv = 1.0f / FlaxEngine.Terrain.UnitsPerVertex;
|
||||
|
||||
|
||||
@@ -382,7 +382,8 @@ bool TerrainTools::ExportTerrain(Terrain* terrain, String outputFolder)
|
||||
const Int2 heightmapSize = size * Terrain::ChunksCountEdge * terrain->GetChunkSize() + 1;
|
||||
Array<float> heightmap;
|
||||
heightmap.Resize(heightmapSize.X * heightmapSize.Y);
|
||||
heightmap.SetAll(firstPatch->GetHeightmapData()[0]);
|
||||
if (const float* heightmapData = firstPatch->GetHeightmapData())
|
||||
heightmap.SetAll(heightmapData[0]);
|
||||
|
||||
// Fill heightmap with data from all patches
|
||||
const int32 rowSize = terrain->GetChunkSize() * Terrain::ChunksCountEdge + 1;
|
||||
@@ -392,8 +393,16 @@ bool TerrainTools::ExportTerrain(Terrain* terrain, String outputFolder)
|
||||
const Int2 pos(patch->GetX() - start.X, patch->GetZ() - start.Y);
|
||||
const float* src = patch->GetHeightmapData();
|
||||
float* dst = heightmap.Get() + pos.X * (rowSize - 1) + pos.Y * heightmapSize.X * (rowSize - 1);
|
||||
for (int32 row = 0; row < rowSize; row++)
|
||||
Platform::MemoryCopy(dst + row * heightmapSize.X, src + row * rowSize, rowSize * sizeof(float));
|
||||
if (src)
|
||||
{
|
||||
for (int32 row = 0; row < rowSize; row++)
|
||||
Platform::MemoryCopy(dst + row * heightmapSize.X, src + row * rowSize, rowSize * sizeof(float));
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int32 row = 0; row < rowSize; row++)
|
||||
Platform::MemoryClear(dst + row * heightmapSize.X, rowSize * sizeof(float));
|
||||
}
|
||||
}
|
||||
|
||||
// Interpolate to 16-bit int
|
||||
|
||||
@@ -85,8 +85,7 @@ namespace FlaxEditor.Tools.Terrain.Undo
|
||||
{
|
||||
_terrain = terrain.ID;
|
||||
_patches = new List<PatchData>(4);
|
||||
var chunkSize = terrain.ChunkSize;
|
||||
var heightmapSize = chunkSize * FlaxEngine.Terrain.PatchEdgeChunksCount + 1;
|
||||
var heightmapSize = terrain.HeightmapSize;
|
||||
_heightmapLength = heightmapSize * heightmapSize;
|
||||
_heightmapDataSize = _heightmapLength * stride;
|
||||
|
||||
|
||||
@@ -163,7 +163,6 @@ namespace FlaxEditor.Viewport
|
||||
private bool _useMouseAcceleration;
|
||||
|
||||
// Input
|
||||
|
||||
internal bool _disableInputUpdate;
|
||||
private bool _isControllingMouse, _isViewportControllingMouse, _wasVirtualMouseRightDown, _isVirtualMouseRightDown;
|
||||
private int _deltaFilteringStep;
|
||||
@@ -247,8 +246,8 @@ namespace FlaxEditor.Viewport
|
||||
{
|
||||
_movementSpeed = value;
|
||||
|
||||
if (_cameraButton != null)
|
||||
_cameraButton.Text = string.Format(MovementSpeedTextFormat, _movementSpeed);
|
||||
if (_orthographicModeButton != null)
|
||||
_orthographicModeButton.Text = string.Format(MovementSpeedTextFormat, _movementSpeed);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -581,7 +580,7 @@ namespace FlaxEditor.Viewport
|
||||
|
||||
// Camera Settings Menu
|
||||
var cameraCM = new ContextMenu();
|
||||
_cameraButton = new ViewportWidgetButton(string.Format(MovementSpeedTextFormat, _movementSpeed), _editor.Icons.Camera64, cameraCM, false, cameraSpeedTextWidth)
|
||||
_cameraButton = new ViewportWidgetButton("", _editor.Icons.Camera64, cameraCM)
|
||||
{
|
||||
Tag = this,
|
||||
TooltipText = "Camera Settings.",
|
||||
@@ -590,7 +589,7 @@ namespace FlaxEditor.Viewport
|
||||
_cameraWidget.Parent = this;
|
||||
|
||||
// Orthographic/Perspective Mode Widget
|
||||
_orthographicModeButton = new ViewportWidgetButton(string.Empty, _editor.Icons.CamSpeed32, null, true)
|
||||
_orthographicModeButton = new OrthoCamToggleViewportWidgetButton(cameraSpeedTextWidth)
|
||||
{
|
||||
Checked = !_isOrtho,
|
||||
TooltipText = "Toggle Orthographic/Perspective Mode.",
|
||||
@@ -1223,7 +1222,7 @@ namespace FlaxEditor.Viewport
|
||||
/// Orients the viewport.
|
||||
/// </summary>
|
||||
/// <param name="orientation">The orientation.</param>
|
||||
protected void OrientViewport(Quaternion orientation)
|
||||
public void OrientViewport(Quaternion orientation)
|
||||
{
|
||||
OrientViewport(ref orientation);
|
||||
}
|
||||
@@ -1232,7 +1231,7 @@ namespace FlaxEditor.Viewport
|
||||
/// Orients the viewport.
|
||||
/// </summary>
|
||||
/// <param name="orientation">The orientation.</param>
|
||||
protected virtual void OrientViewport(ref Quaternion orientation)
|
||||
public virtual void OrientViewport(ref Quaternion orientation)
|
||||
{
|
||||
if (ViewportCamera is FPSCamera fpsCamera)
|
||||
{
|
||||
|
||||
@@ -1,18 +1,19 @@
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Object = FlaxEngine.Object;
|
||||
using FlaxEditor.Content;
|
||||
using FlaxEditor.Gizmo;
|
||||
using FlaxEditor.GUI.ContextMenu;
|
||||
using FlaxEditor.Options;
|
||||
using FlaxEditor.SceneGraph;
|
||||
using FlaxEditor.Scripting;
|
||||
using FlaxEditor.Viewport.Modes;
|
||||
using FlaxEditor.Viewport.Widgets;
|
||||
using FlaxEditor.Windows;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.Gizmo;
|
||||
using FlaxEngine.GUI;
|
||||
using Object = FlaxEngine.Object;
|
||||
|
||||
namespace FlaxEditor.Viewport
|
||||
{
|
||||
@@ -26,6 +27,7 @@ namespace FlaxEditor.Viewport
|
||||
private readonly ContextMenuButton _showGridButton;
|
||||
private readonly ContextMenuButton _showNavigationButton;
|
||||
private readonly ContextMenuButton _toggleGameViewButton;
|
||||
private readonly ContextMenuButton _showDirectionGizmoButton;
|
||||
private SelectionOutline _customSelectionOutline;
|
||||
|
||||
/// <summary>
|
||||
@@ -108,13 +110,14 @@ namespace FlaxEditor.Viewport
|
||||
private readonly ViewportDebugDrawData _debugDrawData = new ViewportDebugDrawData(32);
|
||||
private EditorSpritesRenderer _editorSpritesRenderer;
|
||||
private ViewportRubberBandSelector _rubberBandSelector;
|
||||
private DirectionGizmo _directionGizmo;
|
||||
|
||||
private bool _gameViewActive;
|
||||
private ViewFlags _preGameViewFlags;
|
||||
private ViewMode _preGameViewViewMode;
|
||||
private bool _gameViewWasGridShown;
|
||||
private bool _gameViewWasFpsCounterShown;
|
||||
private bool _gameViewWasNagivationShown;
|
||||
private bool _gameViewWasNavigationShown;
|
||||
|
||||
/// <summary>
|
||||
/// Drag and drop handlers
|
||||
@@ -226,6 +229,13 @@ namespace FlaxEditor.Viewport
|
||||
// Add rubber band selector
|
||||
_rubberBandSelector = new ViewportRubberBandSelector(this);
|
||||
|
||||
// Add direction gizmo
|
||||
_directionGizmo = new DirectionGizmo(this)
|
||||
{
|
||||
AnchorPreset = AnchorPresets.TopRight,
|
||||
Parent = this,
|
||||
};
|
||||
|
||||
// Add grid
|
||||
Grid = new GridGizmo(this);
|
||||
Grid.EnabledChanged += gizmo => _showGridButton.Icon = gizmo.Enabled ? Style.Current.CheckBoxTick : SpriteHandle.Invalid;
|
||||
@@ -244,6 +254,11 @@ namespace FlaxEditor.Viewport
|
||||
_showNavigationButton = ViewWidgetShowMenu.AddButton("Navigation", inputOptions.ToggleNavMeshVisibility, () => ShowNavigation = !ShowNavigation);
|
||||
_showNavigationButton.CloseMenuOnClick = false;
|
||||
|
||||
// Show direction gizmo widget
|
||||
_showDirectionGizmoButton = ViewWidgetShowMenu.AddButton("Direction Gizmo", () => _directionGizmo.Visible = !_directionGizmo.Visible);
|
||||
_showDirectionGizmoButton.AutoCheck = true;
|
||||
_showDirectionGizmoButton.CloseMenuOnClick = false;
|
||||
|
||||
// Game View
|
||||
ViewWidgetButtonMenu.AddSeparator();
|
||||
_toggleGameViewButton = ViewWidgetButtonMenu.AddButton("Game View", inputOptions.ToggleGameView, ToggleGameView);
|
||||
@@ -277,6 +292,18 @@ namespace FlaxEditor.Viewport
|
||||
|
||||
// Game View
|
||||
InputActions.Add(options => options.ToggleGameView, ToggleGameView);
|
||||
|
||||
editor.Options.OptionsChanged += OnEditorOptionsChanged;
|
||||
OnEditorOptionsChanged(editor.Options.Options);
|
||||
}
|
||||
|
||||
private void OnEditorOptionsChanged(EditorOptions options)
|
||||
{
|
||||
_directionGizmo.Visible = options.Viewport.ShowDirectionGizmo;
|
||||
_showDirectionGizmoButton.Checked = _directionGizmo.Visible;
|
||||
_directionGizmo.Size = new Float2(DirectionGizmo.DefaultGizmoSize * options.Viewport.DirectionGizmoScale);
|
||||
_directionGizmo.LocalX = -_directionGizmo.Size.X * 0.5f;
|
||||
_directionGizmo.LocalY = _directionGizmo.Size.Y * 0.5f + ViewportWidgetsContainer.WidgetsHeight;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -514,14 +541,14 @@ namespace FlaxEditor.Viewport
|
||||
_preGameViewViewMode = Task.ViewMode;
|
||||
_gameViewWasGridShown = Grid.Enabled;
|
||||
_gameViewWasFpsCounterShown = ShowFpsCounter;
|
||||
_gameViewWasNagivationShown = ShowNavigation;
|
||||
_gameViewWasNavigationShown = ShowNavigation;
|
||||
}
|
||||
|
||||
// Set flags & values
|
||||
Task.ViewFlags = _gameViewActive ? _preGameViewFlags : ViewFlags.DefaultGame;
|
||||
Task.ViewMode = _gameViewActive ? _preGameViewViewMode : ViewMode.Default;
|
||||
ShowFpsCounter = _gameViewActive ? _gameViewWasFpsCounterShown : false;
|
||||
ShowNavigation = _gameViewActive ? _gameViewWasNagivationShown : false;
|
||||
ShowNavigation = _gameViewActive ? _gameViewWasNavigationShown : false;
|
||||
Grid.Enabled = _gameViewActive ? _gameViewWasGridShown : false;
|
||||
|
||||
_gameViewActive = !_gameViewActive;
|
||||
@@ -647,7 +674,7 @@ namespace FlaxEditor.Viewport
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OrientViewport(ref Quaternion orientation)
|
||||
public override void OrientViewport(ref Quaternion orientation)
|
||||
{
|
||||
if (TransformGizmo.SelectedParents.Count != 0)
|
||||
FocusSelection(ref orientation);
|
||||
|
||||
@@ -6,7 +6,6 @@ using System.Linq;
|
||||
using FlaxEditor.Content;
|
||||
using FlaxEditor.Gizmo;
|
||||
using FlaxEditor.GUI.ContextMenu;
|
||||
using FlaxEditor.GUI.Input;
|
||||
using FlaxEditor.Modules;
|
||||
using FlaxEditor.SceneGraph;
|
||||
using FlaxEditor.Scripting;
|
||||
@@ -15,7 +14,6 @@ using FlaxEditor.Viewport.Previews;
|
||||
using FlaxEditor.Windows.Assets;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
using FlaxEngine.Json;
|
||||
using Utils = FlaxEditor.Utilities.Utils;
|
||||
|
||||
namespace FlaxEditor.Viewport
|
||||
@@ -185,6 +183,7 @@ namespace FlaxEditor.Viewport
|
||||
showGridButton.Clicked += () =>
|
||||
{
|
||||
_gridGizmo.Enabled = !_gridGizmo.Enabled;
|
||||
_uiRoot.ShowGrid = _gridGizmo.Enabled;
|
||||
showGridButton.Checked = _gridGizmo.Enabled;
|
||||
};
|
||||
showGridButton.Checked = true;
|
||||
@@ -682,7 +681,7 @@ namespace FlaxEditor.Viewport
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OrientViewport(ref Quaternion orientation)
|
||||
public override void OrientViewport(ref Quaternion orientation)
|
||||
{
|
||||
if (TransformGizmo.SelectedParents.Count != 0)
|
||||
FocusSelection(ref orientation);
|
||||
|
||||
@@ -303,8 +303,7 @@ namespace FlaxEditor.Viewport.Previews
|
||||
{
|
||||
_terrain = new Terrain();
|
||||
_terrain.Setup(1, 63);
|
||||
var chunkSize = _terrain.ChunkSize;
|
||||
var heightMapSize = chunkSize * Terrain.PatchEdgeChunksCount + 1;
|
||||
var heightMapSize = _terrain.HeightmapSize;
|
||||
var heightMapLength = heightMapSize * heightMapSize;
|
||||
var heightmap = new float[heightMapLength];
|
||||
var patchCoord = new Int2(0, 0);
|
||||
|
||||
@@ -198,6 +198,11 @@ namespace FlaxEditor.Viewport
|
||||
actor.Position = PostProcessSpawnedActorLocation(actor, ref hitLocation);
|
||||
_owner.Spawn(actor);
|
||||
_viewport.Focus();
|
||||
|
||||
// Scroll to the new actor in the hierarchy
|
||||
var actorNode = Editor.Instance.Scene.GetActorNode(actor);
|
||||
if (actorNode?.TreeNode.ParentTree.Parent is Panel treePanel)
|
||||
FlaxEngine.Scripting.InvokeOnUpdate(() => treePanel.ScrollViewTo(actorNode.TreeNode));
|
||||
}
|
||||
|
||||
private void Spawn(ScriptItem item, SceneGraphNode hit, ref Float2 location, ref Vector3 hitLocation, ref Vector3 hitNormal)
|
||||
|
||||
@@ -7,6 +7,100 @@ using FlaxEngine.GUI;
|
||||
|
||||
namespace FlaxEditor.Viewport.Widgets
|
||||
{
|
||||
/// <summary>
|
||||
/// Otrhographic view toggle viewport Widget Button class.
|
||||
/// Will draw a custom camera frustum to represent an orthographic or perspective camera.
|
||||
/// </summary>
|
||||
/// <seealso cref="ViewportWidgetButton" />
|
||||
[HideInEditor]
|
||||
public class OrthoCamToggleViewportWidgetButton : ViewportWidgetButton
|
||||
{
|
||||
private const int iconPointCount = 4;
|
||||
private const float iconRenderScale = 4.0f;
|
||||
|
||||
private readonly Float2 iconDrawOffset = new Float2(3.0f, 3.0f);
|
||||
|
||||
private readonly Float2[] iconPointsPerspective = new[]
|
||||
{
|
||||
new Float2(0.0f, 1.0f), // Top left
|
||||
new Float2(4.0f, 0.0f), // Top right
|
||||
new Float2(4.0f, 3.0f), // Bottom right
|
||||
new Float2(0.0f, 2.0f), // Bottom left
|
||||
};
|
||||
|
||||
private readonly Float2[] iconPointsOrtho = new[]
|
||||
{
|
||||
new Float2(0.0f, 0.0f), // Top left
|
||||
new Float2(4.0f, 0.0f), // Top right
|
||||
new Float2(4.0f, 3.0f), // Bottom right
|
||||
new Float2(0.0f, 3.0f), // Bottom left
|
||||
};
|
||||
|
||||
private bool wasChecked;
|
||||
private float lerpWeight;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="OrthoCamToggleViewportWidgetButton"/> class.
|
||||
/// </summary>
|
||||
public OrthoCamToggleViewportWidgetButton(float speedTextWidth)
|
||||
: base("00.0", SpriteHandle.Invalid, null, true, 20.0f + speedTextWidth)
|
||||
{
|
||||
wasChecked = Checked;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Update(float deltaTime)
|
||||
{
|
||||
if (wasChecked != Checked)
|
||||
{
|
||||
lerpWeight = 0.0f;
|
||||
wasChecked = Checked;
|
||||
}
|
||||
|
||||
if (lerpWeight <= 1.0f)
|
||||
lerpWeight += deltaTime * 4.2f;
|
||||
|
||||
base.Update(deltaTime);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Draw()
|
||||
{
|
||||
// Cache data
|
||||
var style = Style.Current;
|
||||
var textRect = new Rectangle(0.0f, 0.0f, Width - 2.0f, Height);
|
||||
var backgroundRect = textRect with { Width = Width - 1.0f };
|
||||
|
||||
// Check if is checked or mouse is over
|
||||
if (Checked)
|
||||
Render2D.FillRectangle(backgroundRect, style.BackgroundSelected * (IsMouseOver ? 0.9f : 0.6f));
|
||||
else if (IsMouseOver)
|
||||
Render2D.FillRectangle(backgroundRect, style.BackgroundHighlighted);
|
||||
|
||||
// Draw text
|
||||
Render2D.DrawText(style.FontMedium, Text, textRect, style.ForegroundViewport * (IsMouseOver ? 1.0f : 0.9f), TextAlignment.Far, TextAlignment.Center);
|
||||
|
||||
// Draw camera frustum icon
|
||||
Float2[] currentStart = Checked ? iconPointsOrtho : iconPointsPerspective;
|
||||
Float2[] currentTarget = Checked ? iconPointsPerspective : iconPointsOrtho;
|
||||
|
||||
var features = Render2D.Features;
|
||||
Render2D.Features = features & ~Render2D.RenderingFeatures.VertexSnapping;
|
||||
for (int i = 1; i < iconPointCount + 1; i++)
|
||||
{
|
||||
int endPointIndex = Mathf.Wrap(i, 0, iconPointCount - 1);
|
||||
Float2 lineStart = Float2.Lerp(currentStart[i - 1], currentTarget[i - 1], lerpWeight);
|
||||
Float2 lineEnd = Float2.Lerp(currentStart[endPointIndex], currentTarget[endPointIndex], lerpWeight);
|
||||
|
||||
lineStart = lineStart * iconRenderScale + iconDrawOffset;
|
||||
lineEnd = lineEnd * iconRenderScale + iconDrawOffset;
|
||||
|
||||
Render2D.DrawLine(lineStart, lineEnd, Color.White);
|
||||
}
|
||||
Render2D.Features = features;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Viewport Widget Button class.
|
||||
/// </summary>
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
//#define USE_AUTODESK_FBX_SDK
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using FlaxEditor.GUI.Dialogs;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
@@ -45,9 +46,16 @@ namespace FlaxEditor.Windows
|
||||
VerticalAlignment = TextAlignment.Center,
|
||||
Parent = this
|
||||
};
|
||||
var assembly = typeof(Editor).Assembly;
|
||||
var assemblyCopyright = assembly.GetCustomAttribute<AssemblyCopyrightAttribute>();
|
||||
var assemblyInformationalVersion = assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>();
|
||||
var versionParts = assemblyInformationalVersion.InformationalVersion.Split('+');
|
||||
string versionInfo = string.Empty;
|
||||
if (versionParts.Length == 3)
|
||||
versionInfo = $"\nBranch: {versionParts[1]}+{(versionParts[2].Length == 40 ? versionParts[2].Substring(0, 8) : versionParts[2])}";
|
||||
new Label(nameLabel.Left, nameLabel.Bottom + 4, nameLabel.Width, 50)
|
||||
{
|
||||
Text = string.Format("Version: {0}\nCopyright (c) 2012-2025 Wojciech Figat.\nAll rights reserved.", Globals.EngineVersion),
|
||||
Text = $"Version: {Globals.EngineVersion}{versionInfo}\n{assemblyCopyright.Copyright.Replace(". ", ".\n")}",
|
||||
HorizontalAlignment = TextAlignment.Near,
|
||||
VerticalAlignment = TextAlignment.Near,
|
||||
Parent = this
|
||||
@@ -98,6 +106,7 @@ namespace FlaxEditor.Windows
|
||||
"Chandler Cox",
|
||||
"Ari Vuollet",
|
||||
"Vincent Saarmann",
|
||||
"Michael Salvini",
|
||||
});
|
||||
authors.Sort();
|
||||
var authorsLabel = new Label(4, topParentControl.Bottom + 20, Width - 8, 70)
|
||||
|
||||
@@ -431,6 +431,9 @@ namespace FlaxEditor.Windows.Assets
|
||||
_isWaitingForTimelineLoad = true;
|
||||
|
||||
base.OnItemReimported(item);
|
||||
|
||||
// Drop virtual asset state and get a new one from the reimported file
|
||||
LoadFromOriginal();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -455,6 +458,7 @@ namespace FlaxEditor.Windows.Assets
|
||||
_timeline.Enabled = true;
|
||||
_timeline.SetNoTracksText(null);
|
||||
ClearEditedFlag();
|
||||
_timeline.ShowWholeTimeline();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@ namespace FlaxEditor.Windows.Assets
|
||||
{
|
||||
Parent = this
|
||||
};
|
||||
_toolstrip.AddButton(editor.Icons.Search64, () => Editor.Windows.ContentWin.Select(_item)).LinkTooltip("Show and select in Content Window");
|
||||
_toolstrip.AddButton(editor.Icons.Search64, () => Editor.Windows.ContentWin.Select(_item)).LinkTooltip("Show and select in Content Window.");
|
||||
|
||||
InputActions.Add(options => options.Save, Save);
|
||||
|
||||
@@ -527,6 +527,16 @@ namespace FlaxEditor.Windows.Assets
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads the asset from the original location to reflect the state (eg. after original asset reimport).
|
||||
/// </summary>
|
||||
protected virtual void LoadFromOriginal()
|
||||
{
|
||||
_asset = LoadAsset();
|
||||
OnAssetLoaded();
|
||||
ClearEditedFlag();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override T LoadAsset()
|
||||
{
|
||||
|
||||
@@ -21,6 +21,10 @@ namespace FlaxEditor.Windows.Assets
|
||||
/// </summary>
|
||||
private sealed class PropertiesProxy
|
||||
{
|
||||
[DefaultValue(FontRasterMode.Bitmap)]
|
||||
[EditorOrder(5), EditorDisplay("Properties"), Tooltip("The rasterization mode used when generating font atlases.")]
|
||||
public FontRasterMode RasterMode;
|
||||
|
||||
[DefaultValue(FontHinting.Default)]
|
||||
[EditorOrder(10), EditorDisplay("Properties"), Tooltip("The font hinting used when rendering characters.")]
|
||||
public FontHinting Hinting;
|
||||
@@ -41,7 +45,8 @@ namespace FlaxEditor.Windows.Assets
|
||||
{
|
||||
options = new FontOptions
|
||||
{
|
||||
Hinting = Hinting
|
||||
Hinting = Hinting,
|
||||
RasterMode = RasterMode,
|
||||
};
|
||||
if (AntiAliasing)
|
||||
options.Flags |= FontFlags.AntiAliasing;
|
||||
@@ -57,6 +62,7 @@ namespace FlaxEditor.Windows.Assets
|
||||
AntiAliasing = (options.Flags & FontFlags.AntiAliasing) == FontFlags.AntiAliasing;
|
||||
Bold = (options.Flags & FontFlags.Bold) == FontFlags.Bold;
|
||||
Italic = (options.Flags & FontFlags.Italic) == FontFlags.Italic;
|
||||
RasterMode = options.RasterMode;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -39,8 +39,7 @@ namespace FlaxEditor.Windows
|
||||
folder = CurrentViewFolder;
|
||||
}
|
||||
Assert.IsNotNull(folder);
|
||||
bool isRootFolder = CurrentViewFolder == _root.Folder;
|
||||
|
||||
|
||||
// Create context menu
|
||||
ContextMenuButton b;
|
||||
ContextMenu cm = new ContextMenu
|
||||
@@ -178,19 +177,61 @@ namespace FlaxEditor.Windows
|
||||
|
||||
cm.AddSeparator();
|
||||
|
||||
CreateNewModuleMenu(cm, folder);
|
||||
CreateNewFolderMenu(cm, folder, false, item);
|
||||
CreateNewContentItemMenu(cm, folder);
|
||||
|
||||
if (folder.CanHaveAssets)
|
||||
{
|
||||
cm.AddButton("Import file", () =>
|
||||
{
|
||||
_view.ClearSelection();
|
||||
Editor.ContentImporting.ShowImportFileDialog(CurrentViewFolder);
|
||||
});
|
||||
}
|
||||
|
||||
// Remove any leftover separator
|
||||
if (cm.ItemsContainer.Children.LastOrDefault() is ContextMenuSeparator)
|
||||
cm.ItemsContainer.Children.Last().Dispose();
|
||||
|
||||
// Show it
|
||||
cm.Show(this, location);
|
||||
}
|
||||
|
||||
private void CreateNewModuleMenu(ContextMenu menu, ContentFolder folder, bool disableUncreatable = false)
|
||||
{
|
||||
// Check if is source folder to add new module
|
||||
if (folder?.ParentFolder?.Node is ProjectFolderTreeNode parentFolderNode && folder.Node == parentFolderNode.Source)
|
||||
{
|
||||
var button = cm.AddButton("New module");
|
||||
var button = menu.AddButton("New module");
|
||||
button.CloseMenuOnClick = false;
|
||||
button.Clicked += () => NewModule(button, parentFolderNode.Source.Path);
|
||||
}
|
||||
|
||||
if (!isRootFolder && !(item is ContentFolder projectFolder && projectFolder.Node is ProjectFolderTreeNode))
|
||||
else if (disableUncreatable)
|
||||
{
|
||||
cm.AddButton("New folder", NewFolder);
|
||||
var button = menu.AddButton("New module");
|
||||
button.Enabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
private bool CanCreateFolder(ContentItem item = null)
|
||||
{
|
||||
bool canCreateFolder = CurrentViewFolder != _root.Folder && !(item is ContentFolder projectFolder && projectFolder.Node is ProjectFolderTreeNode);
|
||||
return canCreateFolder;
|
||||
}
|
||||
|
||||
private void CreateNewFolderMenu(ContextMenu menu, ContentFolder folder, bool disableUncreatable = false, ContentItem item = null)
|
||||
{
|
||||
bool canCreateFolder = CanCreateFolder(item);
|
||||
if (canCreateFolder || disableUncreatable)
|
||||
{
|
||||
var b = menu.AddButton("New folder", NewFolder);
|
||||
b.Enabled = canCreateFolder;
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateNewContentItemMenu(ContextMenu menu, ContentFolder folder, bool showNew = true, bool disableUncreatable = false)
|
||||
{
|
||||
// Loop through each proxy and user defined json type and add them to the context menu
|
||||
var actorType = new ScriptType(typeof(Actor));
|
||||
var scriptType = new ScriptType(typeof(Script));
|
||||
@@ -230,7 +271,8 @@ namespace FlaxEditor.Windows
|
||||
if (p == null)
|
||||
continue;
|
||||
|
||||
if (p.CanCreate(folder))
|
||||
bool canCreate = p.CanCreate(folder);
|
||||
if (canCreate || disableUncreatable)
|
||||
{
|
||||
var parts = attribute.Path.Split('/');
|
||||
ContextMenuChildMenu childCM = null;
|
||||
@@ -238,16 +280,20 @@ namespace FlaxEditor.Windows
|
||||
for (int i = 0; i < parts?.Length; i++)
|
||||
{
|
||||
var part = parts[i].Trim();
|
||||
if (part == "New" && !showNew)
|
||||
continue;
|
||||
if (i == parts.Length - 1)
|
||||
{
|
||||
if (mainCM)
|
||||
{
|
||||
cm.AddButton(part, () => NewItem(p));
|
||||
var b = menu.AddButton(part, () => NewItem(p));
|
||||
b.Enabled = canCreate;
|
||||
mainCM = false;
|
||||
}
|
||||
else if (childCM != null)
|
||||
{
|
||||
childCM.ContextMenu.AddButton(part, () => NewItem(p));
|
||||
var b = childCM.ContextMenu.AddButton(part, () => NewItem(p));
|
||||
b.Enabled = canCreate;
|
||||
childCM.ContextMenu.AutoSort = true;
|
||||
}
|
||||
}
|
||||
@@ -255,35 +301,21 @@ namespace FlaxEditor.Windows
|
||||
{
|
||||
if (mainCM)
|
||||
{
|
||||
childCM = cm.GetOrAddChildMenu(part);
|
||||
childCM = menu.GetOrAddChildMenu(part);
|
||||
childCM.ContextMenu.AutoSort = true;
|
||||
childCM.Enabled = canCreate;
|
||||
mainCM = false;
|
||||
}
|
||||
else if (childCM != null)
|
||||
{
|
||||
childCM = childCM.ContextMenu.GetOrAddChildMenu(part);
|
||||
childCM.ContextMenu.AutoSort = true;
|
||||
childCM.Enabled = canCreate;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (folder.CanHaveAssets)
|
||||
{
|
||||
cm.AddButton("Import file", () =>
|
||||
{
|
||||
_view.ClearSelection();
|
||||
Editor.ContentImporting.ShowImportFileDialog(CurrentViewFolder);
|
||||
});
|
||||
}
|
||||
|
||||
// Remove any leftover separator
|
||||
if (cm.ItemsContainer.Children.LastOrDefault() is ContextMenuSeparator)
|
||||
cm.ItemsContainer.Children.Last().Dispose();
|
||||
|
||||
// Show it
|
||||
cm.Show(this, location);
|
||||
}
|
||||
|
||||
private void OnExpandAllClicked(ContextMenuButton button)
|
||||
|
||||
@@ -116,6 +116,7 @@ namespace FlaxEditor.Windows
|
||||
var root = _root;
|
||||
root.LockChildrenRecursive();
|
||||
_suppressExpandedStateSave = true;
|
||||
PerformLayout();
|
||||
|
||||
// Update tree
|
||||
var query = _foldersSearchBox.Text;
|
||||
|
||||
@@ -42,6 +42,7 @@ namespace FlaxEditor.Windows
|
||||
|
||||
private readonly ToolStrip _toolStrip;
|
||||
private readonly ToolStripButton _importButton;
|
||||
private readonly ToolStripButton _createNewButton;
|
||||
private readonly ToolStripButton _navigateBackwardButton;
|
||||
private readonly ToolStripButton _navigateForwardButton;
|
||||
private readonly ToolStripButton _navigateUpButton;
|
||||
@@ -167,11 +168,12 @@ namespace FlaxEditor.Windows
|
||||
{
|
||||
Parent = this,
|
||||
};
|
||||
_importButton = (ToolStripButton)_toolStrip.AddButton(Editor.Icons.Import64, () => Editor.ContentImporting.ShowImportFileDialog(CurrentViewFolder)).LinkTooltip("Import content");
|
||||
_importButton = (ToolStripButton)_toolStrip.AddButton(Editor.Icons.Import64, () => Editor.ContentImporting.ShowImportFileDialog(CurrentViewFolder)).LinkTooltip("Import content.");
|
||||
_createNewButton = (ToolStripButton)_toolStrip.AddButton(Editor.Icons.Add64, OnCreateNewItemButtonClicked).LinkTooltip("Create a new asset. Shift + left click to create a new folder.");
|
||||
_toolStrip.AddSeparator();
|
||||
_navigateBackwardButton = (ToolStripButton)_toolStrip.AddButton(Editor.Icons.Left64, NavigateBackward).LinkTooltip("Navigate backward");
|
||||
_navigateForwardButton = (ToolStripButton)_toolStrip.AddButton(Editor.Icons.Right64, NavigateForward).LinkTooltip("Navigate forward");
|
||||
_navigateUpButton = (ToolStripButton)_toolStrip.AddButton(Editor.Icons.Up64, NavigateUp).LinkTooltip("Navigate up");
|
||||
_navigateBackwardButton = (ToolStripButton)_toolStrip.AddButton(Editor.Icons.Left64, NavigateBackward).LinkTooltip("Navigate backward.");
|
||||
_navigateForwardButton = (ToolStripButton)_toolStrip.AddButton(Editor.Icons.Right64, NavigateForward).LinkTooltip("Navigate forward.");
|
||||
_navigateUpButton = (ToolStripButton)_toolStrip.AddButton(Editor.Icons.Up64, NavigateUp).LinkTooltip("Navigate up.");
|
||||
_toolStrip.AddSeparator();
|
||||
|
||||
// Split panel
|
||||
@@ -319,6 +321,42 @@ namespace FlaxEditor.Windows
|
||||
ApplyTreeViewScale();
|
||||
}
|
||||
|
||||
private void OnCreateNewItemButtonClicked()
|
||||
{
|
||||
if (Input.GetKey(KeyboardKeys.Shift) && CanCreateFolder())
|
||||
{
|
||||
NewFolder();
|
||||
return;
|
||||
}
|
||||
|
||||
var menu = new ContextMenu();
|
||||
|
||||
InterfaceOptions interfaceOptions = Editor.Instance.Options.Options.Interface;
|
||||
bool disableUnavaliable = interfaceOptions.UnavaliableContentCreateOptions == InterfaceOptions.DisabledHidden.Disabled;
|
||||
|
||||
CreateNewFolderMenu(menu, CurrentViewFolder, disableUnavaliable);
|
||||
CreateNewModuleMenu(menu, CurrentViewFolder, disableUnavaliable);
|
||||
menu.AddSeparator();
|
||||
CreateNewContentItemMenu(menu, CurrentViewFolder, false, disableUnavaliable);
|
||||
// Hack: Show the menu once to get the direction, then show it above or below the button depending on the direction.
|
||||
menu.Show(this, _createNewButton.UpperLeft);
|
||||
var direction = menu.Direction;
|
||||
menu.Hide();
|
||||
bool below = false;
|
||||
switch (direction)
|
||||
{
|
||||
case ContextMenuDirection.RightDown:
|
||||
case ContextMenuDirection.LeftDown:
|
||||
below = true;
|
||||
break;
|
||||
case ContextMenuDirection.RightUp:
|
||||
case ContextMenuDirection.LeftUp:
|
||||
below = false;
|
||||
break;
|
||||
}
|
||||
menu.Show(this, below ? _createNewButton.BottomLeft : _createNewButton.UpperLeft, direction);
|
||||
}
|
||||
|
||||
private ContextMenu OnViewDropdownPopupCreate(ComboBox comboBox)
|
||||
{
|
||||
var menu = new ContextMenu();
|
||||
@@ -1592,6 +1630,8 @@ namespace FlaxEditor.Windows
|
||||
if (Editor.ContentDatabase.Find(_lastViewedFolderBeforeReload) is ContentFolder folder)
|
||||
_tree.Select(folder.Node);
|
||||
}
|
||||
|
||||
OnFoldersSearchBoxTextChanged();
|
||||
}
|
||||
|
||||
private void Refresh()
|
||||
|
||||
@@ -257,7 +257,7 @@ namespace FlaxEditor.Windows
|
||||
private void UpdateCameraPreview()
|
||||
{
|
||||
// Disable rendering preview during GI baking
|
||||
if (Editor.StateMachine.CurrentState.IsPerformanceHeavy)
|
||||
if (Editor == null || Editor.StateMachine.CurrentState.IsPerformanceHeavy)
|
||||
{
|
||||
HideAllCameraPreviews();
|
||||
return;
|
||||
@@ -406,6 +406,14 @@ namespace FlaxEditor.Windows
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnSizeChanged()
|
||||
{
|
||||
base.OnSizeChanged();
|
||||
|
||||
UpdateCameraPreview();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnDestroy()
|
||||
{
|
||||
|
||||
@@ -68,6 +68,7 @@ namespace FlaxEditor.Windows
|
||||
TooltipText = "Search the scene tree.\n\nYou can prefix your search with different search operators:\ns: -> Actor with script of type\na: -> Actor type\nc: -> Control type",
|
||||
};
|
||||
_searchBox.TextChanged += OnSearchBoxTextChanged;
|
||||
ScriptsBuilder.ScriptsReloadEnd += OnSearchBoxTextChanged;
|
||||
|
||||
// Scene tree panel
|
||||
_sceneTreePanel = new Panel
|
||||
@@ -112,7 +113,7 @@ namespace FlaxEditor.Windows
|
||||
InputActions.Add(options => options.LockFocusSelection, () => Editor.Windows.EditWin.Viewport.LockFocusSelection());
|
||||
InputActions.Add(options => options.Rename, RenameSelection);
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnPlayBeginning()
|
||||
{
|
||||
@@ -125,6 +126,7 @@ namespace FlaxEditor.Windows
|
||||
{
|
||||
base.OnPlayBegin();
|
||||
_blockSceneTreeScroll = false;
|
||||
OnSearchBoxTextChanged();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -139,6 +141,7 @@ namespace FlaxEditor.Windows
|
||||
{
|
||||
base.OnPlayEnd();
|
||||
_blockSceneTreeScroll = true;
|
||||
OnSearchBoxTextChanged();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -174,6 +177,7 @@ namespace FlaxEditor.Windows
|
||||
return;
|
||||
|
||||
_tree.LockChildrenRecursive();
|
||||
PerformLayout();
|
||||
|
||||
// Update tree
|
||||
var query = _searchBox.Text;
|
||||
@@ -600,6 +604,7 @@ namespace FlaxEditor.Windows
|
||||
_dragHandlers = null;
|
||||
_tree = null;
|
||||
_searchBox = null;
|
||||
ScriptsBuilder.ScriptsReloadEnd -= OnSearchBoxTextChanged;
|
||||
|
||||
base.OnDestroy();
|
||||
}
|
||||
|
||||
@@ -109,7 +109,14 @@ void Behavior::UpdateAsync()
|
||||
const BehaviorUpdateResult result = tree->Graph.Root->InvokeUpdate(context);
|
||||
if (result != BehaviorUpdateResult::Running)
|
||||
_result = result;
|
||||
if (_result != BehaviorUpdateResult::Running)
|
||||
if (_result != BehaviorUpdateResult::Running && tree->Graph.Root->Loop)
|
||||
{
|
||||
// Reset State
|
||||
_result = BehaviorUpdateResult::Running;
|
||||
_accumulatedTime = 0.0f;
|
||||
_totalTime = 0;
|
||||
}
|
||||
else if (_result != BehaviorUpdateResult::Running)
|
||||
{
|
||||
Finished();
|
||||
}
|
||||
|
||||
@@ -96,6 +96,10 @@ API_CLASS(Sealed) class FLAXENGINE_API BehaviorTreeRootNode : public BehaviorTre
|
||||
// The target amount of the behavior logic updates per second.
|
||||
API_FIELD(Attributes="EditorOrder(100)")
|
||||
float UpdateFPS = 10.0f;
|
||||
|
||||
// Whether to loop the root node.
|
||||
API_FIELD(Attributes="EditorOrder(200)")
|
||||
bool Loop = true;
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
#include "Audio.h"
|
||||
#include "AudioBackend.h"
|
||||
#include "AudioSettings.h"
|
||||
#include "FlaxEngine.Gen.h"
|
||||
#include "Engine/Scripting/ScriptingType.h"
|
||||
#include "Engine/Scripting/BinaryModule.h"
|
||||
#include "Engine/Level/Level.h"
|
||||
|
||||
@@ -207,16 +207,16 @@ public:
|
||||
API_PROPERTY() void SetAttenuation(float value);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the doppler effect factor. Scale for source velocity. Default is 1.
|
||||
/// Gets the doppler effect factor. Scale for source velocity. Default is 1. Used by spatial sources only. Cannot scale the effect up (only dim it).
|
||||
/// </summary>
|
||||
API_PROPERTY(Attributes="EditorOrder(75), DefaultValue(1.0f), Limit(0, float.MaxValue, 0.1f), EditorDisplay(\"Audio Source\")")
|
||||
API_PROPERTY(Attributes="EditorOrder(75), DefaultValue(1.0f), Limit(0, 1.0f, 0.1f), EditorDisplay(\"Audio Source\")")
|
||||
FORCE_INLINE float GetDopplerFactor() const
|
||||
{
|
||||
return _dopplerFactor;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the doppler effect factor. Scale for source velocity. Default is 1.
|
||||
/// Sets the doppler effect factor. Scale for source velocity. Default is 1. Used by spatial sources only. Cannot scale the effect up (only dim it).
|
||||
/// </summary>
|
||||
API_PROPERTY() void SetDopplerFactor(float value);
|
||||
|
||||
|
||||
@@ -829,7 +829,6 @@ bool AudioBackendOAL::Base_Init()
|
||||
}
|
||||
|
||||
// Init
|
||||
Base_SetDopplerFactor(AudioSettings::Get()->DopplerFactor);
|
||||
alDistanceModel(AL_INVERSE_DISTANCE_CLAMPED); // Default attenuation model
|
||||
int32 clampedIndex = Math::Clamp(activeDeviceIndex, -1, Audio::Devices.Count() - 1);
|
||||
if (clampedIndex == Audio::GetActiveDeviceIndex())
|
||||
@@ -841,6 +840,7 @@ bool AudioBackendOAL::Base_Init()
|
||||
if (ALC::IsExtensionSupported("AL_SOFT_source_spatialize"))
|
||||
ALC::Features = EnumAddFlags(ALC::Features, FeatureFlags::SpatialMultiChannel);
|
||||
#endif
|
||||
Base_SetDopplerFactor(AudioSettings::Get()->DopplerFactor);
|
||||
ALC::Inited = true;
|
||||
|
||||
// Log service info
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
#include "Loading/Tasks/LoadAssetTask.h"
|
||||
#include "Engine/Core/Log.h"
|
||||
#include "Engine/Core/LogContext.h"
|
||||
#include "Engine/Graphics/GPUDevice.h"
|
||||
#include "Engine/Physics/Physics.h"
|
||||
#include "Engine/Profiler/ProfilerCPU.h"
|
||||
#include "Engine/Profiler/ProfilerMemory.h"
|
||||
#include "Engine/Scripting/ManagedCLR/MCore.h"
|
||||
@@ -703,6 +705,38 @@ void Asset::onUnload_MainThread()
|
||||
OnUnloaded(this);
|
||||
}
|
||||
|
||||
bool Asset::WaitForInitGraphics()
|
||||
{
|
||||
#define IS_GPU_NOT_READY() (GPUDevice::Instance == nullptr || GPUDevice::Instance->GetState() != GPUDevice::DeviceState::Ready)
|
||||
if (!IsInMainThread() && IS_GPU_NOT_READY())
|
||||
{
|
||||
PROFILE_CPU();
|
||||
ZoneColor(TracyWaitZoneColor);
|
||||
int32 timeout = 1000;
|
||||
while (IS_GPU_NOT_READY() && timeout-- > 0)
|
||||
Platform::Sleep(1);
|
||||
if (IS_GPU_NOT_READY())
|
||||
return true;
|
||||
}
|
||||
#undef IS_GPU_NOT_READY
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Asset::WaitForInitPhysics()
|
||||
{
|
||||
if (!IsInMainThread() && !Physics::DefaultScene)
|
||||
{
|
||||
PROFILE_CPU();
|
||||
ZoneColor(TracyWaitZoneColor);
|
||||
int32 timeout = 1000;
|
||||
while (!Physics::DefaultScene && timeout-- > 0)
|
||||
Platform::Sleep(1);
|
||||
if (!Physics::DefaultScene)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
#if USE_EDITOR
|
||||
|
||||
bool Asset::OnCheckSave(const StringView& path) const
|
||||
|
||||
@@ -285,6 +285,10 @@ protected:
|
||||
virtual void onRename(const StringView& newPath) = 0;
|
||||
#endif
|
||||
|
||||
// Utilities to ensure specific engine systems are initialized before loading asset (eg. assets can be loaded during engine startup).
|
||||
static bool WaitForInitGraphics();
|
||||
static bool WaitForInitPhysics();
|
||||
|
||||
public:
|
||||
// [ManagedScriptingObject]
|
||||
String ToString() const override;
|
||||
|
||||
@@ -691,11 +691,7 @@ Asset::LoadResult Animation::load()
|
||||
continue;
|
||||
}
|
||||
#if USE_EDITOR
|
||||
if (!_registeredForScriptingReload)
|
||||
{
|
||||
_registeredForScriptingReload = true;
|
||||
Level::ScriptsReloadStart.Bind<Animation, &Animation::OnScriptsReloadStart>(this);
|
||||
}
|
||||
_registerForScriptingReload = true;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -733,6 +729,7 @@ void Animation::unload(bool isReloading)
|
||||
{
|
||||
ScopeWriteLock systemScope(Animations::SystemLocker);
|
||||
#if USE_EDITOR
|
||||
_registerForScriptingReload = false;
|
||||
if (_registeredForScriptingReload)
|
||||
{
|
||||
_registeredForScriptingReload = false;
|
||||
@@ -752,6 +749,22 @@ void Animation::unload(bool isReloading)
|
||||
NestedAnims.Clear();
|
||||
}
|
||||
|
||||
#if USE_EDITOR
|
||||
|
||||
void Animation::onLoaded_MainThread()
|
||||
{
|
||||
if (_registerForScriptingReload && !_registeredForScriptingReload)
|
||||
{
|
||||
_registeredForScriptingReload = true;
|
||||
Level::ScriptsReloadStart.Bind<Animation, &Animation::OnScriptsReloadStart>(this);
|
||||
}
|
||||
_registerForScriptingReload = false;
|
||||
|
||||
BinaryAsset::onLoaded_MainThread();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
AssetChunksFlag Animation::getChunksToPreload() const
|
||||
{
|
||||
return GET_CHUNK_FLAG(0);
|
||||
|
||||
@@ -78,6 +78,7 @@ API_CLASS(NoSpawn) class FLAXENGINE_API Animation : public BinaryAsset
|
||||
|
||||
private:
|
||||
#if USE_EDITOR
|
||||
bool _registerForScriptingReload = false;
|
||||
bool _registeredForScriptingReload = false;
|
||||
void OnScriptsReloadStart();
|
||||
#endif
|
||||
@@ -163,5 +164,8 @@ protected:
|
||||
// [BinaryAsset]
|
||||
LoadResult load() override;
|
||||
void unload(bool isReloading) override;
|
||||
#if USE_EDITOR
|
||||
void onLoaded_MainThread() override;
|
||||
#endif
|
||||
AssetChunksFlag getChunksToPreload() const override;
|
||||
};
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
#include "Engine/Core/Types/DataContainer.h"
|
||||
#include "Engine/Content/Upgraders/ShaderAssetUpgrader.h"
|
||||
#include "Engine/Content/Factories/BinaryAssetFactory.h"
|
||||
#include "Engine/Graphics/GPUDevice.h"
|
||||
#include "Engine/Graphics/RenderTools.h"
|
||||
#include "Engine/Graphics/Materials/MaterialShader.h"
|
||||
#include "Engine/Graphics/Shaders/Cache/ShaderCacheManager.h"
|
||||
@@ -156,16 +155,8 @@ Asset::LoadResult Material::load()
|
||||
FlaxChunk* materialParamsChunk;
|
||||
|
||||
// Wait for the GPU Device to be ready (eg. case when loading material before GPU init)
|
||||
#define IS_GPU_NOT_READY() (GPUDevice::Instance == nullptr || GPUDevice::Instance->GetState() != GPUDevice::DeviceState::Ready)
|
||||
if (!IsInMainThread() && IS_GPU_NOT_READY())
|
||||
{
|
||||
int32 timeout = 1000;
|
||||
while (IS_GPU_NOT_READY() && timeout-- > 0)
|
||||
Platform::Sleep(1);
|
||||
if (IS_GPU_NOT_READY())
|
||||
return LoadResult::InvalidData;
|
||||
}
|
||||
#undef IS_GPU_NOT_READY
|
||||
if (WaitForInitGraphics())
|
||||
return LoadResult::CannotLoadData;
|
||||
|
||||
// If engine was compiled with shaders compiling service:
|
||||
// - Material should be changed in need to convert it to the newer version (via Visject Surface)
|
||||
|
||||
@@ -19,10 +19,10 @@ Variant MaterialBase::GetParameterValue(const StringView& name)
|
||||
if (!IsLoaded() && WaitForLoaded())
|
||||
return Variant::Null;
|
||||
const auto param = Params.Get(name);
|
||||
if (IsMaterialInstance() && param && !param->IsOverride() && ((MaterialInstance*)this)->GetBaseMaterial())
|
||||
return ((MaterialInstance*)this)->GetBaseMaterial()->GetParameterValue(name);
|
||||
if (param)
|
||||
{
|
||||
return param->GetValue();
|
||||
}
|
||||
LOG(Warning, "Missing material parameter '{0}' in material {1}", String(name), ToString());
|
||||
return Variant::Null;
|
||||
}
|
||||
|
||||
@@ -57,6 +57,8 @@ public:
|
||||
/// <summary>
|
||||
/// Gets the material parameter value.
|
||||
/// </summary>
|
||||
/// <remarks>For material instances that inherit a base material, returned value might come from base material if the current one doesn't override it.</remarks>
|
||||
/// <param name="name">The parameter name.</param>
|
||||
/// <returns>The parameter value.</returns>
|
||||
API_FUNCTION() Variant GetParameterValue(const StringView& name);
|
||||
|
||||
|
||||
@@ -46,6 +46,7 @@ namespace
|
||||
|
||||
ContentStorageService ContentStorageServiceInstance;
|
||||
|
||||
TimeSpan ContentStorageManager::UnusedStorageLifetime = TimeSpan::FromSeconds(0.5f);
|
||||
TimeSpan ContentStorageManager::UnusedDataChunksLifetime = TimeSpan::FromSeconds(10);
|
||||
|
||||
FlaxStorageReference ContentStorageManager::GetStorage(const StringView& path, bool loadIt)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user