Merge branch 'master' of https://github.com/FlaxEngine/FlaxEngine into convertactor

This commit is contained in:
Jean-Baptiste Perrier
2021-02-15 21:43:46 +01:00
308 changed files with 3892 additions and 1907 deletions

19
.github/workflows/build_linux.todo vendored Normal file
View File

@@ -0,0 +1,19 @@
name: Build Linux
on: [push, pull_request]
jobs:
# Game
game-linux:
name: Game (Linux, Release x64)
runs-on: "ubuntu-20.04"
steps:
- name: Checkout repo
uses: actions/checkout@v2
- name: Checkout LFS
run: |
git lfs version
git lfs pull
- name: Build
run: |
./Development/Scripts/Linux/CallBuildTool.sh -build -log -arch=x64 -platform=Linux -configuration=Release -buildtargets=FlaxGame

34
.github/workflows/build_windows.yml vendored Normal file
View File

@@ -0,0 +1,34 @@
name: Build Windows
on: [push, pull_request]
jobs:
# Editor
editor-windows:
name: Editor (Windows, Development x64)
runs-on: "windows-latest"
steps:
- name: Checkout repo
uses: actions/checkout@v2
- name: Checkout LFS
run: |
git lfs version
git lfs pull
- name: Build
run: |
.\Development\Scripts\Windows\CallBuildTool.bat -build -log -arch=x64 -platform=Windows -configuration=Development -buildtargets=FlaxEditor
# Game
game-windows:
name: Game (Windows, Release x64)
runs-on: "windows-latest"
steps:
- name: Checkout repo
uses: actions/checkout@v2
- name: Checkout LFS
run: |
git lfs version
git lfs pull
- name: Build
run: |
.\Development\Scripts\Windows\CallBuildTool.bat -build -log -arch=x64 -platform=Windows -configuration=Release -buildtargets=FlaxGame

34
CONTRIBUTING.md Normal file
View File

@@ -0,0 +1,34 @@
# How to contribute to the FlaxEngine
For any questions, suggestions or help join our discord!
<a href="https://flaxengine.com/discord"><img src="https://discordapp.com/api/guilds/437989205315158016/widget.png"/></a>
Want to see whats planned for Flax?
Go check out our [Trello](https://trello.com/b/NQjLXRCP/flax-roadmap).
## **Found a bug?**
* Avoid opening any new issues without having checked if your problem has already been reported. If there are no currently open issues that fit your problem's description, feel free to [add it](https://github.com/FlaxEngine/FlaxEngine/issues/new).
* When writing an issue make sure to include a clear title and description as well as having filled out all the necessary information, depending on the severity of the issue also include the necessary log files and minidump.
* Try to following the given template when writing a new issue if possible.
## **Want to contribute?**
* Fork the FlaxEngine, create a new branch and push your changes there. Then, create a pull request.
* When creating a PR for fixing an issue/bug make sure to describe as to what led to the fix for better understanding, for small and obvious fixes this is not really needed.
However make sure to mention the relevant issue where it was first reported if possible.
* For feature PR's the first thing you should evaluate is the value of your contribution, as in, what would it bring to this engine? Is it really required?
If its a small change you could preferably suggest it to us on our discord, else feel free to open up a PR for it.
* Ensure when creating a PR that your contribution is well explained with a adequate description and title.
* Generally, good code quality is expected, make sure your contribution works as intended and is appropriately commented where necessary.
Thank you for taking interest in contributing to Flax!

Binary file not shown.

Binary file not shown.

Binary file not shown.

6
Development/Scripts/Linux/CallBuildTool.sh Normal file → Executable file
View File

@@ -3,6 +3,12 @@
set -e
testfilesize=$(wc -c < 'Source/Logo.png')
if [ $testfilesize -le 1000 ]; then
echo "CallBuildTool ERROR: Repository was not cloned using Git LFS" 1>&2
exit 1
fi
# Compile the build tool.
xbuild /nologo /verbosity:quiet "Source/Tools/Flax.Build/Flax.Build.csproj" /property:Configuration=Release /property:Platform=AnyCPU /target:Build

View File

@@ -4,6 +4,10 @@ rem Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
if not exist "Development\Scripts\Windows\GetMSBuildPath.bat" goto Error_InvalidLocation
for %%I in (Source\Logo.png) do if %%~zI LSS 2000 (
goto Error_MissingLFS
)
call "Development\Scripts\Windows\GetMSBuildPath.bat"
if errorlevel 1 goto Error_NoVisualStudioEnvironment
@@ -33,6 +37,9 @@ Binaries\Tools\Flax.Build.exe %*
if errorlevel 1 goto Error_FlaxBuildFailed
exit /B 0
:Error_MissingLFS
echo CallBuildTool ERROR: Repository was not cloned using Git LFS
goto Exit
:Error_InvalidLocation
echo CallBuildTool ERROR: The script is in invalid directory.
goto Exit

View File

@@ -3,7 +3,7 @@
"Version": {
"Major": 1,
"Minor": 0,
"Build": 6215
"Build": 6216
},
"Company": "Flax",
"Copyright": "Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.",

View File

@@ -16,6 +16,7 @@ exit /B 0
:BuildToolFailed
echo Flax.Build tool failed.
pause
goto Exit
:Exit

0
GenerateProjectFiles.sh Normal file → Executable file
View File

0
PackagePlatforms.sh Normal file → Executable file
View File

View File

@@ -26,6 +26,10 @@ This repository contains full source code of the Flax (excluding NDA-protected p
Follow the instructions below to compile and run the engine from source.
## Flax plugin for Visual Studio
Flax Visual Studio extension provides better programming workflow, C# scripts debugging functionality and allows to attach to running engine instance to debug C# source. This extension is available to download [here](https://marketplace.visualstudio.com/items?itemName=Flax.FlaxVS).
## Windows
* Install Visual Studio 2015 or newer
@@ -34,7 +38,8 @@ Follow the instructions below to compile and run the engine from source.
* Clone repo (with LFS)
* Run **GenerateProjectFiles.bat**
* Open `Flax.sln` and set solution configuration to **Editor.Development** and solution platform to **Win64**
* Compile Flax project (hit F7 key)
* Set Flax or FlaxEngine as startup project
* Compile Flax project (hit F7 or CTRL+Shift+B)
* Run Flax (hit F5 key)
## Linux
@@ -49,10 +54,6 @@ Follow the instructions below to compile and run the engine from source.
* Open workspace with Visual Code
* Build and run
# Flax plugin for Visual Studio
Flax Visual Studio extension provides better programming workflow, C# scripts debugging functionality and allows to attach to running engine instance to debug C# source. This extension is available to download [here](https://marketplace.visualstudio.com/items?itemName=Flax.FlaxVS).
## Workspace directory
- **Binaries/** - executable files

View File

@@ -262,9 +262,9 @@ namespace FlaxEditor.Content.Import
public int BaseLOD { get; set; } = 0;
/// <summary>
/// The amount of LODs to include in the model (all reaming ones starting from Base LOD will be generated).
/// The amount of LODs to include in the model (all remaining ones starting from Base LOD will be generated).
/// </summary>
[EditorOrder(1120), DefaultValue(4), Limit(1, Model.MaxLODs), EditorDisplay("Level Of Detail", "LOD Count"), Tooltip("The amount of LODs to include in the model (all reaming ones starting from Base LOD will be generated).")]
[EditorOrder(1120), DefaultValue(4), Limit(1, Model.MaxLODs), EditorDisplay("Level Of Detail", "LOD Count"), Tooltip("The amount of LODs to include in the model (all remaining ones starting from Base LOD will be generated).")]
public int LODCount { get; set; } = 4;
/// <summary>

View File

@@ -162,8 +162,7 @@ namespace FlaxEditor.Content
// Auto fit actor to camera
float targetSize = 30.0f;
BoundingBox bounds;
Editor.GetActorEditorBox(_preview.Instance, out bounds);
Editor.GetActorEditorBox(_preview.Instance, out var bounds);
float maxSize = Mathf.Max(0.001f, bounds.Size.MaxValue);
_preview.Instance.Scale = new Vector3(targetSize / maxSize);
_preview.Instance.Position = Vector3.Zero;
@@ -175,6 +174,7 @@ namespace FlaxEditor.Content
/// <inheritdoc />
public override void OnThumbnailDrawEnd(ThumbnailRequest request, ContainerControl guiRoot)
{
_preview.RemoveChildren();
_preview.Prefab = null;
_preview.Parent = null;
}

View File

@@ -295,7 +295,8 @@ namespace FlaxEditor.Content
StartRenaming();
return true;
case KeyboardKeys.Delete:
Editor.Instance.Windows.ContentWin.Delete(Folder);
if (Folder.Exists)
Editor.Instance.Windows.ContentWin.Delete(Folder);
return true;
}
if (RootWindow.GetKey(KeyboardKeys.Control))
@@ -303,7 +304,8 @@ namespace FlaxEditor.Content
switch (key)
{
case KeyboardKeys.D:
Editor.Instance.Windows.ContentWin.Duplicate(Folder);
if (Folder.Exists)
Editor.Instance.Windows.ContentWin.Duplicate(Folder);
return true;
}
}

View File

@@ -141,7 +141,7 @@ bool AndroidPlatformTools::OnPostProcess(CookingData& data)
const auto c = packageName[i];
if (c != '_' && c != '.' && !StringUtils::IsAlnum(c))
{
LOG(Error, "Android Package Name \'{0}\' contains invalid chaarcter. Only letters, numbers, dots and underscore characters are allowed.", packageName);
LOG(Error, "Android Package Name \'{0}\' contains invalid character. Only letters, numbers, dots and underscore characters are allowed.", packageName);
return true;
}
}

View File

@@ -24,7 +24,7 @@ bool UWPPlatformTools::OnScriptsStepDone(CookingData& data)
const String assembliesPath = data.OutputPath;
if (FileSystem::CopyFile(assembliesPath / TEXT("Newtonsoft.Json.dll"), customBinPath))
{
data.Error(TEXT("Failed to copy deloy custom assembly."));
data.Error(TEXT("Failed to copy deploy custom assembly."));
return true;
}
FileSystem::DeleteFile(assembliesPath / TEXT("Newtonsoft.Json.pdb"));

View File

@@ -48,7 +48,7 @@ public:
/// <summary>
/// Gets the value indicating whenever platform requires AOT.
/// </summary>
/// <returns>True if platform uses AOT and needs C# assemblies to be be precompiled, otherwise false.</returns>
/// <returns>True if platform uses AOT and needs C# assemblies to be precompiled, otherwise false.</returns>
virtual bool UseAOT() const
{
return false;

View File

@@ -60,7 +60,7 @@ bool ValidateStep::Perform(CookingData& data)
AssetInfo info;
if (!Content::GetAssetInfo(GameSettings::FirstScene, info))
{
data.Error(TEXT("Missing first scene."));
data.Error(TEXT("Missing first scene. Set it in the game settings."));
return true;
}
}

View File

@@ -71,7 +71,7 @@ namespace FlaxEditor.CustomEditors
// Space before word starting with uppercase letter
if (char.IsUpper(c) && i > 0)
{
if (i + 2 < length && !char.IsUpper(name[i + 1]) && !char.IsUpper(name[i + 2]))
if (i + 1 < length && !char.IsUpper(name[i + 1]))
sb.Append(' ');
}
// Space instead of underscore

View File

@@ -76,7 +76,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
upperRightCell.AddChild(new Label
{
Height = labelsHeight,
Text = layerNames[layerIndex],
Text = layerNames[layerNames.Length - layerIndex - 1],
HorizontalAlignment = TextAlignment.Near,
});
bottomLeftCell.AddChild(new Label

View File

@@ -23,7 +23,7 @@ namespace FlaxEditor.CustomEditors.Editors
/// <inheritdoc />
protected override void OnValueChanged()
{
var value = (StaticFlags)element.EnumComboBox.EnumTypeValue;
var value = (StaticFlags)element.ComboBox.EnumTypeValue;
// If selected is single actor that has children, ask if apply flags to the sub objects as well
if (Values.IsSingleObject && (StaticFlags)Values[0] != value && ParentEditor.Values[0] is Actor actor && actor.HasChildren)

View File

@@ -40,7 +40,7 @@ namespace FlaxEditor.CustomEditors.Editors
{
var enumType = Values.Type.Type != typeof(object) || Values[0] == null ? TypeUtils.GetType(Values.Type) : Values[0].GetType();
element = layout.Enum(enumType, null, mode);
element.EnumComboBox.ValueChanged += OnValueChanged;
element.ComboBox.ValueChanged += OnValueChanged;
}
}
@@ -49,7 +49,7 @@ namespace FlaxEditor.CustomEditors.Editors
/// </summary>
protected virtual void OnValueChanged()
{
SetValue(element.EnumComboBox.EnumTypeValue);
SetValue(element.ComboBox.EnumTypeValue);
}
/// <inheritdoc />
@@ -63,7 +63,7 @@ namespace FlaxEditor.CustomEditors.Editors
}
else
{
element.EnumComboBox.EnumTypeValue = Values[0];
element.ComboBox.EnumTypeValue = Values[0];
}
}
}

View File

@@ -16,7 +16,7 @@ namespace FlaxEditor.CustomEditors.Elements
/// <summary>
/// The combo box used to show enum values.
/// </summary>
public EnumComboBox EnumComboBox;
public EnumComboBox ComboBox;
/// <summary>
/// Initializes a new instance of the <see cref="EnumElement"/> class.
@@ -26,10 +26,10 @@ namespace FlaxEditor.CustomEditors.Elements
/// <param name="formatMode">The formatting mode.</param>
public EnumElement(Type type, EnumComboBox.BuildEntriesDelegate customBuildEntriesDelegate = null, EnumDisplayAttribute.FormatMode formatMode = EnumDisplayAttribute.FormatMode.Default)
{
EnumComboBox = new EnumComboBox(type, customBuildEntriesDelegate, formatMode);
ComboBox = new EnumComboBox(type, customBuildEntriesDelegate, formatMode);
}
/// <inheritdoc />
public override Control Control => EnumComboBox;
public override Control Control => ComboBox;
}
}

View File

@@ -254,7 +254,7 @@ namespace FlaxEditor
if (loadingPreview != null)
{
// Link it to the prefab preview to see it in the editor
loadingPreview.customControlLinked = control.Control;
loadingPreview.customControlLinked = control;
return loadingPreview;
}
return null;

View File

@@ -43,7 +43,7 @@ public:
public:
/// <summary>
/// The flag used to determine if a project was used with the older engine version last time it was opened. Some cached data should be regenerated to prevent version difference issues. The version number comparision is based on major and minor part of the version. Build number is ignored.
/// The flag used to determine if a project was used with the older engine version last time it was opened. Some cached data should be regenerated to prevent version difference issues. The version number comparison is based on major and minor part of the version. Build number is ignored.
/// </summary>
static bool IsOldProjectOpened;

View File

@@ -355,12 +355,10 @@ namespace FlaxEditor.GUI
_mousePos = location;
// Check if start drag drop
if (_isMouseDown && Vector2.Distance(location, _mouseDownPos) > 10.0f)
if (_isMouseDown && Vector2.Distance(location, _mouseDownPos) > 10.0f && IconRect.Contains(_mouseDownPos))
{
// Clear flag
_isMouseDown = false;
// Do the drag
_isMouseDown = false;
DoDrag();
}
@@ -370,35 +368,35 @@ namespace FlaxEditor.GUI
/// <inheritdoc />
public override bool OnMouseUp(Vector2 location, MouseButton button)
{
if (button == MouseButton.Left)
if (button == MouseButton.Left && _isMouseDown)
{
_isMouseDown = false;
}
// Buttons logic
if (Button1Rect.Contains(location))
{
// Show asset picker popup
Focus();
AssetSearchPopup.Show(this, Button1Rect.BottomLeft, IsValid, assetItem =>
// Buttons logic
if (Button1Rect.Contains(location))
{
SelectedItem = assetItem;
RootWindow.Focus();
// Show asset picker popup
Focus();
});
}
else if (_selected != null || _selectedItem != null)
{
if (Button2Rect.Contains(location) && _selectedItem != null)
{
// Select asset
Editor.Instance.Windows.ContentWin.Select(_selectedItem);
AssetSearchPopup.Show(this, Button1Rect.BottomLeft, IsValid, assetItem =>
{
SelectedItem = assetItem;
RootWindow.Focus();
Focus();
});
}
else if (Button3Rect.Contains(location))
else if (_selected != null || _selectedItem != null)
{
// Deselect asset
Focus();
SelectedItem = null;
if (Button2Rect.Contains(location) && _selectedItem != null)
{
// Select asset
Editor.Instance.Windows.ContentWin.Select(_selectedItem);
}
else if (Button3Rect.Contains(location))
{
// Deselect asset
Focus();
SelectedItem = null;
}
}
}
@@ -409,8 +407,7 @@ namespace FlaxEditor.GUI
/// <inheritdoc />
public override bool OnMouseDown(Vector2 location, MouseButton button)
{
// Set flag for dragging asset
if (button == MouseButton.Left && IconRect.Contains(location))
if (button == MouseButton.Left)
{
_isMouseDown = true;
_mouseDownPos = location;

View File

@@ -221,7 +221,7 @@ namespace FlaxEditor.GUI
}
/// <summary>
/// Filters teh given value using the the <see cref="UseMode"/>.
/// Filters the given value using the <see cref="UseMode"/>.
/// </summary>
/// <param name="mode">The mode.</param>
/// <param name="value">The value to process.</param>

View File

@@ -186,7 +186,7 @@ namespace FlaxEditor.GUI.Dialogs
// Clean up
_window = null;
// Check if any thead is blocked during ShowDialog, then wait for it
// Check if any thread is blocked during ShowDialog, then wait for it
bool wait = true;
while (wait)
{

View File

@@ -307,6 +307,14 @@ namespace FlaxEditor.GUI.Docking
_dockedTo?.SelectTab(this, autoFocus);
}
/// <summary>
/// Brings the window to the front of the Z order.
/// </summary>
public void BringToFront()
{
_dockedTo?.RootWindow?.BringToFront();
}
internal void OnUnlinkInternal()
{
OnUnlink();
@@ -412,6 +420,7 @@ namespace FlaxEditor.GUI.Docking
base.Focus();
SelectTab();
BringToFront();
}
/// <inheritdoc />

View File

@@ -64,7 +64,7 @@ namespace FlaxEditor.GUI.Docking
for (int i = 0; i < childPanels.Length; i++)
childPanels[i].Dispose();
// Delete reaming controls (except tabs proxy)
// Delete remaining controls (except tabs proxy)
if (TabsProxy != null)
TabsProxy.Parent = null;
DisposeChildren();

View File

@@ -168,6 +168,7 @@ namespace FlaxEditor.GUI.Input
{
if (button == MouseButton.Left)
{
Focus();
float mousePosition = location.X;
if (_thumbRect.Contains(ref location))
@@ -208,7 +209,6 @@ namespace FlaxEditor.GUI.Input
{
if (button == MouseButton.Left && _isSliding)
{
// End sliding
EndSliding();
return true;
}

View File

@@ -143,6 +143,8 @@ namespace FlaxEditor.GUI.Input
try
{
var value = ShuntingYard.Parse(Text);
if (value < 0)
value = 0;
Value = (uint)value;
}
catch (Exception ex)

View File

@@ -49,6 +49,11 @@ namespace FlaxEditor.GUI.Input
/// </summary>
protected T _startSlideValue;
/// <summary>
/// The text cached on editing start. Used to compare with the end result to detect changes.
/// </summary>
protected string _startEditText;
private Vector2 _startSlideLocation;
/// <summary>
@@ -257,11 +262,23 @@ namespace FlaxEditor.GUI.Input
return base.OnMouseUp(location, button);
}
/// <inheritdoc />
protected override void OnEditBegin()
{
base.OnEditBegin();
_startEditText = _text;
}
/// <inheritdoc />
protected override void OnEditEnd()
{
// Update value
TryGetValue();
if (_startEditText != _text)
{
// Update value
TryGetValue();
}
_startEditText = null;
base.OnEditEnd();
}

View File

@@ -120,7 +120,7 @@ namespace FlaxEditor.GUI
/// <summary>
/// Draws the column.
/// </summary>
/// <param name="rect">The the header area rectangle.</param>
/// <param name="rect">The header area rectangle.</param>
/// <param name="columnIndex">The zero-based index of the column.</param>
protected virtual void DrawColumn(ref Rectangle rect, int columnIndex)
{

View File

@@ -401,7 +401,7 @@ namespace FlaxEditor.GUI
}
/// <summary>
/// Converts the input point from editor editor contents control space into the keyframes time/value coordinates.
/// Converts the input point from editor contents control space into the keyframes time/value coordinates.
/// </summary>
/// <param name="point">The point.</param>
/// <param name="keyframesContentAreaBounds">The keyframes contents area bounds.</param>

View File

@@ -16,6 +16,7 @@ namespace FlaxEditor.GUI.Timeline
/// The Timeline track that contains a header and custom timeline events/media.
/// </summary>
/// <seealso cref="FlaxEngine.GUI.ContainerControl" />
[HideInEditor]
public class Track : ContainerControl
{
/// <summary>

View File

@@ -179,7 +179,7 @@ namespace FlaxEditor.GUI.Timeline.Tracks
}
/// <summary>
/// Evaluates the member value value at the specified time.
/// Evaluates the member value at the specified time.
/// </summary>
/// <param name="time">The time to evaluate the member at.</param>
/// <returns>The member value at provided time.</returns>

View File

@@ -21,7 +21,7 @@ namespace FlaxEditor.GUI.Tree
/// <summary>
/// The default node offset on Y axis.
/// </summary>
public const float DefaultNodeOffsetY = 1;
public const float DefaultNodeOffsetY = 0;
private Tree _tree;
@@ -539,7 +539,7 @@ namespace FlaxEditor.GUI.Tree
{
if (new Rectangle(_headerRect.X, _headerRect.Y - DefaultDragInsertPositionMargin - DefaultNodeOffsetY, _headerRect.Width, DefaultDragInsertPositionMargin * 2.0f).Contains(location))
_dragOverMode = DragItemPositioning.Above;
else if (IsCollapsed && new Rectangle(_headerRect.X, _headerRect.Bottom - DefaultDragInsertPositionMargin, _headerRect.Width, DefaultDragInsertPositionMargin * 2.0f).Contains(location))
else if ((IsCollapsed || !HasAnyVisibleChild) && new Rectangle(_headerRect.X, _headerRect.Bottom - DefaultDragInsertPositionMargin, _headerRect.Width, DefaultDragInsertPositionMargin * 2.0f).Contains(location))
_dragOverMode = DragItemPositioning.Below;
else
_dragOverMode = DragItemPositioning.At;

View File

@@ -7,7 +7,7 @@ using FlaxEngine;
namespace FlaxEditor.Gizmo
{
/// <summary>
/// Represents collection of Gizmo tools where one is active and in use.
/// Represents collection of gizmo tools where one is active and in use.
/// </summary>
/// <seealso cref="GizmoBase" />
[HideInEditor]

View File

@@ -45,7 +45,7 @@ namespace FlaxEditor.Gizmo
)
{
// Error
Platform.Fatal("Failed to load Transform Gizmo resources.");
Platform.Fatal("Failed to load transform gizmo resources.");
}
}

View File

@@ -104,7 +104,7 @@ namespace FlaxEditor.Gizmo
public Axis ActiveAxis => _activeAxis;
/// <summary>
/// Gets or sts the current gizmo mode.
/// Gets or sets the current gizmo mode.
/// </summary>
public Mode ActiveMode
{

View File

@@ -151,7 +151,7 @@ namespace FlaxEditor.Gizmo
// Set positions of the gizmo
UpdateGizmoPosition();
// Scale Gizmo to fit on-screen
// Scale gizmo to fit on-screen
Vector3 vLength = Owner.ViewPosition - Position;
float gizmoSize = Editor.Instance.Options.Options.Visual.GizmoSize;
_screenScale = vLength.Length / GizmoScaleFactor * gizmoSize;
@@ -196,12 +196,10 @@ namespace FlaxEditor.Gizmo
private void UpdateTranslateScale()
{
bool isScaling = _activeMode == Mode.Scale;
Vector3 delta = Vector3.Zero;
Ray ray = Owner.MouseRay;
Matrix invRotationMatrix;
Matrix.Invert(ref _rotationMatrix, out invRotationMatrix);
Matrix.Invert(ref _rotationMatrix, out var invRotationMatrix);
ray.Position = Vector3.Transform(ray.Position, invRotationMatrix);
Vector3.TransformNormal(ref ray.Direction, ref invRotationMatrix, out ray.Direction);
@@ -211,9 +209,7 @@ namespace FlaxEditor.Gizmo
case Axis.X:
{
var plane = new Plane(Vector3.Backward, Vector3.Transform(Position, invRotationMatrix).Z);
float intersection;
if (ray.Intersects(ref plane, out intersection))
if (ray.Intersects(ref plane, out float intersection))
{
_intersectPosition = ray.Position + ray.Direction * intersection;
if (_lastIntersectionPosition != Vector3.Zero)
@@ -222,18 +218,14 @@ namespace FlaxEditor.Gizmo
? new Vector3(_tDelta.X, 0, 0)
: new Vector3(_tDelta.X, _tDelta.Y, 0);
}
break;
}
case Axis.Z:
case Axis.YZ:
case Axis.Y:
{
var plane = new Plane(Vector3.Left, Vector3.Transform(Position, invRotationMatrix).X);
float intersection;
if (ray.Intersects(ref plane, out intersection))
if (ray.Intersects(ref plane, out float intersection))
{
_intersectPosition = ray.Position + ray.Direction * intersection;
if (_lastIntersectionPosition != Vector3.Zero)
@@ -251,41 +243,31 @@ namespace FlaxEditor.Gizmo
break;
}
}
break;
}
case Axis.ZX:
{
var plane = new Plane(Vector3.Down, Vector3.Transform(Position, invRotationMatrix).Y);
float intersection;
if (ray.Intersects(ref plane, out intersection))
if (ray.Intersects(ref plane, out float intersection))
{
_intersectPosition = ray.Position + ray.Direction * intersection;
if (_lastIntersectionPosition != Vector3.Zero)
_tDelta = _intersectPosition - _lastIntersectionPosition;
delta = new Vector3(_tDelta.X, 0, _tDelta.Z);
}
break;
}
case Axis.Center:
{
Vector3 gizmoToView = Position - Owner.ViewPosition;
var gizmoToView = Position - Owner.ViewPosition;
var plane = new Plane(-Vector3.Normalize(gizmoToView), gizmoToView.Length);
float intersection;
if (ray.Intersects(ref plane, out intersection))
if (ray.Intersects(ref plane, out float intersection))
{
_intersectPosition = ray.Position + ray.Direction * intersection;
if (_lastIntersectionPosition != Vector3.Zero)
_tDelta = _intersectPosition - _lastIntersectionPosition;
}
delta = _tDelta;
break;
}
}
@@ -299,14 +281,11 @@ namespace FlaxEditor.Gizmo
if ((isScaling ? ScaleSnapEnabled : TranslationSnapEnable) || Owner.UseSnapping)
{
float snapValue = isScaling ? ScaleSnapValue : TranslationSnapValue;
_translationScaleSnapDelta += delta;
delta = new Vector3(
(int)(_translationScaleSnapDelta.X / snapValue) * snapValue,
(int)(_translationScaleSnapDelta.Y / snapValue) * snapValue,
(int)(_translationScaleSnapDelta.Z / snapValue) * snapValue);
_translationScaleSnapDelta -= delta;
}
@@ -318,7 +297,30 @@ namespace FlaxEditor.Gizmo
}
else if (_activeMode == Mode.Scale)
{
// Apply Scale
// Scale
if (_activeTransformSpace == TransformSpace.World && _activeAxis != Axis.Center)
{
var deltaLocal = delta;
Quaternion orientation = GetSelectedObject(0).Orientation;
delta = Vector3.Transform(delta, orientation);
// Fix axis sign of delta movement for rotated object in some cases (eg. rotated object by 90 deg on Y axis and scale in world space with Red/X axis)
switch (_activeAxis)
{
case Axis.X:
if (deltaLocal.X < 0)
delta *= -1;
break;
case Axis.Y:
if (deltaLocal.Y < 0)
delta *= -1;
break;
case Axis.Z:
if (deltaLocal.Z < 0)
delta *= -1;
break;
}
}
_scaleDelta = delta;
}
}
@@ -382,7 +384,7 @@ namespace FlaxEditor.Gizmo
// Snap to ground
if (_activeAxis == Axis.None && SelectionCount != 0 && Owner.SnapToGround)
{
if (Physics.RayCast(Position, Vector3.Down, out var hit, float.MaxValue, int.MaxValue, false))
if (Physics.RayCast(Position, Vector3.Down, out var hit, float.MaxValue, uint.MaxValue, false))
{
StartTransforming();
var translationDelta = hit.Point - Position;
@@ -408,7 +410,6 @@ namespace FlaxEditor.Gizmo
case Mode.Translate:
UpdateTranslateScale();
break;
case Mode.Rotate:
UpdateRotate(dt);
break;
@@ -437,7 +438,7 @@ namespace FlaxEditor.Gizmo
translationDelta = _translationDelta;
_translationDelta = Vector3.Zero;
// Prevent from moving objects too far away, like to different galaxy or sth
// Prevent from moving objects too far away, like to a different galaxy or sth
Vector3 prevMoveDelta = _accMoveDelta;
_accMoveDelta += _translationDelta;
if (_accMoveDelta.Length > Owner.ViewFarPlane * 0.7f)

View File

@@ -74,7 +74,7 @@ namespace FlaxEditor.History
_reverseActions.PushBack(reverse[i]);
}
// Cleanup reaming actions
// Cleanup remaining actions
for (int i = _historyActionsLimit; i < history.Length; i++)
{
history[i].Dispose();

View File

@@ -108,7 +108,7 @@ namespace FlaxEditor.Modules
/// <summary>
/// Removes a quick action by name.
/// </summary>
/// <param name="name">Thr action's name.</param>
/// <param name="name">The action's name.</param>
/// <returns>True when it succeed, false if there is no Quick Action with this name.</returns>
public bool RemoveQuickAction(string name)
{

View File

@@ -410,7 +410,7 @@ namespace FlaxEditor.Modules
{
if (request.Settings != null && entry.TryOverrideSettings(request.Settings))
{
// Use overriden settings
// Use overridden settings
}
else if (!request.SkipSettingsDialog)
{

View File

@@ -276,8 +276,8 @@ namespace FlaxEditor.Modules
// Get metadata
int version = int.Parse(root.Attributes["Version"].Value, CultureInfo.InvariantCulture);
var virtualDesktopBounds = Platform.VirtualDesktopBounds;
var virtualDesktopSafeLeftCorner = virtualDesktopBounds.Location + new Vector2(0, 23); // 23 is a window strip size
var virtualDesktopSafeRightCorner = virtualDesktopBounds.BottomRight - new Vector2(50, 50); // apply some safe area
var virtualDesktopSafeLeftCorner = virtualDesktopBounds.Location;
var virtualDesktopSafeRightCorner = virtualDesktopBounds.BottomRight;
switch (version)
{
@@ -971,7 +971,7 @@ namespace FlaxEditor.Modules
}
#region Window Events
private void OnEditorStateChanged()
{
for (int i = 0; i < Windows.Count; i++)

View File

@@ -143,7 +143,31 @@ namespace FlaxEditor.Options
{
var root = control.Root;
if (root.GetKeyDown(Key))
if (root.GetKey(Key))
{
if (Modifier1 == KeyboardKeys.None || root.GetKey(Modifier1))
{
if (Modifier2 == KeyboardKeys.None || root.GetKey(Modifier2))
{
return true;
}
}
}
return false;
}
/// <summary>
/// Processes this input binding to check if state matches.
/// </summary>
/// <param name="control">The input providing control.</param>
/// <param name="key">The input key.</param>
/// <returns>True if input has been processed, otherwise false.</returns>
public bool Process(Control control, KeyboardKeys key)
{
var root = control.Root;
if (key == Key)
{
if (Modifier1 == KeyboardKeys.None || root.GetKey(Modifier1))
{
@@ -435,16 +459,10 @@ namespace FlaxEditor.Options
for (int i = 0; i < _bindings.Count; i++)
{
var binding = _bindings[i].Binder(options);
if (binding.Key == key)
if (binding.Process(control, key))
{
if (binding.Modifier1 == KeyboardKeys.None || root.GetKey(binding.Modifier1))
{
if (binding.Modifier2 == KeyboardKeys.None || root.GetKey(binding.Modifier2))
{
_bindings[i].Callback();
return true;
}
}
_bindings[i].Callback();
return true;
}
}

View File

@@ -41,7 +41,7 @@ namespace FlaxEditor.Options
private readonly Dictionary<string, CreateCustomSettingsDelegate> _customSettings = new Dictionary<string, CreateCustomSettingsDelegate>();
/// <summary>
/// Gets the custom settings factories. Each entry defines the custom settings type identified by teh given key name. The value si a factory function that returns the default options fpr a given type.
/// Gets the custom settings factories. Each entry defines the custom settings type identified by the given key name. The value is a factory function that returns the default options for a given type.
/// </summary>
public IReadOnlyDictionary<string, CreateCustomSettingsDelegate> CustomSettings => _customSettings;

View File

@@ -45,5 +45,12 @@ namespace FlaxEditor.Options
[DefaultValue(60.0f), Limit(35.0f, 160.0f, 0.1f)]
[EditorDisplay("Defaults", "Default Field Of View"), EditorOrder(140), Tooltip("The default field of view angle (in degrees) for the viewport camera.")]
public float DefaultFieldOfView { get; set; } = 60.0f;
/// <summary>
/// Gets or sets if the panning direction is inverted for the viewport camera.
/// </summary>
[DefaultValue(false)]
[EditorDisplay("Defaults"), EditorOrder(150), Tooltip( "Invert the panning direction for the viewport camera." )]
public bool DefaultInvertPanning { get; set; } = false;
}
}

View File

@@ -79,7 +79,7 @@ namespace FlaxEditor.Progress
}
/// <summary>
/// Called when progress action gets updated (changed nfo text or progress value).
/// Called when progress action gets updated (changed info text or progress value).
/// </summary>
/// <param name="progress">The progress (normalized to range [0;1]).</param>
/// <param name="infoText">The information text.</param>

View File

@@ -204,7 +204,7 @@ bool ProjectInfo::LoadProject(const String& projectPath)
reference.Project = Load(referencePath);
if (reference.Project == nullptr)
{
LOG(Error, "Faield to load referenced project ({0}, from {1})", reference.Name, referencePath);
LOG(Error, "Failed to load referenced project ({0}, from {1})", reference.Name, referencePath);
return true;
}
}
@@ -277,7 +277,7 @@ bool ProjectInfo::LoadOldProject(const String& projectPath)
flaxReference.Project = Load(Globals::StartupFolder / TEXT("Flax.flaxproj"));
if (!flaxReference.Project)
{
ShowProjectLoadError(TEXT("Failed to load Flax Engien project."), projectPath);
ShowProjectLoadError(TEXT("Failed to load Flax Engine project."), projectPath);
return true;
}

View File

@@ -49,7 +49,7 @@ public:
{
if (dwRejectType == SERVERCALL_RETRYLATER)
{
// Retry immediatey
// Retry immediately
return 99;
}

View File

@@ -567,7 +567,7 @@ bool ScriptsBuilderService::Init()
LOG(Warning, "Missing EditorTarget property in opened project, using deducted target name {0}", Editor::Project->EditorTarget);
}
// Remove any reaming files from previous Editor run hot-reloads
// Remove any remaining files from previous Editor run hot-reloads
const Char *target, *platform, *architecture, *configuration;
ScriptsBuilder::GetBinariesConfiguration(target, platform, architecture, configuration);
if (target)

View File

@@ -65,8 +65,8 @@ namespace FlaxEditor.Surface
NodeElementArchetype.Factory.Output(0, "Length", typeof(float), 0),
NodeElementArchetype.Factory.Output(1, "Time", typeof(float), 1),
NodeElementArchetype.Factory.Output(2, "Normalized Time", typeof(float), 2),
NodeElementArchetype.Factory.Output(3, "Reaming Time", typeof(float), 3),
NodeElementArchetype.Factory.Output(4, "Reaming Normalized Time", typeof(float), 4),
NodeElementArchetype.Factory.Output(3, "Remaining Time", typeof(float), 3),
NodeElementArchetype.Factory.Output(4, "Remaining Normalized Time", typeof(float), 4),
}
},
}

View File

@@ -1046,7 +1046,7 @@ namespace FlaxEditor.Surface.Archetypes
startPos += nrm;
endPos += nrm;
// Swap fo the other arrow
// Swap to the other arrow
if (!diff)
{
var tmp = startPos;

View File

@@ -764,8 +764,8 @@ namespace FlaxEditor.Surface.Archetypes
NodeElementArchetype.Factory.Output(0, "Length", typeof(float), 0),
NodeElementArchetype.Factory.Output(1, "Time", typeof(float), 1),
NodeElementArchetype.Factory.Output(2, "Normalized Time", typeof(float), 2),
NodeElementArchetype.Factory.Output(3, "Reaming Time", typeof(float), 3),
NodeElementArchetype.Factory.Output(4, "Reaming Normalized Time", typeof(float), 4),
NodeElementArchetype.Factory.Output(3, "Remaining Time", typeof(float), 3),
NodeElementArchetype.Factory.Output(4, "Remaining Normalized Time", typeof(float), 4),
}
},
new NodeArchetype
@@ -791,7 +791,7 @@ namespace FlaxEditor.Surface.Archetypes
Title = "Transform Node (local space)",
Description = "Transforms the skeleton node",
Flags = NodeFlags.AnimGraph,
Size = new Vector2(270, 130),
Size = new Vector2(280, 130),
DefaultValues = new object[]
{
string.Empty,
@@ -816,7 +816,7 @@ namespace FlaxEditor.Surface.Archetypes
Title = "Transform Node (model space)",
Description = "Transforms the skeleton node",
Flags = NodeFlags.AnimGraph,
Size = new Vector2(270, 130),
Size = new Vector2(280, 130),
DefaultValues = new object[]
{
string.Empty,
@@ -872,7 +872,7 @@ namespace FlaxEditor.Surface.Archetypes
Title = "Get Node Transform (model space)",
Description = "Samples the skeleton node transformation (in model space)",
Flags = NodeFlags.AnimGraph,
Size = new Vector2(250, 40),
Size = new Vector2(324, 40),
DefaultValues = new object[]
{
string.Empty,
@@ -880,7 +880,7 @@ namespace FlaxEditor.Surface.Archetypes
Elements = new[]
{
NodeElementArchetype.Factory.Input(0, string.Empty, true, typeof(void), 0),
NodeElementArchetype.Factory.SkeletonNodeNameSelect(40, Surface.Constants.LayoutOffsetY * 1, 120, 0),
NodeElementArchetype.Factory.SkeletonNodeNameSelect(40, Surface.Constants.LayoutOffsetY * 1, 160, 0),
NodeElementArchetype.Factory.Text(0, Surface.Constants.LayoutOffsetY * 1, "Node:"),
NodeElementArchetype.Factory.Output(0, "Transform", typeof(Transform), 1),
}
@@ -903,7 +903,7 @@ namespace FlaxEditor.Surface.Archetypes
NodeElementArchetype.Factory.Input(0, string.Empty, true, typeof(void), 1),
NodeElementArchetype.Factory.Input(1, "Target", true, typeof(Vector3), 2),
NodeElementArchetype.Factory.Input(2, "Weight", true, typeof(float), 3, 1),
NodeElementArchetype.Factory.SkeletonNodeNameSelect(40, Surface.Constants.LayoutOffsetY * 3, 120, 0),
NodeElementArchetype.Factory.SkeletonNodeNameSelect(40, Surface.Constants.LayoutOffsetY * 3, 160, 0),
NodeElementArchetype.Factory.Text(0, Surface.Constants.LayoutOffsetY * 3, "Node:"),
}
},
@@ -913,7 +913,7 @@ namespace FlaxEditor.Surface.Archetypes
Title = "Get Node Transform (local space)",
Description = "Samples the skeleton node transformation (in local space)",
Flags = NodeFlags.AnimGraph,
Size = new Vector2(250, 40),
Size = new Vector2(316, 40),
DefaultValues = new object[]
{
string.Empty,

View File

@@ -379,7 +379,7 @@ namespace FlaxEditor.Surface.Archetypes
Elements = new[]
{
NodeElementArchetype.Factory.Output(0, "Value", typeof(uint), 0),
NodeElementArchetype.Factory.Integer(0, 0, 0)
NodeElementArchetype.Factory.UnsignedInteger(0, 0, 0, -1, 0, int.MaxValue)
}
},
};

View File

@@ -867,8 +867,15 @@ namespace FlaxEditor.Surface.Archetypes
for (int i = 0; i < signature.Params.Length; i++)
{
ref var param = ref signature.Params[i];
if (param.Type != memberParameters[i].Type || param.IsOut != memberParameters[i].IsOut)
ref var paramMember = ref memberParameters[i];
if (param.Type != paramMember.Type || param.IsOut != paramMember.IsOut)
{
// Special case: param.Type is serialized as just a type while paramMember.Type might be a reference for output parameters (eg. `out Int32` vs `out Int32&`)
var paramMemberTypeName = paramMember.Type.TypeName;
if (param.IsOut && param.IsOut == paramMember.IsOut && paramMember.Type.IsReference && !param.Type.IsReference &&
paramMemberTypeName.Substring(0, paramMemberTypeName.Length - 1) == param.Type.TypeName)
continue;
isInvalid = true;
break;
}
@@ -1176,7 +1183,7 @@ namespace FlaxEditor.Surface.Archetypes
[EditorOrder(0), Tooltip("The name of the parameter."), ExpandGroups]
public string Name;
[EditorOrder(1), Tooltip("The type fo the parameter value.")]
[EditorOrder(1), Tooltip("The type for the parameter value.")]
[TypeReference(typeof(object), nameof(IsTypeValid))]
public ScriptType Type;
@@ -1547,7 +1554,7 @@ namespace FlaxEditor.Surface.Archetypes
// Check if return type has been changed
if (_signature.ReturnType != prevReturnType)
{
// Update all return nodes used by this function to match teh new type
// Update all return nodes used by this function to match the new type
var usedNodes = DepthFirstTraversal(false);
var hasAnyReturnNode = false;
foreach (var node in usedNodes)

View File

@@ -642,6 +642,179 @@ namespace FlaxEditor.Surface.Archetypes
NodeElementArchetype.Factory.Output(0, "XYZ", typeof(Vector3), 0),
}
},
new NodeArchetype
{
TypeID = 26,
Title = "Blend Normals",
Description = "Blend two normal maps to create a single normal map",
Flags = NodeFlags.MaterialGraph,
Size = new Vector2(170, 40),
ConnectionsHints = ConnectionsHint.Vector,
Elements = new[]
{
NodeElementArchetype.Factory.Input(0, "Base Normal", true, typeof(Vector3), 0),
NodeElementArchetype.Factory.Input(1, "Additional Normal", true, typeof(Vector3), 1),
NodeElementArchetype.Factory.Output(0, "Result", typeof(Vector3), 2)
}
},
new NodeArchetype
{
TypeID = 27,
Title = "Rotator",
Description = "Rotates UV coordinates according to a scalar angle (0-1)",
Flags = NodeFlags.MaterialGraph,
Size = new Vector2(150, 55),
Elements = new[]
{
NodeElementArchetype.Factory.Input(0, "UV", true, typeof(Vector2), 0),
NodeElementArchetype.Factory.Input(1, "Center", true, typeof(Vector2), 1),
NodeElementArchetype.Factory.Input(2, "Rotation Angle", true, typeof(float), 2),
NodeElementArchetype.Factory.Output(0, string.Empty, typeof(Vector2), 3),
}
},
new NodeArchetype
{
TypeID = 28,
Title = "Sphere Mask",
Description = "Creates a sphere mask",
Flags = NodeFlags.MaterialGraph,
Size = new Vector2(150, 100),
ConnectionsHints = ConnectionsHint.Vector,
IndependentBoxes = new[]
{
0,
1
},
DefaultValues = new object[]
{
0.3f,
0.5f,
false
},
Elements = new[]
{
NodeElementArchetype.Factory.Input(0, "A", true, null, 0),
NodeElementArchetype.Factory.Input(1, "B", true, null, 1),
NodeElementArchetype.Factory.Input(2, "Radius", true, typeof(float), 2, 0),
NodeElementArchetype.Factory.Input(3, "Hardness", true, typeof(float), 3, 1),
NodeElementArchetype.Factory.Input(4, "Invert", true, typeof(bool), 4, 2),
NodeElementArchetype.Factory.Output(0, string.Empty, typeof(float), 5),
}
},
new NodeArchetype
{
TypeID = 29,
Title = "UV Tiling & Offset",
Description = "Takes UVs and applies tiling and offset",
Flags = NodeFlags.MaterialGraph,
Size = new Vector2(175, 60),
DefaultValues = new object[]
{
Vector2.One,
Vector2.Zero
},
Elements = new[]
{
NodeElementArchetype.Factory.Input(0, "UV", true, typeof(Vector2), 0),
NodeElementArchetype.Factory.Input(1, "Tiling", true, typeof(Vector2), 1, 0),
NodeElementArchetype.Factory.Input(2, "Offset", true, typeof(Vector2), 2, 1),
NodeElementArchetype.Factory.Output(0, string.Empty, typeof(Vector2), 3),
}
},
new NodeArchetype
{
TypeID = 30,
Title = "DDX",
Description = "Returns the partial derivative of the specified value with respect to the screen-space x-coordinate",
Flags = NodeFlags.MaterialGraph,
Size = new Vector2(90, 25),
ConnectionsHints = ConnectionsHint.Numeric,
IndependentBoxes = new[] { 0 },
DependentBoxes = new[] { 1 },
Elements = new[]
{
NodeElementArchetype.Factory.Input(0, "Value", true, null, 0),
NodeElementArchetype.Factory.Output(0, string.Empty, null, 1),
}
},
new NodeArchetype
{
TypeID = 31,
Title = "DDY",
Description = "Returns the partial derivative of the specified value with respect to the screen-space y-coordinate",
Flags = NodeFlags.MaterialGraph,
Size = new Vector2(90, 25),
ConnectionsHints = ConnectionsHint.Numeric,
IndependentBoxes = new[] { 0 },
DependentBoxes = new[] { 1 },
Elements = new[]
{
NodeElementArchetype.Factory.Input(0, "Value", true, null, 0),
NodeElementArchetype.Factory.Output(0, string.Empty, null, 1),
}
},
new NodeArchetype
{
TypeID = 32,
Title = "Sign",
Description = "Returns -1 if value is less than zero; 0 if value equals zero; and 1 if value is greater than zero",
Flags = NodeFlags.MaterialGraph,
Size = new Vector2(90, 25),
ConnectionsHints = ConnectionsHint.Numeric,
IndependentBoxes = new[] { 0 },
Elements = new[]
{
NodeElementArchetype.Factory.Input(0, "Value", true, null, 0),
NodeElementArchetype.Factory.Output(0, string.Empty, typeof(float), 1),
}
},
new NodeArchetype
{
TypeID = 33,
Title = "Any",
Description = "True if any components of value are non-zero; otherwise, false",
Flags = NodeFlags.MaterialGraph,
Size = new Vector2(90, 25),
ConnectionsHints = ConnectionsHint.Numeric,
IndependentBoxes = new[] { 0 },
Elements = new[]
{
NodeElementArchetype.Factory.Input(0, "Value", true, null, 0),
NodeElementArchetype.Factory.Output(0, string.Empty, typeof(bool), 1),
}
},
new NodeArchetype
{
TypeID = 34,
Title = "All",
Description = "Determines if all components of the specified value are non-zero",
Flags = NodeFlags.MaterialGraph,
Size = new Vector2(90, 25),
ConnectionsHints = ConnectionsHint.Numeric,
IndependentBoxes = new[] { 0 },
Elements = new[]
{
NodeElementArchetype.Factory.Input(0, "Value", true, null, 0),
NodeElementArchetype.Factory.Output(0, string.Empty, typeof(bool), 1),
}
},
new NodeArchetype
{
TypeID = 35,
Title = "Black Body",
Description = "Simulates black body radiation via a given temperature in kelvin",
Flags = NodeFlags.MaterialGraph,
Size = new Vector2(120, 25),
DefaultValues = new object[]
{
0.0f,
},
Elements = new[]
{
NodeElementArchetype.Factory.Input(0, "Temp", true, typeof(float), 0, 0),
NodeElementArchetype.Factory.Output(0, string.Empty, typeof(Vector3), 1),
}
},
};
}
}

View File

@@ -404,6 +404,29 @@ namespace FlaxEditor.Surface.Archetypes
NodeElementArchetype.Factory.Output(0, "A | B", null, 2),
}
},
new NodeArchetype
{
TypeID = 48,
Title = "Remap",
Description = "Remaps a value from one range to another, so for example having 25 in a range of 0 to 100 being remapped to 0 to 1 would return 0.25",
Flags = NodeFlags.AllGraphs,
Size = new Vector2(175, 75),
DefaultValues = new object[]
{
25.0f,
new Vector2(0.0f, 100.0f),
new Vector2(0.0f, 1.0f),
false
},
Elements = new[]
{
NodeElementArchetype.Factory.Input(0, "Value", true, typeof(float), 0, 0),
NodeElementArchetype.Factory.Input(1, "In Range", true, typeof(Vector2), 1, 1),
NodeElementArchetype.Factory.Input(2, "Out Range", true, typeof(Vector2), 2, 2),
NodeElementArchetype.Factory.Input(3, "Clamp", true, typeof(bool), 3, 3),
NodeElementArchetype.Factory.Output(0, string.Empty, typeof(float), 4),
}
},
};
}
}

View File

@@ -486,7 +486,7 @@ namespace FlaxEditor.Surface.Archetypes
Description = "Unpack X component from Vector",
Flags = NodeFlags.AllGraphs,
ConnectionsHints = ConnectionsHint.Vector,
Size = new Vector2(160, 30),
Size = new Vector2(110, 30),
Elements = new[]
{
NodeElementArchetype.Factory.Input(0, "Value", true, null, 0),
@@ -500,7 +500,7 @@ namespace FlaxEditor.Surface.Archetypes
Description = "Unpack Y component from Vector",
Flags = NodeFlags.AllGraphs,
ConnectionsHints = ConnectionsHint.Vector,
Size = new Vector2(160, 30),
Size = new Vector2(110, 30),
Elements = new[]
{
NodeElementArchetype.Factory.Input(0, "Value", true, null, 0),
@@ -514,7 +514,7 @@ namespace FlaxEditor.Surface.Archetypes
Description = "Unpack Z component from Vector",
Flags = NodeFlags.AllGraphs,
ConnectionsHints = ConnectionsHint.Vector,
Size = new Vector2(160, 30),
Size = new Vector2(110, 30),
Elements = new[]
{
NodeElementArchetype.Factory.Input(0, "Value", true, null, 0),
@@ -528,7 +528,7 @@ namespace FlaxEditor.Surface.Archetypes
Description = "Unpack W component from Vector",
Flags = NodeFlags.AllGraphs,
ConnectionsHints = ConnectionsHint.Vector,
Size = new Vector2(160, 30),
Size = new Vector2(110, 30),
Elements = new[]
{
NodeElementArchetype.Factory.Input(0, "Value", true, null, 0),
@@ -544,7 +544,7 @@ namespace FlaxEditor.Surface.Archetypes
Description = "Unpack XY components from Vector",
Flags = NodeFlags.AllGraphs,
ConnectionsHints = ConnectionsHint.Vector,
Size = new Vector2(160, 30),
Size = new Vector2(110, 30),
Elements = new[]
{
NodeElementArchetype.Factory.Input(0, "Value", true, null, 0),
@@ -558,7 +558,7 @@ namespace FlaxEditor.Surface.Archetypes
Description = "Unpack XZ components from Vector",
Flags = NodeFlags.AllGraphs,
ConnectionsHints = ConnectionsHint.Vector,
Size = new Vector2(160, 30),
Size = new Vector2(110, 30),
Elements = new[]
{
NodeElementArchetype.Factory.Input(0, "Value", true, null, 0),
@@ -572,7 +572,7 @@ namespace FlaxEditor.Surface.Archetypes
Description = "Unpack YZ components from Vector",
Flags = NodeFlags.AllGraphs,
ConnectionsHints = ConnectionsHint.Vector,
Size = new Vector2(160, 30),
Size = new Vector2(110, 30),
Elements = new[]
{
NodeElementArchetype.Factory.Input(0, "Value", true, null, 0),
@@ -588,7 +588,7 @@ namespace FlaxEditor.Surface.Archetypes
Description = "Unpack XYZ components from Vector",
Flags = NodeFlags.AllGraphs,
ConnectionsHints = ConnectionsHint.Vector,
Size = new Vector2(160, 30),
Size = new Vector2(110, 30),
Elements = new[]
{
NodeElementArchetype.Factory.Input(0, "Value", true, null, 0),

View File

@@ -32,7 +32,7 @@ namespace FlaxEditor.Surface.Archetypes
public Dictionary<Type, NodeElementArchetype[]> Prototypes = DefaultPrototypes;
/// <summary>
/// The default prototypes for thr node elements to use for the given parameter type.
/// The default prototypes for the node elements to use for the given parameter type.
/// </summary>
public static readonly Dictionary<Type, NodeElementArchetype[]> DefaultPrototypes = new Dictionary<Type, NodeElementArchetype[]>
{

View File

@@ -338,7 +338,7 @@ namespace FlaxEditor.Surface.Archetypes
Size = new Vector2(300, 600),
DefaultValues = new object[]
{
500, // Capacity
1000, // Capacity
(int)ParticlesSimulationMode.Default, // Simulation Mode
(int)ParticlesSimulationSpace.Local, // Simulation Space
true, // Enable Pooling

View File

@@ -97,7 +97,7 @@ namespace FlaxEditor.Surface.Elements
var connections = Connections.ToArray();
for (int i = 0; i < connections.Length; i++)
{
var targetBox = Connections[i];
var targetBox = connections[i];
// Break connection
Connections.Remove(targetBox);
@@ -565,7 +565,28 @@ namespace FlaxEditor.Surface.Elements
{
_isMouseDown = false;
if (Surface.CanEdit)
Surface.ConnectingStart(this);
{
if (!IsOutput && HasSingleConnection)
{
var connectedBox = Connections[0];
if (Surface.Undo != null)
{
var action = new ConnectBoxesAction((InputBox)this, (OutputBox)connectedBox, false);
BreakConnection(connectedBox);
action.End();
Surface.Undo.AddAction(action);
}
else
{
BreakConnection(connectedBox);
}
Surface.ConnectingStart(connectedBox);
}
else
{
Surface.ConnectingStart(this);
}
}
}
base.OnMouseLeave();
}

View File

@@ -32,8 +32,8 @@ namespace FlaxEditor.Surface.Elements
}
set
{
if (!string.IsNullOrEmpty(value))
SelectedIndex = _nodeNameToIndex[value];
if (!string.IsNullOrEmpty(value) && _nodeNameToIndex.TryGetValue(value, out var index))
SelectedIndex = index;
else
SelectedIndex = -1;
}
@@ -60,8 +60,8 @@ namespace FlaxEditor.Surface.Elements
{
_selectedIndices.Clear();
var selectedNode = (string)ParentNode.Values[Archetype.ValueIndex];
if (!string.IsNullOrEmpty(selectedNode))
_selectedIndices.Add(_nodeNameToIndex[selectedNode]);
if (!string.IsNullOrEmpty(selectedNode) && _nodeNameToIndex.TryGetValue(selectedNode, out var index))
_selectedIndices.Add(index);
OnSelectedIndexChanged();
}
@@ -92,7 +92,7 @@ namespace FlaxEditor.Surface.Elements
{
sb.Clear();
var node = nodes[nodeIndex];
_nodeNameToIndex.Add(node.Name, nodeIndex);
_nodeNameToIndex[node.Name] = nodeIndex;
int parent = node.ParentIndex;
while (parent != -1)
{

View File

@@ -85,7 +85,7 @@ namespace FlaxEditor.Surface.Elements
else if (value is Vector4 valueVec4)
result = (uint)(arch.BoxID == 0 ? valueVec4.X : arch.BoxID == 1 ? valueVec4.Y : arch.BoxID == 2 ? valueVec4.Z : valueVec4.W);
else
result = 0;
result = 0u;
return result;
}

View File

@@ -239,6 +239,32 @@ namespace FlaxEditor.Surface
};
}
/// <summary>
/// Creates new Unsigned Integer value element description.
/// </summary>
/// <param name="x">The x location (in node area space).</param>
/// <param name="y">The y location (in node area space).</param>
/// <param name="valueIndex">The index of the node variable linked as the input. Useful to make a physical connection between input box and default value for it.</param>
/// <param name="component">The index of the component to edit. For vectors this can be set to modify only single component of it. Eg. for vec2 value component set to 1 will edit only Y component. Default value -1 will be used to edit whole value.</param>
/// <param name="valueMin">The minimum value range.</param>
/// <param name="valueMax">The maximum value range.</param>
/// <returns>The archetype.</returns>
public static NodeElementArchetype UnsignedInteger(float x, float y, int valueIndex = -1, int component = -1, uint valueMin = 0, uint valueMax = 1000000)
{
return new NodeElementArchetype
{
Type = NodeElementType.UnsignedIntegerValue,
Position = new Vector2(Constants.NodeMarginX + x, Constants.NodeMarginY + Constants.NodeHeaderSize + y),
Text = null,
Single = false,
ValueIndex = valueIndex,
ValueMin = valueMin,
ValueMax = valueMax,
BoxID = -1,
ConnectionsType = ScriptType.Null
};
}
/// <summary>
/// Creates new Float value element description.
/// </summary>

View File

@@ -94,5 +94,10 @@ namespace FlaxEditor.Surface
/// The actor picker.
/// </summary>
Actor = 19,
/// <summary>
/// The unsigned integer value.
/// </summary>
UnsignedIntegerValue = 20,
}
}

View File

@@ -185,9 +185,9 @@ namespace FlaxEditor.Surface
var rightWidth = 40.0f;
var boxLabelFont = Style.Current.FontSmall;
var titleLabelFont = Style.Current.FontLarge;
for (int i = 0; i < Elements.Count; i++)
for (int i = 0; i < Children.Count; i++)
{
if (Elements[i] is InputBox inputBox)
if (Children[i] is InputBox inputBox)
{
var boxWidth = boxLabelFont.MeasureText(inputBox.Text).X + 20;
if (inputBox.DefaultValueEditor != null)
@@ -195,15 +195,23 @@ namespace FlaxEditor.Surface
leftWidth = Mathf.Max(leftWidth, boxWidth);
leftHeight = Mathf.Max(leftHeight, inputBox.Archetype.Position.Y - Constants.NodeMarginY - Constants.NodeHeaderSize + 20.0f);
}
else if (Elements[i] is OutputBox outputBox)
else if (Children[i] is OutputBox outputBox)
{
rightWidth = Mathf.Max(rightWidth, boxLabelFont.MeasureText(outputBox.Text).X + 20);
rightHeight = Mathf.Max(rightHeight, outputBox.Archetype.Position.Y - Constants.NodeMarginY - Constants.NodeHeaderSize + 20.0f);
}
else if (Elements[i] is Control control)
else if (Children[i] is Control control)
{
width = Mathf.Max(width, control.Width + 10);
height = Mathf.Max(height, control.Height + 10);
if (control.AnchorPreset == AnchorPresets.TopLeft)
{
width = Mathf.Max(width, control.Right + 4 - Constants.NodeMarginX);
height = Mathf.Max(height, control.Bottom + 4 - Constants.NodeMarginY - Constants.NodeHeaderSize);
}
else
{
width = Mathf.Max(width, control.Width + 4);
height = Mathf.Max(height, control.Height + 4);
}
}
}
width = Mathf.Max(width, leftWidth + rightWidth + 10);
@@ -267,6 +275,9 @@ namespace FlaxEditor.Surface
case NodeElementType.Actor:
element = new ActorSelect(this, arch);
break;
case NodeElementType.UnsignedIntegerValue:
element = new UnsignedIntegerValue(this, arch);
break;
//default: throw new NotImplementedException("Unknown node element type: " + arch.Type);
}
if (element != null)
@@ -303,12 +314,16 @@ namespace FlaxEditor.Surface
{
if (type == ScriptType.Null)
type = new ScriptType(typeof(object));
// Try to reuse box
var box = GetBox(id);
if ((isOut && box is InputBox) || (!isOut && box is OutputBox))
{
box.Dispose();
box = null;
}
// Create new if missing
if (box == null)
{
if (isOut)
@@ -319,10 +334,15 @@ namespace FlaxEditor.Surface
}
else
{
// Sync properties for exiting box
box.Text = text;
box.CurrentType = type;
box.Y = Constants.NodeMarginY + Constants.NodeHeaderSize + yLevel * Constants.LayoutOffsetY;
}
// Update box
box.OnConnectionsChanged();
return box;
}

View File

@@ -15,6 +15,7 @@ namespace FlaxEditor.Surface
{
private ContextMenuButton _cmCopyButton;
private ContextMenuButton _cmDuplicateButton;
private ContextMenuButton _cmFormatNodesConnectionButton;
private ContextMenuButton _cmRemoveNodeConnectionsButton;
private ContextMenuButton _cmRemoveBoxConnectionsButton;
private readonly Vector2 ContextMenuOffset = new Vector2(5);
@@ -216,6 +217,13 @@ namespace FlaxEditor.Surface
}
}).Enabled = Nodes.Any(x => x.Breakpoint.Set && x.Breakpoint.Enabled);
}
menu.AddSeparator();
_cmFormatNodesConnectionButton = menu.AddButton("Format node(s)", () =>
{
FormatGraph(SelectedNodes);
});
_cmFormatNodesConnectionButton.Enabled = HasNodesSelection;
menu.AddSeparator();
_cmRemoveNodeConnectionsButton = menu.AddButton("Remove all connections to that node(s)", () =>

View File

@@ -107,7 +107,7 @@ namespace FlaxEditor.Surface
/// Validates the parameter drag operation.
/// </summary>
/// <param name="parameterName">Name of the parameter.</param>
/// <returns>Tre if can drag that parameter, otherwise false.</returns>
/// <returns>True if can drag that parameter, otherwise false.</returns>
protected virtual bool ValidateDragParameter(string parameterName)
{
return GetParameter(parameterName) != null;

View File

@@ -0,0 +1,287 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using FlaxEngine;
using FlaxEditor.Surface.Elements;
using FlaxEditor.Surface.Undo;
namespace FlaxEditor.Surface
{
public partial class VisjectSurface
{
// Reference https://github.com/stefnotch/xnode-graph-formatter/blob/812e08e71c7b9b7eb0810dbdfb0a9a1034da6941/Assets/Examples/MathGraph/Editor/MathGraphEditor.cs
private class NodeFormattingData
{
/// <summary>
/// Starting from 0 at the main nodes
/// </summary>
public int Layer;
/// <summary>
/// Position in the layer
/// </summary>
public int Offset;
/// <summary>
/// How far the subtree needs to be moved additionally
/// </summary>
public int SubtreeOffset;
}
/// <summary>
/// Formats a graph where the nodes can be disjointed.
/// Uses the Sugiyama method
/// </summary>
/// <param name="nodes">List of nodes</param>
protected void FormatGraph(List<SurfaceNode> nodes)
{
if (nodes.Count <= 1 || !CanEdit) return;
var nodesToVisit = new HashSet<SurfaceNode>(nodes);
// While we haven't formatted every node
while (nodesToVisit.Count > 0)
{
// Run a search in both directions
var connectedNodes = new List<SurfaceNode>();
var queue = new Queue<SurfaceNode>();
var startNode = nodesToVisit.First();
nodesToVisit.Remove(startNode);
queue.Enqueue(startNode);
while (queue.Count > 0)
{
var node = queue.Dequeue();
connectedNodes.Add(node);
for (int i = 0; i < node.Elements.Count; i++)
{
if (node.Elements[i] is Box box)
{
for (int j = 0; j < box.Connections.Count; j++)
{
if (nodesToVisit.Contains(box.Connections[j].ParentNode))
{
nodesToVisit.Remove(box.Connections[j].ParentNode);
queue.Enqueue(box.Connections[j].ParentNode);
}
}
}
}
}
FormatConnectedGraph(connectedNodes);
}
}
/// <summary>
/// Formats a graph where all nodes are connected
/// </summary>
/// <param name="nodes">List of connected nodes</param>
protected void FormatConnectedGraph(List<SurfaceNode> nodes)
{
if (nodes.Count <= 1 || !CanEdit) return;
var boundingBox = GetNodesBounds(nodes);
var nodeData = nodes.ToDictionary(n => n, n => new NodeFormattingData { });
// Rightmost nodes with none of our nodes to the right of them
var endNodes = nodes
.Where(n => !n.GetBoxes().Any(b => b.IsOutput && b.Connections.Any(c => nodeData.ContainsKey(c.ParentNode))))
.OrderBy(n => n.Top) // Keep their relative order
.ToList();
// Longest path layering
int maxLayer = SetLayers(nodeData, endNodes);
// Set the vertical offsets
int maxOffset = SetOffsets(nodeData, endNodes, maxLayer);
// Layout the nodes
// Get the largest nodes in the Y and X direction
float[] widths = new float[maxLayer + 1];
float[] heights = new float[maxOffset + 1];
for (int i = 0; i < nodes.Count; i++)
{
if (nodeData.TryGetValue(nodes[i], out var data))
{
if (nodes[i].Width > widths[data.Layer])
{
widths[data.Layer] = nodes[i].Width;
}
if (nodes[i].Height > heights[data.Offset])
{
heights[data.Offset] = nodes[i].Height;
}
}
}
Vector2 minDistanceBetweenNodes = new Vector2(30, 30);
// Figure out the node positions (aligned to a grid)
float[] nodeXPositions = new float[widths.Length];
for (int i = 1; i < widths.Length; i++)
{
// Go from right to left (backwards) through the nodes
nodeXPositions[i] = nodeXPositions[i - 1] + minDistanceBetweenNodes.X + widths[i];
}
float[] nodeYPositions = new float[heights.Length];
for (int i = 1; i < heights.Length; i++)
{
// Go from top to bottom through the nodes
nodeYPositions[i] = nodeYPositions[i - 1] + heights[i - 1] + minDistanceBetweenNodes.Y;
}
// Set the node positions
var undoActions = new List<MoveNodesAction>();
var topRightPosition = endNodes[0].Location;
for (int i = 0; i < nodes.Count; i++)
{
if (nodeData.TryGetValue(nodes[i], out var data))
{
Vector2 newLocation = new Vector2(-nodeXPositions[data.Layer], nodeYPositions[data.Offset]) + topRightPosition;
Vector2 locationDelta = newLocation - nodes[i].Location;
nodes[i].Location = newLocation;
if (Undo != null)
undoActions.Add(new MoveNodesAction(Context, new[] { nodes[i].ID }, locationDelta));
}
}
MarkAsEdited(false);
Undo?.AddAction(new MultiUndoAction(undoActions, "Format nodes"));
}
/// <summary>
/// Assigns a layer to every node
/// </summary>
/// <param name="nodeData">The exta node data</param>
/// <param name="endNodes">The end nodes</param>
/// <returns>The number of the maximum layer</returns>
private int SetLayers(Dictionary<SurfaceNode, NodeFormattingData> nodeData, List<SurfaceNode> endNodes)
{
// Longest path layering
int maxLayer = 0;
var stack = new Stack<SurfaceNode>(endNodes);
while (stack.Count > 0)
{
var node = stack.Pop();
int layer = nodeData[node].Layer;
for (int i = 0; i < node.Elements.Count; i++)
{
if (node.Elements[i] is InputBox box && box.HasAnyConnection)
{
var childNode = box.Connections[0].ParentNode;
if (nodeData.TryGetValue(childNode, out var data))
{
int nodeLayer = Math.Max(data.Layer, layer + 1);
data.Layer = nodeLayer;
if (nodeLayer > maxLayer)
{
maxLayer = nodeLayer;
}
stack.Push(childNode);
}
}
}
}
return maxLayer;
}
/// <summary>
/// Sets the node offsets
/// </summary>
/// <param name="nodeData">The exta node data</param>
/// <param name="endNodes">The end nodes</param>
/// <param name="maxLayer">The number of the maximum layer</param>
/// <returns>The number of the maximum offset</returns>
private int SetOffsets(Dictionary<SurfaceNode, NodeFormattingData> nodeData, List<SurfaceNode> endNodes, int maxLayer)
{
int maxOffset = 0;
// Keeps track of the largest offset (Y axis) for every layer
int[] offsets = new int[maxLayer + 1];
var visitedNodes = new HashSet<SurfaceNode>();
void SetOffsets(SurfaceNode node, NodeFormattingData straightParentData)
{
if (!nodeData.TryGetValue(node, out var data)) return;
// If we realize that the current node would collide with an already existing node in this layer
if (data.Layer >= 0 && offsets[data.Layer] > data.Offset)
{
// Move the entire sub-tree down
straightParentData.SubtreeOffset = Math.Max(straightParentData.SubtreeOffset, offsets[data.Layer] - data.Offset);
}
// Keeps track of the offset of the last direct child we visited
int childOffset = data.Offset;
bool straightChild = true;
// Run the algorithm for every child
for (int i = 0; i < node.Elements.Count; i++)
{
if (node.Elements[i] is InputBox box && box.HasAnyConnection)
{
var childNode = box.Connections[0].ParentNode;
if (!visitedNodes.Contains(childNode) && nodeData.TryGetValue(childNode, out var childData))
{
visitedNodes.Add(childNode);
childData.Offset = childOffset;
SetOffsets(childNode, straightChild ? straightParentData : childData);
childOffset = childData.Offset + 1;
straightChild = false;
}
}
}
if (data.Layer >= 0)
{
// When coming out of the recursion, apply the extra subtree offsets
data.Offset += straightParentData.SubtreeOffset;
if (data.Offset > maxOffset)
{
maxOffset = data.Offset;
}
offsets[data.Layer] = data.Offset + 1;
}
}
{
// An imaginary final node
var endNodeData = new NodeFormattingData { Layer = -1 };
int childOffset = 0;
bool straightChild = true;
for (int i = 0; i < endNodes.Count; i++)
{
if (nodeData.TryGetValue(endNodes[i], out var childData))
{
visitedNodes.Add(endNodes[i]);
childData.Offset = childOffset;
SetOffsets(endNodes[i], straightChild ? endNodeData : childData);
childOffset = childData.Offset + 1;
straightChild = false;
}
}
}
return maxOffset;
}
}
}

View File

@@ -24,7 +24,9 @@ namespace FlaxEditor.Surface
private class InputBracket
{
private readonly float DefaultWidth = 120f;
private readonly Margin _padding = new Margin(10f);
public Box Box { get; }
public Vector2 EndBracketPosition { get; }
public List<SurfaceNode> Nodes { get; } = new List<SurfaceNode>();
@@ -33,7 +35,7 @@ namespace FlaxEditor.Surface
public InputBracket(Box box, Vector2 nodePosition)
{
Box = box;
EndBracketPosition = nodePosition;
EndBracketPosition = nodePosition + new Vector2(DefaultWidth, 0);
Update();
}
@@ -47,11 +49,10 @@ namespace FlaxEditor.Surface
}
else
{
area = new Rectangle(EndBracketPosition, new Vector2(120f, 80f));
area = new Rectangle(EndBracketPosition, new Vector2(DefaultWidth, 80f));
}
_padding.ExpandRectangle(ref area);
Vector2 endPoint = area.Location + new Vector2(area.Width, area.Height / 2f);
Vector2 offset = EndBracketPosition - endPoint;
Vector2 offset = EndBracketPosition - area.UpperRight;
area.Location += offset;
Area = area;
if (!offset.IsZero)
@@ -99,18 +100,6 @@ namespace FlaxEditor.Surface
/// </summary>
public event Window.MouseWheelDelegate CustomMouseWheel;
/// <summary>
/// Gets the node under the mouse location.
/// </summary>
/// <returns>The node or null if no intersection.</returns>
public SurfaceNode GetNodeUnderMouse()
{
var pos = _rootControl.PointFromParent(ref _mousePos);
if (_rootControl.GetChildAt(pos) is SurfaceNode node)
return node;
return null;
}
/// <summary>
/// Gets the control under the mouse location.
/// </summary>
@@ -118,9 +107,7 @@ namespace FlaxEditor.Surface
public SurfaceControl GetControlUnderMouse()
{
var pos = _rootControl.PointFromParent(ref _mousePos);
if (_rootControl.GetChildAtRecursive(pos) is SurfaceControl control)
return control;
return null;
return _rootControl.GetChildAt(pos) as SurfaceControl;
}
private void UpdateSelectionRectangle()
@@ -464,15 +451,7 @@ namespace FlaxEditor.Surface
{
// Check if any control is under the mouse
_cmStartPos = location;
if (controlUnderMouse != null)
{
if (!HasNodesSelection)
Select(controlUnderMouse);
// Show secondary context menu
ShowSecondaryCM(_cmStartPos, controlUnderMouse);
}
else
if (controlUnderMouse == null)
{
// Show primary context menu
ShowPrimaryMenu(_cmStartPos);
@@ -708,31 +687,38 @@ namespace FlaxEditor.Surface
private Vector2 FindEmptySpace(Box box)
{
int boxIndex = 0;
Vector2 distanceBetweenNodes = new Vector2(30, 30);
var node = box.ParentNode;
// Same height as node
float yLocation = node.Top;
for (int i = 0; i < node.Elements.Count; i++)
{
// Box on the same side above the current box
if (node.Elements[i] is Box nodeBox && nodeBox.IsOutput == box.IsOutput && nodeBox.Y < box.Y)
{
boxIndex++;
// Below connected node
yLocation = Mathf.Max(yLocation, nodeBox.ParentNode.Bottom + distanceBetweenNodes.Y);
}
}
// TODO: Dodge the other nodes
Vector2 distanceBetweenNodes = new Vector2(40, 20);
const float NodeHeight = 120;
float xLocation = node.Location.X;
if (box.IsOutput)
{
xLocation += node.Width + distanceBetweenNodes.X;
}
else
{
xLocation += -120 - distanceBetweenNodes.X;
}
float direction = box.IsOutput ? 1 : -1;
Vector2 newNodeLocation = node.Location +
new Vector2(
(node.Width + distanceBetweenNodes.X) * direction,
boxIndex * (NodeHeight + distanceBetweenNodes.Y)
);
return newNodeLocation;
return new Vector2(
xLocation,
yLocation
);
}
}
}

View File

@@ -71,7 +71,7 @@ namespace FlaxEditor.Surface
// Check if has cached groups
if (_cache.Count != 0)
{
// Check if context menu doesn;t have the recent cached groups
// Check if context menu doesn't have the recent cached groups
if (!contextMenu.Groups.Any(g => g.Archetype.Tag is int asInt && asInt == _version))
{
var groups = contextMenu.Groups.Where(g => g.Archetype.Tag is int).ToArray();

View File

@@ -21,6 +21,7 @@ namespace FlaxEditor.Tools.Foliage
private readonly Tabs _modes;
private readonly ContainerControl _noFoliagePanel;
private int _selectedFoliageTypeIndex = -1;
private Button _createNewFoliage;
/// <summary>
/// The editor instance.
@@ -99,6 +100,7 @@ namespace FlaxEditor.Tools.Foliage
public FoliageTab(SpriteHandle icon, Editor editor)
: base(string.Empty, icon)
{
Level.SceneLoaded += OnSceneLoaded;
Editor = editor;
Editor.SceneEditing.SelectionChanged += OnSelectionChanged;
@@ -135,14 +137,31 @@ namespace FlaxEditor.Tools.Foliage
Offsets = Margin.Zero,
Parent = _noFoliagePanel
};
var noFoliageButton = new Button
_createNewFoliage = new Button
{
Text = "Create new foliage",
AnchorPreset = AnchorPresets.MiddleCenter,
Offsets = new Margin(-60, 120, -12, 24),
Parent = _noFoliagePanel,
Enabled = false
};
noFoliageButton.Clicked += OnCreateNewFoliageClicked;
_createNewFoliage.Clicked += OnCreateNewFoliageClicked;
}
private void OnSceneLoaded(Scene arg1, Guid arg2)
{
_createNewFoliage.Enabled = true;
Level.SceneUnloaded += OnSceneUnloaded;
Level.SceneLoaded -= OnSceneLoaded;
}
private void OnSceneUnloaded(Scene arg1, Guid arg2)
{
_createNewFoliage.Enabled = false;
Level.SceneLoaded += OnSceneLoaded;
Level.SceneUnloaded -= OnSceneUnloaded;
}
private void OnSelected(Tab tab)
@@ -248,5 +267,16 @@ namespace FlaxEditor.Tools.Foliage
{
SelectedFoliageTypesChanged?.Invoke();
}
/// <inheritdoc />
public override void OnDestroy()
{
if (_createNewFoliage.Enabled)
Level.SceneUnloaded -= OnSceneUnloaded;
else
Level.SceneLoaded -= OnSceneLoaded;
base.OnDestroy();
}
}
}

View File

@@ -67,7 +67,7 @@ struct GeometryLookup
static bool Search(Actor* actor, GeometryLookup& lookup)
{
// Early out if object is not intersecting with the foliage brush bounds
if (!actor->GetBox().Intersects(lookup.Brush))
if (!actor->GetIsActive() || !actor->GetBox().Intersects(lookup.Brush))
return true;
const auto brush = lookup.Brush;

View File

@@ -18,6 +18,7 @@ namespace FlaxEditor.Tools.Terrain
{
private readonly Tabs _modes;
private readonly ContainerControl _noTerrainPanel;
private readonly Button _createTerrainButton;
/// <summary>
/// The editor instance.
@@ -57,6 +58,7 @@ namespace FlaxEditor.Tools.Terrain
public CarveTab(SpriteHandle icon, Editor editor)
: base(string.Empty, icon)
{
Level.SceneLoaded += OnSceneLoaded;
Editor = editor;
Editor.SceneEditing.SelectionChanged += OnSelectionChanged;
@@ -93,14 +95,31 @@ namespace FlaxEditor.Tools.Terrain
Offsets = Margin.Zero,
Parent = _noTerrainPanel
};
var noTerrainButton = new Button
_createTerrainButton = new Button
{
Text = "Create new terrain",
AnchorPreset = AnchorPresets.MiddleCenter,
Offsets = new Margin(-60, 120, -12, 24),
Parent = _noTerrainPanel
Parent = _noTerrainPanel,
Enabled = false
};
noTerrainButton.Clicked += OnCreateNewTerrainClicked;
_createTerrainButton.Clicked += OnCreateNewTerrainClicked;
}
private void OnSceneLoaded(Scene arg1, Guid arg2)
{
_createTerrainButton.Enabled = true;
Level.SceneUnloaded += OnSceneUnloaded;
Level.SceneLoaded -= OnSceneLoaded;
}
private void OnSceneUnloaded(Scene arg1, Guid arg2)
{
_createTerrainButton.Enabled = false;
Level.SceneLoaded += OnSceneLoaded;
Level.SceneUnloaded -= OnSceneUnloaded;
}
private void OnSelected(Tab tab)
@@ -183,5 +202,16 @@ namespace FlaxEditor.Tools.Terrain
default: throw new IndexOutOfRangeException("Invalid carve tab mode.");
}
}
/// <inheritdoc />
public override void OnDestroy()
{
if (_createTerrainButton.Enabled)
Level.SceneUnloaded -= OnSceneUnloaded;
else
Level.SceneLoaded -= OnSceneLoaded;
base.OnDestroy();
}
}
}

View File

@@ -33,7 +33,7 @@ namespace FlaxEditor.Tools.Terrain.Paint
}
/// <summary>
/// The tool strength (normalized to range 0-1). Defines the intensity of the paint operation to make it stronger or mre subtle.
/// The tool strength (normalized to range 0-1). Defines the intensity of the paint operation to make it stronger or more subtle.
/// </summary>
[EditorOrder(0), Limit(0, 10, 0.01f), Tooltip("The tool strength (normalized to range 0-1). Defines the intensity of the paint operation to make it stronger or more subtle.")]
public float Strength = 1.0f;

View File

@@ -33,7 +33,7 @@ namespace FlaxEditor.Tools.Terrain.Sculpt
}
/// <summary>
/// The tool strength (normalized to range 0-1). Defines the intensity of the sculpt operation to make it stronger or mre subtle.
/// The tool strength (normalized to range 0-1). Defines the intensity of the sculpt operation to make it stronger or more subtle.
/// </summary>
[EditorOrder(0), Limit(0, 6, 0.01f), Tooltip("The tool strength (normalized to range 0-1). Defines the intensity of the sculpt operation to make it stronger or more subtle.")]
public float Strength = 1.2f;

View File

@@ -2,7 +2,7 @@
#include "TerrainTools.h"
#include "Engine/Core/Cache.h"
#include "Engine/Core/Math/VectorInt.h"
#include "Engine/Core/Math/Int2.h"
#include "Engine/Core/Math/Color32.h"
#include "Engine/Core/Collections/CollectionPoolCache.h"
#include "Engine/Terrain/TerrainPatch.h"

View File

@@ -49,7 +49,7 @@ namespace FlaxEditor.Tools
set => Tab._gizmoMode.BrushStrength = value;
}
[EditorOrder(20), EditorDisplay("Brush"), Limit(0.0f, 1.0f, 0.01f), Tooltip("The falloff parameter fo the brush. Adjusts the paint strength for the vertices that are far from the brush center. Use lower values to make painting smoother and softer.")]
[EditorOrder(20), EditorDisplay("Brush"), Limit(0.0f, 1.0f, 0.01f), Tooltip("The falloff parameter for the brush. Adjusts the paint strength for the vertices that are far from the brush center. Use lower values to make painting smoother and softer.")]
public float BrushFalloff
{
get => Tab._gizmoMode.BrushFalloff;

View File

@@ -6,10 +6,8 @@
#include "Engine/Core/Log.h"
#include "Engine/Graphics/Textures/TextureData.h"
#include "Engine/Graphics/PixelFormatExtensions.h"
#include "Engine/Serialization/FileReadStream.h"
#include "Engine/Tools/TextureTool/TextureTool.h"
#include "Engine/Core/Math/Color32.h"
#include "Engine/Core/Math/VectorInt.h"
#include "Engine/Core/Config/GameSettings.h"
#include "Engine/Content/Content.h"
#include "Engine/Content/AssetReference.h"
@@ -247,7 +245,7 @@ void UpdateIconData(uint8* iconData, const TextureData* icon)
iconTexSize = Math::RoundUpToPowerOf2(width);
}
// Try to pick a proper mip (requrie the same size)
// Try to pick a proper mip (require the same size)
const TextureMipData* srcPixels = nullptr;
int32 mipLevels = icon->GetMipLevels();
for (int32 mipIndex = 0; mipIndex < mipLevels; mipIndex++)
@@ -743,6 +741,59 @@ bool EditorUtilities::GenerateCertificate(const String& name, const String& outp
return false;
}
bool EditorUtilities::IsInvalidPathChar(Char c)
{
char illegalChars[] =
{
'?',
'\\',
'/',
'\"',
'<',
'>',
'|',
':',
'*',
'\u0001',
'\u0002',
'\u0003',
'\u0004',
'\u0005',
'\u0006',
'\a',
'\b',
'\t',
'\n',
'\v',
'\f',
'\r',
'\u000E',
'\u000F',
'\u0010',
'\u0011',
'\u0012',
'\u0013',
'\u0014',
'\u0015',
'\u0016',
'\u0017',
'\u0018',
'\u0019',
'\u001A',
'\u001B',
'\u001C',
'\u001D',
'\u001E',
'\u001F'
};
for (auto i : illegalChars)
{
if (c == i)
return true;
}
return false;
}
bool EditorUtilities::ReplaceInFiles(const String& folderPath, const Char* searchPattern, DirectorySearchOption searchOption, const String& findWhat, const String& replaceWith)
{
Array<String> files;

View File

@@ -42,6 +42,13 @@ public:
public:
/// <summary>
/// Determines whether the specified path character is invalid.
/// </summary>
/// <param name="c">The path character.</param>
/// <returns><c>true</c> if the given character cannot be used as a path because it is illegal character; otherwise, <c>false</c>.</returns>
static bool IsInvalidPathChar(Char c);
/// <summary>
/// Replaces the given text with other one in the files.
/// </summary>

View File

@@ -251,7 +251,7 @@ namespace FlaxEditor.Utilities
var list = new List<MemberComparison>();
#if DEBUG_OBJECT_SNAPSHOT_COMPARISION
Debug.Logger.LogHandler.LogWrite(LogType.Warning, "-------------- Comparision --------------");
Debug.Logger.LogHandler.LogWrite(LogType.Warning, "-------------- Comparison --------------");
#endif
for (int i = _members.Count - 1; i >= 0; i--)
{

View File

@@ -126,7 +126,7 @@ namespace FlaxEditor.Utilities
/// </summary>
/// <param name="oper1">The first operator.</param>
/// <param name="oper2">The second operator.</param>
/// <returns>The comparision result.</returns>
/// <returns>The comparison result.</returns>
private static bool CompareOperators(string oper1, string oper2)
{
var op1 = Operators[oper1];
@@ -193,6 +193,7 @@ namespace FlaxEditor.Utilities
{
case 'x':
case 'X':
{
// Hexadecimal value
i++;
token.Clear();
@@ -200,22 +201,35 @@ namespace FlaxEditor.Utilities
throw new ParsingException("invalid hexadecimal number");
while (i + 1 < text.Length && StringUtils.IsHexDigit(text[i + 1]))
{
i++;
token.Append(text[i]);
token.Append(text[++i]);
}
var value = ulong.Parse(token.ToString(), NumberStyles.HexNumber);
token.Clear();
token.Append(value.ToString());
break;
}
default:
{
// Decimal value
while (i + 1 < text.Length && DetermineType(text[i + 1]) == TokenType.Number)
{
i++;
token.Append(text[i]);
token.Append(text[++i]);
}
// Exponential notation
if (i + 2 < text.Length && (text[i + 1] == 'e' || text[i + 1] == 'E'))
{
token.Append(text[++i]);
if (text[i + 1] == '-' || text[i + 1] == '+')
token.Append(text[++i]);
while (i + 1 < text.Length && DetermineType(text[i + 1]) == TokenType.Number)
{
token.Append(text[++i]);
}
}
break;
}
}
}
// Discard solo '-'

View File

@@ -532,6 +532,8 @@ namespace FlaxEditor.Utilities
break;
case VariantType.Enum:
case VariantType.Structure:
case VariantType.ManagedObject:
case VariantType.Typename:
stream.Write(int.MaxValue);
stream.WriteStrAnsi(type.FullName, 77);
break;
@@ -742,6 +744,7 @@ namespace FlaxEditor.Utilities
case VariantType.Array: return new ScriptType(typeof(object[]));
case VariantType.Dictionary: return new ScriptType(typeof(Dictionary<object, object>));
case VariantType.ManagedObject: return new ScriptType(typeof(object));
case VariantType.Blob: return new ScriptType(typeof(byte[]));
default: throw new ArgumentOutOfRangeException($"Unknown Variant Type {variantType} without typename.");
}
}
@@ -760,7 +763,7 @@ namespace FlaxEditor.Utilities
data[i] = (byte)(c ^ 77);
}
var typeName = System.Text.Encoding.ASCII.GetString(data);
return TypeUtils.GetType(typeName).Type;
return TypeUtils.GetManagedType(typeName);
}
if (typeNameLength > 0)
{
@@ -772,7 +775,7 @@ namespace FlaxEditor.Utilities
data[i] = (char)(c ^ 77);
}
var typeName = new string(data);
return TypeUtils.GetType(typeName).Type;
return TypeUtils.GetManagedType(typeName);
}
switch (variantType)
{
@@ -805,6 +808,7 @@ namespace FlaxEditor.Utilities
case VariantType.Array: return typeof(object[]);
case VariantType.Dictionary: return typeof(Dictionary<object, object>);
case VariantType.ManagedObject: return typeof(object);
case VariantType.Blob: return typeof(byte[]);
default: throw new ArgumentOutOfRangeException($"Unknown Variant Type {variantType} without typename.");
}
}
@@ -824,7 +828,7 @@ namespace FlaxEditor.Utilities
data[i] = (byte)(c ^ 77);
}
var typeName = System.Text.Encoding.ASCII.GetString(data);
type = TypeUtils.GetType(typeName).Type;
type = TypeUtils.GetManagedType(typeName);
}
else if (typeNameLength > 0)
{
@@ -836,7 +840,7 @@ namespace FlaxEditor.Utilities
data[i] = (char)(c ^ 77);
}
var typeName = new string(data);
type = TypeUtils.GetType(typeName).Type;
type = TypeUtils.GetManagedType(typeName);
}
switch (variantType)
{

View File

@@ -24,7 +24,7 @@ namespace FlaxEditor.Viewport.Cameras
public bool IsAnimatingMove => _moveStartTime > Mathf.Epsilon;
/// <summary>
/// The target point location. It's used to orbit around it whe user clicks Alt+LMB.
/// The target point location. It's used to orbit around it when user clicks Alt+LMB.
/// </summary>
public Vector3 TargetPoint = new Vector3(-200);
@@ -188,8 +188,16 @@ namespace FlaxEditor.Viewport.Cameras
if (input.IsPanning)
{
var panningSpeed = 0.8f;
position -= right * (mouseDelta.X * panningSpeed);
position -= up * (mouseDelta.Y * panningSpeed);
if (Viewport.InvertPanning)
{
position += up * (mouseDelta.Y * panningSpeed);
position += right * (mouseDelta.X * panningSpeed);
}
else
{
position -= right * (mouseDelta.X * panningSpeed);
position -= up * (mouseDelta.Y * panningSpeed);
}
}
// Move

View File

@@ -32,9 +32,9 @@ namespace FlaxEditor.Viewport.Cameras
/// </summary>
/// <param name="objectBounds">The target object bounds.</param>
/// <param name="marginDistanceScale">The margin distance scale of the orbit radius.</param>
public void SerArcBallView(BoundingBox objectBounds, float marginDistanceScale = 2.0f)
public void SetArcBallView(BoundingBox objectBounds, float marginDistanceScale = 2.0f)
{
SerArcBallView(BoundingSphere.FromBox(objectBounds), marginDistanceScale);
SetArcBallView(BoundingSphere.FromBox(objectBounds), marginDistanceScale);
}
/// <summary>
@@ -42,18 +42,18 @@ namespace FlaxEditor.Viewport.Cameras
/// </summary>
/// <param name="objectBounds">The target object bounds.</param>
/// <param name="marginDistanceScale">The margin distance scale of the orbit radius.</param>
public void SerArcBallView(BoundingSphere objectBounds, float marginDistanceScale = 2.0f)
public void SetArcBallView(BoundingSphere objectBounds, float marginDistanceScale = 2.0f)
{
SerArcBallView(new Quaternion(-0.08f, -0.92f, 0.31f, -0.23f), objectBounds.Center, objectBounds.Radius * marginDistanceScale);
SetArcBallView(new Quaternion(-0.08f, -0.92f, 0.31f, -0.23f), objectBounds.Center, objectBounds.Radius * marginDistanceScale);
}
/// <summary>
/// Sets view orientation and position to match the arc ball camera style view for the given orbit radius.
/// </summary>
/// <param name="orbitRadius">The orbit radius.</param>
public void SerArcBallView(float orbitRadius)
public void SetArcBallView(float orbitRadius)
{
SerArcBallView(new Quaternion(-0.08f, -0.92f, 0.31f, -0.23f), Vector3.Zero, orbitRadius);
SetArcBallView(new Quaternion(-0.08f, -0.92f, 0.31f, -0.23f), Vector3.Zero, orbitRadius);
}
/// <summary>
@@ -62,7 +62,7 @@ namespace FlaxEditor.Viewport.Cameras
/// <param name="orientation">The view rotation.</param>
/// <param name="orbitCenter">The orbit center location.</param>
/// <param name="orbitRadius">The orbit radius.</param>
public void SerArcBallView(Quaternion orientation, Vector3 orbitCenter, float orbitRadius)
public void SetArcBallView(Quaternion orientation, Vector3 orbitCenter, float orbitRadius)
{
// Rotate
Viewport.ViewOrientation = orientation;

View File

@@ -138,9 +138,7 @@ namespace FlaxEditor.Viewport
private bool _isControllingMouse;
private int _deltaFilteringStep;
private Vector2 _startPosMiddle;
private Vector2 _startPosRight;
private Vector2 _startPosLeft;
private Vector2 _startPos;
private Vector2 _mouseDeltaRightLast;
private Vector2[] _deltaFilteringBuffer = new Vector2[FpsCameraFilteringFrames];
@@ -180,6 +178,7 @@ namespace FlaxEditor.Viewport
private float _orthoSize = 1.0f;
private bool _isOrtho = false;
private float _wheelMovementChangeDeltaSum = 0;
private bool _invertPanning;
/// <summary>
/// Speed of the mouse.
@@ -403,6 +402,15 @@ namespace FlaxEditor.Viewport
set => _isOrtho = value;
}
/// <summary>
/// Gets or sets if the panning direction is inverted.
/// </summary>
public bool InvertPanning
{
get => _invertPanning;
set => _invertPanning = value;
}
/// <summary>
/// The input actions collection to processed during user input.
/// </summary>
@@ -434,6 +442,7 @@ namespace FlaxEditor.Viewport
_nearPlane = options.Viewport.DefaultNearPlane;
_farPlane = options.Viewport.DefaultFarPlane;
_fieldOfView = options.Viewport.DefaultFieldOfView;
_invertPanning = options.Viewport.DefaultInvertPanning;
Editor.Instance.Options.OptionsChanged += OnEditorOptionsChanged;
OnEditorOptionsChanged(options);
@@ -454,7 +463,7 @@ namespace FlaxEditor.Viewport
var button = camSpeedCM.AddButton(v.ToString());
button.Tag = v;
}
camSpeedCM.ButtonClicked += (button) => MovementSpeed = (float)button.Tag;
camSpeedCM.ButtonClicked += button => MovementSpeed = (float)button.Tag;
camSpeedCM.VisibleChanged += WidgetCamSpeedShowHide;
camSpeedButton.Parent = camSpeed;
camSpeed.Parent = this;
@@ -515,9 +524,9 @@ namespace FlaxEditor.Viewport
// Orthographic
{
var ortho = ViewWidgetButtonMenu.AddButton("Orthographic");
var orthoValue = new CheckBox(75, 2, _isOrtho);
var orthoValue = new CheckBox(90, 2, _isOrtho);
orthoValue.Parent = ortho;
orthoValue.StateChanged += (checkBox) =>
orthoValue.StateChanged += checkBox =>
{
if (checkBox.Checked != _isOrtho)
{
@@ -528,13 +537,25 @@ namespace FlaxEditor.Viewport
ViewWidgetButtonMenu.VisibleChanged += control => orthoValue.Checked = _isOrtho;
}
// Cara Orientation
{
var cameraView = ViewWidgetButtonMenu.AddChildMenu("Orientation").ContextMenu;
for (int i = 0; i < EditorViewportCameraOrientationValues.Length; i++)
{
var co = EditorViewportCameraOrientationValues[i];
var button = cameraView.AddButton(co.Name);
button.Tag = co.Orientation;
}
cameraView.ButtonClicked += button => ViewOrientation = Quaternion.Euler((Vector3)button.Tag);
}
// Field of View
{
var fov = ViewWidgetButtonMenu.AddButton("Field Of View");
var fovValue = new FloatValueBox(1, 75, 2, 50.0f, 35.0f, 160.0f, 0.1f);
var fovValue = new FloatValueBox(1, 90, 2, 70.0f, 35.0f, 160.0f, 0.1f);
fovValue.Parent = fov;
fovValue.ValueChanged += () => _fieldOfView = fovValue.Value;
ViewWidgetButtonMenu.VisibleChanged += (control) =>
ViewWidgetButtonMenu.VisibleChanged += control =>
{
fov.Visible = !_isOrtho;
fovValue.Value = _fieldOfView;
@@ -544,10 +565,10 @@ namespace FlaxEditor.Viewport
// Ortho Scale
{
var orthoSize = ViewWidgetButtonMenu.AddButton("Ortho Scale");
var orthoSizeValue = new FloatValueBox(_orthoSize, 75, 2, 50.0f, 0.001f, 100000.0f, 0.01f);
var orthoSizeValue = new FloatValueBox(_orthoSize, 90, 2, 70.0f, 0.001f, 100000.0f, 0.01f);
orthoSizeValue.Parent = orthoSize;
orthoSizeValue.ValueChanged += () => _orthoSize = orthoSizeValue.Value;
ViewWidgetButtonMenu.VisibleChanged += (control) =>
ViewWidgetButtonMenu.VisibleChanged += control =>
{
orthoSize.Visible = _isOrtho;
orthoSizeValue.Value = _orthoSize;
@@ -557,7 +578,7 @@ namespace FlaxEditor.Viewport
// Near Plane
{
var nearPlane = ViewWidgetButtonMenu.AddButton("Near Plane");
var nearPlaneValue = new FloatValueBox(2.0f, 75, 2, 50.0f, 0.001f, 1000.0f);
var nearPlaneValue = new FloatValueBox(2.0f, 90, 2, 70.0f, 0.001f, 1000.0f);
nearPlaneValue.Parent = nearPlane;
nearPlaneValue.ValueChanged += () => _nearPlane = nearPlaneValue.Value;
ViewWidgetButtonMenu.VisibleChanged += control => nearPlaneValue.Value = _nearPlane;
@@ -566,7 +587,7 @@ namespace FlaxEditor.Viewport
// Far Plane
{
var farPlane = ViewWidgetButtonMenu.AddButton("Far Plane");
var farPlaneValue = new FloatValueBox(1000, 75, 2, 50.0f, 10.0f);
var farPlaneValue = new FloatValueBox(1000, 90, 2, 70.0f, 10.0f);
farPlaneValue.Parent = farPlane;
farPlaneValue.ValueChanged += () => _farPlane = farPlaneValue.Value;
ViewWidgetButtonMenu.VisibleChanged += control => farPlaneValue.Value = _farPlane;
@@ -575,7 +596,7 @@ namespace FlaxEditor.Viewport
// Brightness
{
var brightness = ViewWidgetButtonMenu.AddButton("Brightness");
var brightnessValue = new FloatValueBox(1.0f, 75, 2, 50.0f, 0.001f, 10.0f, 0.001f);
var brightnessValue = new FloatValueBox(1.0f, 90, 2, 70.0f, 0.001f, 10.0f, 0.001f);
brightnessValue.Parent = brightness;
brightnessValue.ValueChanged += () => Brightness = brightnessValue.Value;
ViewWidgetButtonMenu.VisibleChanged += control => brightnessValue.Value = Brightness;
@@ -584,11 +605,26 @@ namespace FlaxEditor.Viewport
// Resolution
{
var resolution = ViewWidgetButtonMenu.AddButton("Resolution");
var resolutionValue = new FloatValueBox(1.0f, 75, 2, 50.0f, 0.1f, 4.0f, 0.001f);
var resolutionValue = new FloatValueBox(1.0f, 90, 2, 70.0f, 0.1f, 4.0f, 0.001f);
resolutionValue.Parent = resolution;
resolutionValue.ValueChanged += () => ResolutionScale = resolutionValue.Value;
ViewWidgetButtonMenu.VisibleChanged += control => resolutionValue.Value = ResolutionScale;
}
// Invert Panning
{
var invert = ViewWidgetButtonMenu.AddButton("Invert Panning");
var invertValue = new CheckBox(90, 2, _invertPanning);
invertValue.Parent = invert;
invertValue.StateChanged += checkBox =>
{
if (checkBox.Checked != _invertPanning)
{
_invertPanning = checkBox.Checked;
}
};
ViewWidgetButtonMenu.VisibleChanged += control => invertValue.Checked = _invertPanning;
}
}
// Link for task event
@@ -812,7 +848,7 @@ namespace FlaxEditor.Viewport
/// </summary>
protected virtual void OnLeftMouseButtonDown()
{
_startPosLeft = _viewMousePos;
_startPos = _viewMousePos;
}
/// <summary>
@@ -827,7 +863,7 @@ namespace FlaxEditor.Viewport
/// </summary>
protected virtual void OnRightMouseButtonDown()
{
_startPosRight = _viewMousePos;
_startPos = _viewMousePos;
}
/// <summary>
@@ -842,7 +878,7 @@ namespace FlaxEditor.Viewport
/// </summary>
protected virtual void OnMiddleMouseButtonDown()
{
_startPosMiddle = _viewMousePos;
_startPos = _viewMousePos;
}
/// <summary>
@@ -1013,7 +1049,13 @@ namespace FlaxEditor.Viewport
moveDelta *= 0.3f;
// Calculate smooth mouse delta not dependant on viewport size
Vector2 offset = _viewMousePos - (_input.IsMouseMiddleDown ? _startPosMiddle : _startPosRight);
Vector2 offset = _viewMousePos - _startPos;
if (_input.IsZooming && !_input.IsMouseRightDown && !_input.IsMouseLeftDown && !_input.IsMouseMiddleDown)
{
offset = Vector2.Zero;
}
offset.X = offset.X > 0 ? Mathf.Floor(offset.X) : Mathf.Ceil(offset.X);
offset.Y = offset.Y > 0 ? Mathf.Floor(offset.Y) : Mathf.Ceil(offset.Y);
_mouseDeltaRight = offset / size;
@@ -1055,7 +1097,7 @@ namespace FlaxEditor.Viewport
// Move mouse back to the root position
if (centerMouse && (_input.IsMouseRightDown || _input.IsMouseLeftDown || _input.IsMouseMiddleDown))
{
Vector2 center = PointToWindow(_input.IsMouseMiddleDown ? _startPosMiddle : _startPosRight);
Vector2 center = PointToWindow(_startPos);
win.MousePosition = center;
}
}
@@ -1101,11 +1143,11 @@ namespace FlaxEditor.Viewport
if (_input.IsMouseLeftDown)
{
// Calculate smooth mouse delta not dependant on viewport size
Vector2 offset = _viewMousePos - _startPosLeft;
Vector2 offset = _viewMousePos - _startPos;
offset.X = offset.X > 0 ? Mathf.Floor(offset.X) : Mathf.Ceil(offset.X);
offset.Y = offset.Y > 0 ? Mathf.Floor(offset.Y) : Mathf.Ceil(offset.Y);
_mouseDeltaLeft = offset / size;
_startPosLeft = _viewMousePos;
_startPos = _viewMousePos;
}
else
{
@@ -1193,6 +1235,28 @@ namespace FlaxEditor.Viewport
base.OnDestroy();
}
private struct CameraOrientation
{
public readonly string Name;
public readonly Vector3 Orientation;
public CameraOrientation(string name, Vector3 orientation)
{
Name = name;
Orientation = orientation;
}
}
private readonly CameraOrientation[] EditorViewportCameraOrientationValues =
{
new CameraOrientation("Front", new Vector3(0, 0, 0)),
new CameraOrientation("Back", new Vector3(0, 180, 0)),
new CameraOrientation("Left", new Vector3(0, 90, 0)),
new CameraOrientation("Right", new Vector3(0, -90, 0)),
new CameraOrientation("Top", new Vector3(-90, 0, 0)),
new CameraOrientation("Bottom", new Vector3(90, 0, 0))
};
private readonly float[] EditorViewportCameraSpeedValues =
{
0.1f,

View File

@@ -485,6 +485,23 @@ namespace FlaxEditor.Viewport
}
}
/// <inheritdoc />
public override void Draw()
{
base.Draw();
// Selected UI controls outline
for (var i = 0; i < _window.Selection.Count; i++)
{
if (_window.Selection[i].EditableObject is UIControl controlActor && controlActor.Control != null)
{
var control = controlActor.Control;
var bounds = Rectangle.FromPoints(control.PointToParent(this, Vector2.Zero), control.PointToParent(this, control.Size));
Render2D.DrawRectangle(bounds, Editor.Instance.Options.Options.Visual.SelectionOutlineColor0, Editor.Instance.Options.Options.Visual.UISelectionOutlineSize);
}
}
}
/// <inheritdoc />
protected override void OnLeftMouseButtonUp()
{

View File

@@ -84,7 +84,7 @@ namespace FlaxEditor.Viewport.Previews
// Preview LOD
{
var previewLOD = ViewWidgetButtonMenu.AddButton("Preview LOD");
var previewLODValue = new IntValueBox(-1, 75, 2, 50.0f, -1, 10, 0.02f);
var previewLODValue = new IntValueBox(-1, 90, 2, 70.0f, -1, 10, 0.02f);
previewLODValue.Parent = previewLOD;
previewLODValue.ValueChanged += () => _previewModel.ForcedLOD = previewLODValue.Value;
ViewWidgetButtonMenu.VisibleChanged += control => previewLODValue.Value = _previewModel.ForcedLOD;

View File

@@ -85,7 +85,7 @@ namespace FlaxEditor.Viewport.Previews
var orbitRadius = 200.0f;
if (camera is ArcBallCamera arcBallCamera)
orbitRadius = arcBallCamera.OrbitRadius;
camera.SerArcBallView(new Quaternion(-0.08f, -0.92f, 0.31f, -0.23f), Vector3.Zero, orbitRadius);
camera.SetArcBallView(new Quaternion(-0.08f, -0.92f, 0.31f, -0.23f), Vector3.Zero, orbitRadius);
if (useWidgets)
{

View File

@@ -23,6 +23,15 @@ namespace FlaxEditor.Viewport.Previews
"Cone"
};
private static readonly Transform[] Transforms =
{
new Transform(Vector3.Zero, Quaternion.RotationY(Mathf.Pi), new Vector3(0.45f)),
new Transform(Vector3.Zero, Quaternion.RotationY(Mathf.Pi), new Vector3(0.45f)),
new Transform(Vector3.Zero, Quaternion.Identity, new Vector3(0.45f)),
new Transform(Vector3.Zero, Quaternion.RotationY(Mathf.Pi), new Vector3(0.45f)),
new Transform(Vector3.Zero, Quaternion.RotationY(Mathf.Pi), new Vector3(0.45f)),
};
private StaticModel _previewModel;
private Decal _decal;
private Terrain _terrain;
@@ -65,6 +74,7 @@ namespace FlaxEditor.Viewport.Previews
_selectedModelIndex = value;
_previewModel.Model = FlaxEngine.Content.LoadAsyncInternal<Model>("Editor/Primitives/" + Models[value]);
_previewModel.Transform = Transforms[value];
}
}
@@ -77,7 +87,6 @@ namespace FlaxEditor.Viewport.Previews
{
// Setup preview scene
_previewModel = new StaticModel();
_previewModel.Transform = new Transform(Vector3.Zero, Quaternion.RotationY(Mathf.Pi), new Vector3(0.45f));
SelectedModelIndex = 0;
// Link actors for rendering

View File

@@ -53,7 +53,7 @@ namespace FlaxEditor.Viewport.Previews
// Preview LOD
{
var previewLOD = ViewWidgetButtonMenu.AddButton("Preview LOD");
var previewLODValue = new IntValueBox(-1, 75, 2, 50.0f, -1, 10, 0.02f);
var previewLODValue = new IntValueBox(-1, 90, 2, 70.0f, -1, 10, 0.02f);
previewLODValue.Parent = previewLOD;
previewLODValue.ValueChanged += () => _previewModel.ForcedLOD = previewLODValue.Value;
ViewWidgetButtonMenu.VisibleChanged += control => previewLODValue.Value = _previewModel.ForcedLOD;

View File

@@ -67,7 +67,7 @@ namespace FlaxEditor.Viewport.Previews
if (useWidgets)
{
var playbackDuration = ViewWidgetButtonMenu.AddButton("Duration");
var playbackDurationValue = new FloatValueBox(_playbackDuration, 75, 2, 50.0f, 0.1f, 1000000.0f, 0.1f);
var playbackDurationValue = new FloatValueBox(_playbackDuration, 90, 2, 70.0f, 0.1f, 1000000.0f, 0.1f);
playbackDurationValue.Parent = playbackDuration;
playbackDurationValue.ValueChanged += () => PlaybackDuration = playbackDurationValue.Value;
ViewWidgetButtonMenu.VisibleChanged += control => playbackDurationValue.Value = PlaybackDuration;

View File

@@ -1,7 +1,6 @@
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
using FlaxEngine;
using FlaxEngine.GUI;
using Object = FlaxEngine.Object;
namespace FlaxEditor.Viewport.Previews
@@ -19,7 +18,7 @@ namespace FlaxEditor.Viewport.Previews
private Prefab _prefab;
private Actor _instance;
internal Control customControlLinked;
internal UIControl customControlLinked;
/// <summary>
/// Gets or sets the prefab asset to preview.
@@ -29,39 +28,37 @@ namespace FlaxEditor.Viewport.Previews
get => _prefab;
set
{
if (_prefab != value)
if (_prefab == value)
return;
// Unset and cleanup spawned instance
if (_instance)
{
if (_instance)
var instance = _instance;
Instance = null;
Object.Destroy(instance);
}
_prefab = value;
if (_prefab)
{
// Load prefab
_prefab.WaitForLoaded();
// Spawn prefab
var prevPreview = LoadingPreview;
LoadingPreview = this;
var instance = PrefabManager.SpawnPrefab(_prefab, null);
LoadingPreview = prevPreview;
if (instance == null)
{
if (customControlLinked != null)
{
customControlLinked.Parent = null;
customControlLinked = null;
}
Task.RemoveCustomActor(_instance);
Object.Destroy(_instance);
_prefab = null;
throw new FlaxException("Failed to spawn a prefab for the preview.");
}
_prefab = value;
if (_prefab)
{
_prefab.WaitForLoaded(); // TODO: use lazy prefab spawning to reduce stalls
var prevPreview = LoadingPreview;
LoadingPreview = this;
_instance = PrefabManager.SpawnPrefab(_prefab, null);
LoadingPreview = prevPreview;
if (_instance == null)
{
_prefab = null;
throw new FlaxException("Failed to spawn a prefab for the preview.");
}
Task.AddCustomActor(_instance);
}
// Set instance
Instance = instance;
}
}
}
@@ -72,7 +69,47 @@ namespace FlaxEditor.Viewport.Previews
public Actor Instance
{
get => _instance;
internal set => _instance = value;
internal set
{
if (_instance == value)
return;
if (_instance)
{
// Unlink UI control
if (customControlLinked)
{
if (customControlLinked.Control?.Parent == this)
customControlLinked.Control.Parent = null;
customControlLinked = null;
}
// Remove for the preview
Task.RemoveCustomActor(_instance);
}
_instance = value;
if (_instance)
{
// Add to the preview
Task.AddCustomActor(_instance);
// Link UI canvases to the preview
LinkCanvas(_instance);
}
}
}
private void LinkCanvas(Actor actor)
{
if (actor is UICanvas uiCanvas)
uiCanvas.EditorOverride(Task, this);
var children = actor.ChildrenCount;
for (int i = 0; i < children; i++)
{
LinkCanvas(actor.GetChild(i));
}
}
/// <summary>
@@ -87,7 +124,6 @@ namespace FlaxEditor.Viewport.Previews
/// <inheritdoc />
public override void OnDestroy()
{
// Cleanup
Prefab = null;
base.OnDestroy();

View File

@@ -159,13 +159,14 @@ namespace FlaxEditor.Viewport.Previews
float prevScale = _viewScale;
_viewScale = Mathf.Clamp(_viewScale + delta * 0.24f, 0.001f, 20.0f);
// Move view to make use of the control much more soother
//float coeff = (prevScale + (_viewScale - prevScale)) / prevScale;
//_viewPos += (location * coeff - location) / _viewScale;
//_viewPos += location / _viewScale;
Vector2 sizeDelta = (_viewScale - prevScale) * _textureRect.Size;
// Compensate for the Rectangle.MakeScaled
Vector2 sizeDelta = (_viewScale - prevScale) * _textureRect.Size * 0.5f;
_viewPos += sizeDelta * 0.5f;
// Move to zoom position
Vector2 locationOnTexture = (location - _textureRect.Location) / _textureRect.Size;
_viewPos -= sizeDelta * locationOnTexture;
return true;
}

View File

@@ -115,7 +115,7 @@ namespace FlaxEditor.Windows
{
var thirdPartyPanel = new Panel(ScrollBars.Vertical)
{
Bounds = new Rectangle(0, topParentControl.Bottom + 4, Width, Height - topParentControl.Bottom - 24),
Bounds = new Rectangle(4, topParentControl.Bottom + 4, Width - 8, Height - topParentControl.Bottom - 24),
Parent = this
};
var thirdPartyEntries = new[]

Some files were not shown because too many files have changed in this diff Show More