diff --git a/.github/data/bt.sh b/.github/data/bt.sh index f636b0ffa..c02a09adc 100644 --- a/.github/data/bt.sh +++ b/.github/data/bt.sh @@ -9,6 +9,7 @@ gdb -q --batch \ -ex 'handle SIGUSR1 nostop pass' \ -ex 'handle SIGUSR2 nostop pass' \ -ex 'handle SIGCHLD nostop pass' \ + -ex 'handle SIG34 nostop pass' \ -ex 'set print thread-events off' \ -return-child-result \ -ex 'run' \ diff --git a/.github/workflows/build_android.yml b/.github/workflows/build_android.yml index 772e3f67c..741d446f7 100644 --- a/.github/workflows/build_android.yml +++ b/.github/workflows/build_android.yml @@ -4,6 +4,7 @@ on: [push, pull_request] env: DOTNET_NOLOGO: true DOTNET_CLI_TELEMETRY_OPTOUT: false + DOTNET_ROLL_FORWARD: 'minor' jobs: @@ -19,7 +20,7 @@ jobs: - name: Setup .NET uses: actions/setup-dotnet@v3 with: - dotnet-version: 8.0.x + dotnet-version: 8.0.419 - name: Setup .NET Workload run: | dotnet workload install android @@ -33,4 +34,7 @@ jobs: git lfs pull - name: Build run: | + PowerShell "(Get-Content global.json).Replace('latestMajor', 'minor') | Set-Content global.json" + PowerShell "(Get-Content Source/Tools/Flax.Build/global.json).Replace('latestMajor', 'minor') | Set-Content Source/Tools/Flax.Build/global.json" + PowerShell "(Get-Content Source/Tools/Flax.Build/Flax.Build.csproj).Replace('LatestMajor', 'Minor') | Set-Content Source/Tools/Flax.Build/Flax.Build.csproj" .\Development\Scripts\Windows\CallBuildTool.bat -build -log -printSDKs -dotnet=8 -arch=ARM64 -platform=Android -configuration=Release -buildtargets=FlaxGame diff --git a/.github/workflows/build_windows.yml b/.github/workflows/build_windows.yml index 85f4e0c79..f64e800b3 100644 --- a/.github/workflows/build_windows.yml +++ b/.github/workflows/build_windows.yml @@ -4,6 +4,7 @@ on: [push, pull_request] env: DOTNET_NOLOGO: true DOTNET_CLI_TELEMETRY_OPTOUT: false + DOTNET_ROLL_FORWARD: 'minor' jobs: @@ -19,7 +20,7 @@ jobs: - name: Setup .NET uses: actions/setup-dotnet@v3 with: - dotnet-version: 8.0.x + dotnet-version: 8.0.419 - name: Print .NET info run: | dotnet --info @@ -30,6 +31,9 @@ jobs: git lfs pull - name: Build run: | + PowerShell "(Get-Content global.json).Replace('latestMajor', 'minor') | Set-Content global.json" + PowerShell "(Get-Content Source/Tools/Flax.Build/global.json).Replace('latestMajor', 'minor') | Set-Content Source/Tools/Flax.Build/global.json" + PowerShell "(Get-Content Source/Tools/Flax.Build/Flax.Build.csproj).Replace('LatestMajor', 'Minor') | Set-Content Source/Tools/Flax.Build/Flax.Build.csproj" .\Development\Scripts\Windows\CallBuildTool.bat -build -log -printSDKs -dotnet=8 -arch=x64 -platform=Windows -configuration=Development -buildtargets=FlaxEditor # Game @@ -44,7 +48,7 @@ jobs: - name: Setup .NET uses: actions/setup-dotnet@v3 with: - dotnet-version: 8.0.x + dotnet-version: 8.0.419 - name: Print .NET info run: | dotnet --info @@ -55,4 +59,7 @@ jobs: git lfs pull - name: Build run: | + PowerShell "(Get-Content global.json).Replace('latestMajor', 'minor') | Set-Content global.json" + PowerShell "(Get-Content Source/Tools/Flax.Build/global.json).Replace('latestMajor', 'minor') | Set-Content Source/Tools/Flax.Build/global.json" + PowerShell "(Get-Content Source/Tools/Flax.Build/Flax.Build.csproj).Replace('LatestMajor', 'Minor') | Set-Content Source/Tools/Flax.Build/Flax.Build.csproj" .\Development\Scripts\Windows\CallBuildTool.bat -build -log -printSDKs -dotnet=8 -arch=x64 -platform=Windows -configuration=Release -buildtargets=FlaxGame diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index 64809dafd..1b6e48e4e 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -7,6 +7,7 @@ on: env: DOTNET_NOLOGO: true DOTNET_CLI_TELEMETRY_OPTOUT: false + DOTNET_ROLL_FORWARD: 'minor' GIT_LFS_PULL_OPTIONS: '-c lfs.concurrenttransfers=1 -c lfs.transfer.maxretries=2 -c http.version="HTTP/1.1" -c lfs.activitytimeout=60' jobs: @@ -27,13 +28,16 @@ jobs: - name: Setup .NET uses: actions/setup-dotnet@v3 with: - dotnet-version: 8.0.x + dotnet-version: 8.0.419 - name: Print .NET info run: | dotnet --info dotnet workload --info - name: Build run: | + PowerShell "(Get-Content global.json).Replace('latestMajor', 'minor') | Set-Content global.json" + PowerShell "(Get-Content Source/Tools/Flax.Build/global.json).Replace('latestMajor', 'minor') | Set-Content Source/Tools/Flax.Build/global.json" + PowerShell "(Get-Content Source/Tools/Flax.Build/Flax.Build.csproj).Replace('LatestMajor', 'Minor') | Set-Content Source/Tools/Flax.Build/Flax.Build.csproj" .\PackageEditor.bat -arch=x64 -platform=Windows -deployOutput=Output -dotnet=8 - name: Upload uses: actions/upload-artifact@v4 @@ -60,13 +64,16 @@ jobs: - name: Setup .NET uses: actions/setup-dotnet@v3 with: - dotnet-version: 8.0.x + dotnet-version: 8.0.419 - name: Print .NET info run: | dotnet --info dotnet workload --info - name: Build run: | + PowerShell "(Get-Content global.json).Replace('latestMajor', 'minor') | Set-Content global.json" + PowerShell "(Get-Content Source/Tools/Flax.Build/global.json).Replace('latestMajor', 'minor') | Set-Content Source/Tools/Flax.Build/global.json" + PowerShell "(Get-Content Source/Tools/Flax.Build/Flax.Build.csproj).Replace('LatestMajor', 'Minor') | Set-Content Source/Tools/Flax.Build/Flax.Build.csproj" .\PackagePlatforms.bat -arch=x64 -platform=Windows -deployOutput=Output -dotnet=8 - name: Upload uses: actions/upload-artifact@v4 diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 6b9168f11..2c1dfc762 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -4,6 +4,7 @@ on: [push, pull_request] env: DOTNET_NOLOGO: true DOTNET_CLI_TELEMETRY_OPTOUT: false + DOTNET_ROLL_FORWARD: 'minor' jobs: @@ -56,7 +57,7 @@ jobs: - name: Setup .NET uses: actions/setup-dotnet@v3 with: - dotnet-version: 8.0.x + dotnet-version: 8.0.419 - name: Print .NET info run: | dotnet --info @@ -67,6 +68,9 @@ jobs: git lfs pull - name: Build run: | + PowerShell "(Get-Content global.json).Replace('latestMajor', 'minor') | Set-Content global.json" + PowerShell "(Get-Content Source/Tools/Flax.Build/global.json).Replace('latestMajor', 'minor') | Set-Content Source/Tools/Flax.Build/global.json" + PowerShell "(Get-Content Source/Tools/Flax.Build/Flax.Build.csproj).Replace('LatestMajor', 'Minor') | Set-Content Source/Tools/Flax.Build/Flax.Build.csproj" .\GenerateProjectFiles.bat -vs2022 -log -verbose -printSDKs -dotnet=8 .\Development\Scripts\Windows\CallBuildTool.bat -build -log -dotnet=8 -arch=x64 -platform=Windows -configuration=Development -buildtargets=FlaxTestsTarget dotnet msbuild Source\Tools\Flax.Build.Tests\Flax.Build.Tests.csproj /m /t:Restore,Build /p:Configuration=Debug /p:Platform=AnyCPU /nologo diff --git a/Flax.flaxproj b/Flax.flaxproj index a77fe9485..d6cf594a8 100644 --- a/Flax.flaxproj +++ b/Flax.flaxproj @@ -4,7 +4,7 @@ "Major": 1, "Minor": 12, "Revision": 0, - "Build": 6908 + "Build": 6909 }, "Company": "Flax", "Copyright": "Copyright (c) 2012-2026 Wojciech Figat. All rights reserved.", @@ -14,6 +14,14 @@ "UseCSharp": true, "UseLargeWorlds": false, "UseDotNet": true, - "UseSDL": true + "Windows": { + "UseSDL": false, + }, + "Mac": { + "UseSDL": false, + }, + "Linux": { + "UseSDL": true, + }, } -} \ No newline at end of file +} diff --git a/Source/Editor/Content/Tree/ContentTreeNode.cs b/Source/Editor/Content/Tree/ContentTreeNode.cs index 8629296fc..ca629000d 100644 --- a/Source/Editor/Content/Tree/ContentTreeNode.cs +++ b/Source/Editor/Content/Tree/ContentTreeNode.cs @@ -175,15 +175,13 @@ namespace FlaxEditor.Content } } - bool isExpanded = isAnyChildVisible; - - if (isExpanded) + if (!noFilter) { - Expand(true); - } - else - { - Collapse(true); + bool isExpanded = isAnyChildVisible; + if (isExpanded) + Expand(true); + else + Collapse(true); } Visible = isThisVisible | isAnyChildVisible; diff --git a/Source/Editor/CustomEditors/CustomEditor.cs b/Source/Editor/CustomEditors/CustomEditor.cs index ff9a14ea3..253fbc590 100644 --- a/Source/Editor/CustomEditors/CustomEditor.cs +++ b/Source/Editor/CustomEditors/CustomEditor.cs @@ -52,6 +52,7 @@ namespace FlaxEditor.CustomEditors private readonly List _children = new List(); private ValueContainer _values; private bool _isSetBlocked; + private bool _isRebuilding; private bool _skipChildrenRefresh; private bool _hasValueDirty; private bool _rebuildOnRefresh; @@ -178,7 +179,7 @@ namespace FlaxEditor.CustomEditors public void RebuildLayout() { // Skip rebuilding during init - if (CurrentCustomEditor == this) + if (CurrentCustomEditor == this || _isRebuilding) return; // Special case for root objects to run normal layout build @@ -197,6 +198,7 @@ namespace FlaxEditor.CustomEditors _parent?.RebuildLayout(); return; } + _isRebuilding = true; var control = layout.ContainerControl; var parent = _parent; var parentScrollV = (_presenter?.Panel.Parent as Panel)?.VScrollBar?.Value ?? -1; @@ -216,6 +218,7 @@ namespace FlaxEditor.CustomEditors // Restore scroll value if (parentScrollV > -1 && _presenter != null && _presenter.Panel.Parent is Panel panel && panel.VScrollBar != null) panel.VScrollBar.Value = parentScrollV; + _isRebuilding = false; } /// diff --git a/Source/Editor/Editor.Build.cs b/Source/Editor/Editor.Build.cs index 5dad98458..7e8b03f6e 100644 --- a/Source/Editor/Editor.Build.cs +++ b/Source/Editor/Editor.Build.cs @@ -61,6 +61,7 @@ public class Editor : EditorModule options.PrivateDependencies.Add("Renderer"); options.PrivateDependencies.Add("TextureTool"); options.PrivateDependencies.Add("Particles"); + options.PrivateDependencies.Add("Terrain"); var platformToolsRoot = Path.Combine(FolderPath, "Cooker", "Platform"); var platformToolsRootExternal = Path.Combine(Globals.EngineRoot, "Source", "Platforms"); diff --git a/Source/Editor/GUI/Docking/FloatWindowDockPanel.cs b/Source/Editor/GUI/Docking/FloatWindowDockPanel.cs index e53b9000b..9dfc2a896 100644 --- a/Source/Editor/GUI/Docking/FloatWindowDockPanel.cs +++ b/Source/Editor/GUI/Docking/FloatWindowDockPanel.cs @@ -135,11 +135,7 @@ namespace FlaxEditor.GUI.Docking settings.MaximumSize = Float2.Zero; // Unlimited size settings.Fullscreen = false; settings.HasBorder = true; -#if PLATFORM_SDL settings.SupportsTransparency = true; -#else - settings.SupportsTransparency = false; -#endif settings.ActivateWhenFirstShown = true; settings.AllowInput = true; settings.AllowMinimize = true; @@ -211,6 +207,7 @@ namespace FlaxEditor.GUI.Docking { if (ChildPanelsCount > 0) return; + // Close window _window?.Close(); } diff --git a/Source/Editor/GUI/Docking/WindowDragHelper.cs b/Source/Editor/GUI/Docking/WindowDragHelper.cs index 854d5628e..42ae619f6 100644 --- a/Source/Editor/GUI/Docking/WindowDragHelper.cs +++ b/Source/Editor/GUI/Docking/WindowDragHelper.cs @@ -269,8 +269,9 @@ namespace FlaxEditor.GUI.Docking if (_toDock == null) return; - if (_toDock.RootWindow.Window != _dragSourceWindow) - _toDock.RootWindow.Window.MouseUp -= OnMouseUp; + var window = _toDock.RootWindow?.Window; + if (window != null && window != _dragSourceWindow) + window.MouseUp -= OnMouseUp; _dockHintDown?.Parent.RemoveChild(_dockHintDown); _dockHintUp?.Parent.RemoveChild(_dockHintUp); @@ -327,10 +328,10 @@ namespace FlaxEditor.GUI.Docking _toDock?.RootWindow.Window.BringToFront(); //_toDock?.RootWindow.Window.Focus(); -#if PLATFORM_SDL // Make the dragged window transparent when dock hints are visible _toMove.Window.Window.Opacity = _toDock == null ? 1.0f : DragWindowOpacity; -#else + +#if !PLATFORM_SDL // Bring the drop source always to the top if (_dragSourceWindow != null) _dragSourceWindow.BringToFront(); diff --git a/Source/Editor/Gizmo/DirectionGizmo.cs b/Source/Editor/Gizmo/DirectionGizmo.cs new file mode 100644 index 000000000..babebbd5b --- /dev/null +++ b/Source/Editor/Gizmo/DirectionGizmo.cs @@ -0,0 +1,267 @@ +// Copyright (c) Wojciech Figat. All rights reserved. + +using System.Collections.Generic; +using FlaxEditor.Viewport; +using FlaxEngine; +using FlaxEngine.GUI; + +namespace FlaxEditor.Gizmo; + +[HideInEditor] +internal class DirectionGizmo : ContainerControl +{ + private IGizmoOwner _owner; + private ViewportProjection _viewportProjection; + private EditorViewport _viewport; + private Vector3 _gizmoCenter; + private float _axisLength = 75.0f; + private float _textAxisLength = 95.0f; + private float _spriteRadius = 12.0f; + + private AxisData _xAxisData; + private AxisData _yAxisData; + private AxisData _zAxisData; + private AxisData _negXAxisData; + private AxisData _negYAxisData; + private AxisData _negZAxisData; + + private List _axisData = new List(); + private int _hoveredAxisIndex = -1; + + private SpriteHandle _posHandle; + private SpriteHandle _negHandle; + + private FontReference _fontReference; + + // Store sprite positions for hover detection + private List<(Float2 position, AxisDirection direction)> _spritePositions = new List<(Float2, AxisDirection)>(); + + private struct ViewportProjection + { + private Matrix _viewProjection; + private BoundingFrustum _frustum; + private FlaxEngine.Viewport _viewport; + private Vector3 _origin; + + public void Init(EditorViewport editorViewport) + { + // Inline EditorViewport.ProjectPoint to save on calculation for large set of points + _viewport = new FlaxEngine.Viewport(0, 0, editorViewport.Width, editorViewport.Height); + _frustum = editorViewport.ViewFrustum; + _viewProjection = _frustum.Matrix; + _origin = editorViewport.Task.View.Origin; + } + + public void ProjectPoint(Vector3 worldSpaceLocation, out Float2 viewportSpaceLocation) + { + worldSpaceLocation -= _origin; + _viewport.Project(ref worldSpaceLocation, ref _viewProjection, out var projected); + viewportSpaceLocation = new Float2((float)projected.X, (float)projected.Y); + } + } + + private struct AxisData + { + public Float2 Delta; + public float Distance; + public string Label; + public Color AxisColor; + public bool Negative; + public AxisDirection Direction; + } + + private enum AxisDirection + { + PosX, + PosY, + PosZ, + NegX, + NegY, + NegZ + } + + /// + /// Constructor of the Direction Gizmo + /// + /// The owner of this object. + public DirectionGizmo(IGizmoOwner owner) + { + _owner = owner; + _viewport = owner.Viewport; + _viewportProjection.Init(owner.Viewport); + + _xAxisData = new AxisData { Delta = new Float2(0, 0), Distance = 0, Label = "X", AxisColor = new Color(1.0f, 0.0f, 0.02745f, 1.0f), Negative = false, Direction = AxisDirection.PosX }; + _yAxisData = new AxisData { Delta = new Float2(0, 0), Distance = 0, Label = "Y", AxisColor = new Color(0.239215f, 1.0f, 0.047058f, 1.0f), Negative = false, Direction = AxisDirection.PosY }; + _zAxisData = new AxisData { Delta = new Float2(0, 0), Distance = 0, Label = "Z", AxisColor = new Color(0.0f, 0.0235294f, 1.0f, 1.0f), Negative = false, Direction = AxisDirection.PosZ }; + + _negXAxisData = new AxisData { Delta = new Float2(0, 0), Distance = 0, Label = "-X", AxisColor = new Color(1.0f, 0.0f, 0.02745f, 1.0f), Negative = true, Direction = AxisDirection.NegX }; + _negYAxisData = new AxisData { Delta = new Float2(0, 0), Distance = 0, Label = "-Y", AxisColor = new Color(0.239215f, 1.0f, 0.047058f, 1.0f), Negative = true, Direction = AxisDirection.NegY }; + _negZAxisData = new AxisData { Delta = new Float2(0, 0), Distance = 0, Label = "-Z", AxisColor = new Color(0.0f, 0.0235294f, 1.0f, 1.0f), Negative = true, Direction = AxisDirection.NegZ }; + _axisData.EnsureCapacity(6); + _spritePositions.EnsureCapacity(6); + + _posHandle = Editor.Instance.Icons.VisjectBoxClosed32; + _negHandle = Editor.Instance.Icons.VisjectBoxOpen32; + + _fontReference = new FontReference(Style.Current.FontSmall); + _fontReference.Size = 8; + } + + private bool IsPointInSprite(Float2 point, Float2 spriteCenter) + { + Float2 delta = point - spriteCenter; + float distanceSq = delta.LengthSquared; + float radiusSq = _spriteRadius * _spriteRadius; + return distanceSq <= radiusSq; + } + + /// + public override void OnMouseMove(Float2 location) + { + _hoveredAxisIndex = -1; + + // Check which axis is being hovered - check from closest to farthest for proper layering + for (int i = _spritePositions.Count - 1; i >= 0; i--) + { + if (IsPointInSprite(location, _spritePositions[i].position)) + { + _hoveredAxisIndex = i; + break; + } + } + + base.OnMouseMove(location); + } + + /// + public override bool OnMouseDown(Float2 location, MouseButton button) + { + if (base.OnMouseDown(location, button)) + return true; + + // Check which axis is being clicked - check from closest to farthest for proper layering + for (int i = _spritePositions.Count - 1; i >= 0; i--) + { + if (IsPointInSprite(location, _spritePositions[i].position)) + { + OrientViewToAxis(_spritePositions[i].direction); + return true; + } + } + + return false; + } + + private void OrientViewToAxis(AxisDirection direction) + { + Quaternion orientation = direction switch + { + AxisDirection.PosX => Quaternion.Euler(0, 90, 0), + AxisDirection.NegX => Quaternion.Euler(0, -90, 0), + AxisDirection.PosY => Quaternion.Euler(-90, 0, 0), + AxisDirection.NegY => Quaternion.Euler(90, 0, 0), + AxisDirection.PosZ => Quaternion.Euler(0, 0, 0), + AxisDirection.NegZ => Quaternion.Euler(0, 180, 0), + _ => Quaternion.Identity + }; + _viewport.OrientViewport(ref orientation); + } + + /// + /// Used to Draw the gizmo. + /// + public override void DrawSelf() + { + base.DrawSelf(); + + var features = Render2D.Features; + Render2D.Features = features & ~Render2D.RenderingFeatures.VertexSnapping; + _viewportProjection.Init(_owner.Viewport); + _gizmoCenter = _viewport.Task.View.WorldPosition + _viewport.Task.View.Direction * 1500; + _viewportProjection.ProjectPoint(_gizmoCenter, out var gizmoCenterScreen); + + var relativeCenter = Size * 0.5f; + + // Project unit vectors + _viewportProjection.ProjectPoint(_gizmoCenter + Vector3.Right, out var xProjected); + _viewportProjection.ProjectPoint(_gizmoCenter + Vector3.Up, out var yProjected); + _viewportProjection.ProjectPoint(_gizmoCenter + Vector3.Forward, out var zProjected); + _viewportProjection.ProjectPoint(_gizmoCenter - Vector3.Right, out var negXProjected); + _viewportProjection.ProjectPoint(_gizmoCenter - Vector3.Up, out var negYProjected); + _viewportProjection.ProjectPoint(_gizmoCenter - Vector3.Forward, out var negZProjected); + + // Normalize by viewport height to keep size independent of FOV and viewport dimensions + float heightNormalization = _viewport.Height / 720.0f; // 720 = reference height + if (_owner.Viewport.UseOrthographicProjection) + heightNormalization /= _owner.Viewport.OrthographicScale * 0.5f; // Fix in ortho view to keep consistent size regardless of zoom level + + Float2 xDelta = (xProjected - gizmoCenterScreen) / heightNormalization; + Float2 yDelta = (yProjected - gizmoCenterScreen) / heightNormalization; + Float2 zDelta = (zProjected - gizmoCenterScreen) / heightNormalization; + Float2 negXDelta = (negXProjected - gizmoCenterScreen) / heightNormalization; + Float2 negYDelta = (negYProjected - gizmoCenterScreen) / heightNormalization; + Float2 negZDelta = (negZProjected - gizmoCenterScreen) / heightNormalization; + + // Calculate distances from camera to determine draw order + Vector3 cameraPosition = _viewport.Task.View.Position; + float xDistance = (float)Vector3.Distance(cameraPosition, _gizmoCenter + Vector3.Right); + float yDistance = (float)Vector3.Distance(cameraPosition, _gizmoCenter + Vector3.Up); + float zDistance = (float)Vector3.Distance(cameraPosition, _gizmoCenter + Vector3.Forward); + float negXDistance = (float)Vector3.Distance(cameraPosition, _gizmoCenter - Vector3.Right); + float negYDistance = (float)Vector3.Distance(cameraPosition, _gizmoCenter - Vector3.Up); + float negZDistance = (float)Vector3.Distance(cameraPosition, _gizmoCenter - Vector3.Forward); + + _xAxisData.Delta = xDelta; + _xAxisData.Distance = xDistance; + _yAxisData.Delta = yDelta; + _yAxisData.Distance = yDistance; + _zAxisData.Delta = zDelta; + _zAxisData.Distance = zDistance; + _negXAxisData.Delta = negXDelta; + _negXAxisData.Distance = negXDistance; + _negYAxisData.Delta = negYDelta; + _negYAxisData.Distance = negYDistance; + _negZAxisData.Delta = negZDelta; + _negZAxisData.Distance = negZDistance; + + // Sort for correct draw order. + _axisData.Clear(); + _axisData.AddRange([_xAxisData, _yAxisData, _zAxisData, _negXAxisData, _negYAxisData, _negZAxisData]); + _axisData.Sort((a, b) => -a.Distance.CompareTo(b.Distance)); + + // Rebuild sprite positions list for hover detection + _spritePositions.Clear(); + + Render2D.DrawSprite(_posHandle, new Rectangle(0, 0, Size), Color.Black.AlphaMultiplied(0.1f)); + + // Draw in order from farthest to closest + for (int i = 0; i < _axisData.Count; i++) + { + var axis = _axisData[i]; + Float2 tipScreen = relativeCenter + axis.Delta * _axisLength; + Float2 tipTextScreen = relativeCenter + axis.Delta * _textAxisLength; + bool isHovered = _hoveredAxisIndex == i; + + // Store sprite position for hover detection + _spritePositions.Add((tipTextScreen, axis.Direction)); + + var axisColor = isHovered ? new Color(1.0f, 0.8980392f, 0.039215688f) : axis.AxisColor; + var font = _fontReference.GetFont(); + if (!axis.Negative) + { + Render2D.DrawLine(relativeCenter, tipScreen, axisColor, 2.0f); + Render2D.DrawSprite(_posHandle, new Rectangle(tipTextScreen - new Float2(_spriteRadius), new Float2(_spriteRadius * 2)), axisColor); + Render2D.DrawText(font, axis.Label, isHovered ? Color.Gray : Color.Black, tipTextScreen - font.MeasureText(axis.Label) * 0.5f); + } + else + { + Render2D.DrawSprite(_posHandle, new Rectangle(tipTextScreen - new Float2(_spriteRadius), new Float2(_spriteRadius * 2)), axisColor.RGBMultiplied(0.65f)); + Render2D.DrawSprite(_negHandle, new Rectangle(tipTextScreen - new Float2(_spriteRadius), new Float2(_spriteRadius * 2)), axisColor); + if (isHovered) + Render2D.DrawText(font, axis.Label, Color.Black, tipTextScreen - font.MeasureText(axis.Label) * 0.5f); + } + } + + Render2D.Features = features; + } +} diff --git a/Source/Editor/Options/InputOptions.cs b/Source/Editor/Options/InputOptions.cs index 9b15c0c09..82ccc21d5 100644 --- a/Source/Editor/Options/InputOptions.cs +++ b/Source/Editor/Options/InputOptions.cs @@ -710,6 +710,10 @@ namespace FlaxEditor.Options [EditorDisplay("Node Editors"), EditorOrder(4580)] public InputBinding NodesDistributeVertical = new InputBinding(KeyboardKeys.A, KeyboardKeys.Alt); + [DefaultValue(typeof(InputBinding), "Shift+F")] + [EditorDisplay("Node Editors"), EditorOrder(4590)] + public InputBinding FocusSelectedNodes = new InputBinding(KeyboardKeys.F, KeyboardKeys.Shift); + #endregion } } diff --git a/Source/Editor/SceneGraph/Actors/RigidBodyNode.cs b/Source/Editor/SceneGraph/Actors/RigidBodyNode.cs new file mode 100644 index 000000000..9f9a38258 --- /dev/null +++ b/Source/Editor/SceneGraph/Actors/RigidBodyNode.cs @@ -0,0 +1,29 @@ +// Copyright (c) Wojciech Figat. All rights reserved. + +using FlaxEngine; + +namespace FlaxEditor.SceneGraph.Actors +{ + /// + /// Scene tree node for actor type. + /// + [HideInEditor] + public sealed class RigidBodyNode : ActorNode + { + /// + public RigidBodyNode(Actor actor) + : base(actor) + { + } + + /// + public override void PostSpawn() + { + base.PostSpawn(); + + if (HasPrefabLink) + return; + Actor.StaticFlags = StaticFlags.None; + } + } +} diff --git a/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs b/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs index 5f56e918a..4607a4f63 100644 --- a/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs +++ b/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs @@ -324,13 +324,12 @@ namespace FlaxEditor.SceneGraph.GUI isExpanded = Editor.Instance.ProjectCache.IsExpandedActor(ref id); } - if (isExpanded) + if (!noFilter) { - Expand(true); - } - else - { - Collapse(true); + if (isExpanded) + Expand(true); + else + Collapse(true); } Visible = isThisVisible | isAnyChildVisible; diff --git a/Source/Editor/SceneGraph/SceneGraphFactory.cs b/Source/Editor/SceneGraph/SceneGraphFactory.cs index bd465aad2..8437167aa 100644 --- a/Source/Editor/SceneGraph/SceneGraphFactory.cs +++ b/Source/Editor/SceneGraph/SceneGraphFactory.cs @@ -74,6 +74,7 @@ namespace FlaxEditor.SceneGraph CustomNodesTypes.Add(typeof(NavMesh), typeof(ActorNode)); CustomNodesTypes.Add(typeof(SpriteRender), typeof(SpriteRenderNode)); CustomNodesTypes.Add(typeof(Joint), typeof(JointNode)); + CustomNodesTypes.Add(typeof(RigidBody), typeof(RigidBodyNode)); } /// diff --git a/Source/Editor/Surface/Archetypes/Layers.cs b/Source/Editor/Surface/Archetypes/Layers.cs index fe0053f7f..6c4caa6e7 100644 --- a/Source/Editor/Surface/Archetypes/Layers.cs +++ b/Source/Editor/Surface/Archetypes/Layers.cs @@ -52,6 +52,7 @@ namespace FlaxEditor.Surface.Archetypes }, new NodeArchetype { + // [Deprecated] TypeID = 3, Title = "Pack Material Layer", Description = "Pack material properties", @@ -75,6 +76,7 @@ namespace FlaxEditor.Surface.Archetypes }, new NodeArchetype { + // [Deprecated] TypeID = 4, Title = "Unpack Material Layer", Description = "Unpack material properties", @@ -120,6 +122,7 @@ namespace FlaxEditor.Surface.Archetypes TypeID = 6, Title = "Pack Material Layer", Description = "Pack material properties", + AlternativeTitles = new[] { "Make Material Layer", "Construct Material Layer", "Compose Material Layer" }, Flags = NodeFlags.MaterialGraph, Size = new Float2(200, 280), Elements = new[] @@ -146,6 +149,7 @@ namespace FlaxEditor.Surface.Archetypes TypeID = 7, Title = "Unpack Material Layer", Description = "Unpack material properties", + AlternativeTitles = new[] { "Break Material Layer", "Deconstruct Material Layer", "Decompose Material Layer", "Split Material Layer" }, Flags = NodeFlags.MaterialGraph, Size = new Float2(210, 280), Elements = new[] diff --git a/Source/Editor/Surface/Archetypes/Packing.cs b/Source/Editor/Surface/Archetypes/Packing.cs index 363d867f7..8d57bc81e 100644 --- a/Source/Editor/Surface/Archetypes/Packing.cs +++ b/Source/Editor/Surface/Archetypes/Packing.cs @@ -342,6 +342,7 @@ namespace FlaxEditor.Surface.Archetypes TypeID = 20, Title = "Pack Float2", Description = "Pack components to Float2", + AlternativeTitles = new[] { "Make Float2", "Construct Float2", "Compose Float2" }, Flags = NodeFlags.AllGraphs, Size = new Float2(150, 40), DefaultValues = new object[] @@ -361,6 +362,7 @@ namespace FlaxEditor.Surface.Archetypes TypeID = 21, Title = "Pack Float3", Description = "Pack components to Float3", + AlternativeTitles = new[] { "Make Float3", "Construct Float3", "Compose Float3" }, Flags = NodeFlags.AllGraphs, Size = new Float2(150, 60), DefaultValues = new object[] @@ -382,6 +384,7 @@ namespace FlaxEditor.Surface.Archetypes TypeID = 22, Title = "Pack Float4", Description = "Pack components to Float4", + AlternativeTitles = new[] { "Make Float4", "Construct Float4", "Compose Float4" }, Flags = NodeFlags.AllGraphs, Size = new Float2(150, 80), DefaultValues = new object[] @@ -405,6 +408,7 @@ namespace FlaxEditor.Surface.Archetypes TypeID = 23, Title = "Pack Rotation", Description = "Pack components to Rotation", + AlternativeTitles = new[] { "Make Rotation", "Construct Rotation", "Compose Rotation" }, Flags = NodeFlags.AllGraphs, Size = new Float2(150, 60), DefaultValues = new object[] @@ -426,6 +430,7 @@ namespace FlaxEditor.Surface.Archetypes TypeID = 24, Title = "Pack Transform", Description = "Pack components to Transform", + AlternativeTitles = new[] { "Make Transform", "Construct Transform", "Compose Transform" }, Flags = NodeFlags.AllGraphs, Size = new Float2(150, 80), Elements = new[] @@ -441,6 +446,7 @@ namespace FlaxEditor.Surface.Archetypes TypeID = 25, Title = "Pack Box", Description = "Pack components to BoundingBox", + AlternativeTitles = new[] { "Make Box", "Construct Box", "Compose Box" }, Flags = NodeFlags.AllGraphs, Size = new Float2(150, 40), Elements = new[] @@ -454,6 +460,7 @@ namespace FlaxEditor.Surface.Archetypes { TypeID = 26, Title = "Pack Structure", + AlternativeTitles = new[] { "Make Structure", "Construct Structure", "Compose Structure" }, Create = (id, context, arch, groupArch) => new PackStructureNode(id, context, arch, groupArch), IsInputCompatible = PackStructureNode.IsInputCompatible, IsOutputCompatible = PackStructureNode.IsOutputCompatible, @@ -479,6 +486,7 @@ namespace FlaxEditor.Surface.Archetypes TypeID = 30, Title = "Unpack Float2", Description = "Unpack components from Float2", + AlternativeTitles = new[] { "Break Float2", "Deconstruct Float2", "Decompose Float2", "Split Float2" }, Flags = NodeFlags.AllGraphs, Size = new Float2(150, 40), Elements = new[] @@ -493,6 +501,7 @@ namespace FlaxEditor.Surface.Archetypes TypeID = 31, Title = "Unpack Float3", Description = "Unpack components from Float3", + AlternativeTitles = new[] { "Break Float3", "Deconstruct Float3", "Decompose Float3", "Split Float3" }, Flags = NodeFlags.AllGraphs, Size = new Float2(150, 60), Elements = new[] @@ -508,6 +517,7 @@ namespace FlaxEditor.Surface.Archetypes TypeID = 32, Title = "Unpack Float4", Description = "Unpack components from Float4", + AlternativeTitles = new[] { "Break Float4", "Deconstruct Float4", "Decompose Float4", "Split Float4" }, Flags = NodeFlags.AllGraphs, Size = new Float2(150, 80), Elements = new[] @@ -524,6 +534,7 @@ namespace FlaxEditor.Surface.Archetypes TypeID = 33, Title = "Unpack Rotation", Description = "Unpack components from Rotation", + AlternativeTitles = new[] { "Break Rotation", "Deconstruct Rotation", "Decompose Rotation", "Split Rotation" }, Flags = NodeFlags.AllGraphs, Size = new Float2(170, 60), Elements = new[] @@ -539,6 +550,7 @@ namespace FlaxEditor.Surface.Archetypes TypeID = 34, Title = "Unpack Transform", Description = "Unpack components from Transform", + AlternativeTitles = new[] { "Break Transform", "Deconstruct Transform", "Decompose Transform", "Split Transform" }, Flags = NodeFlags.AllGraphs, Size = new Float2(170, 60), Elements = new[] @@ -554,6 +566,7 @@ namespace FlaxEditor.Surface.Archetypes TypeID = 35, Title = "Unpack Box", Description = "Unpack components from BoundingBox", + AlternativeTitles = new[] { "Break BoundingBox", "Deconstruct BoundingBox", "Decompose BoundingBox", "Split BoundingBox" }, Flags = NodeFlags.AllGraphs, Size = new Float2(170, 40), Elements = new[] @@ -572,6 +585,7 @@ namespace FlaxEditor.Surface.Archetypes IsOutputCompatible = UnpackStructureNode.IsOutputCompatible, GetInputOutputDescription = UnpackStructureNode.GetInputOutputDescription, Description = "Breaks the structure data to allow extracting components from it.", + AlternativeTitles = new[] { "Break Structure", "Deconstruct Structure", "Decompose Structure", "Split Structure" }, Flags = NodeFlags.VisualScriptGraph | NodeFlags.AnimGraph | NodeFlags.NoSpawnViaGUI, Size = new Float2(180, 20), DefaultValues = new object[] diff --git a/Source/Editor/Surface/ContextMenu/VisjectCM.cs b/Source/Editor/Surface/ContextMenu/VisjectCM.cs index 21d0e7470..e1a61fd5f 100644 --- a/Source/Editor/Surface/ContextMenu/VisjectCM.cs +++ b/Source/Editor/Surface/ContextMenu/VisjectCM.cs @@ -585,7 +585,7 @@ namespace FlaxEditor.Surface.ContextMenu private void UpdateFilters() { - if (string.IsNullOrEmpty(_searchBox.Text) && _selectedBoxes[0] == null) + if (string.IsNullOrEmpty(_searchBox.Text) && _selectedBoxes.Count == 0) { ResetView(); Profiler.EndEvent(); diff --git a/Source/Editor/Surface/Elements/OutputBox.cs b/Source/Editor/Surface/Elements/OutputBox.cs index 7e271fef0..8836dc0dc 100644 --- a/Source/Editor/Surface/Elements/OutputBox.cs +++ b/Source/Editor/Surface/Elements/OutputBox.cs @@ -34,11 +34,6 @@ namespace FlaxEditor.Surface.Elements /// public const float DefaultConnectionOffset = 24f; - /// - /// Distance for the mouse to be considered above the connection - /// - public float MouseOverConnectionDistance => 100f / Surface.ViewScale; - /// public OutputBox(SurfaceNode parentNode, NodeElementArchetype archetype) : base(parentNode, archetype, archetype.Position + new Float2(parentNode.Archetype.Size.X, 0)) @@ -109,12 +104,13 @@ namespace FlaxEditor.Surface.Elements /// /// The other box. /// The mouse position - public bool IntersectsConnection(Box targetBox, ref Float2 mousePosition) + /// Distance at which its an intersection + public bool IntersectsConnection(Box targetBox, ref Float2 mousePosition, float distance) { float connectionOffset = Mathf.Max(0f, DefaultConnectionOffset * (1 - Editor.Instance.Options.Options.Interface.ConnectionCurvature)); Float2 start = new Float2(ConnectionOrigin.X + connectionOffset, ConnectionOrigin.Y); Float2 end = new Float2(targetBox.ConnectionOrigin.X - connectionOffset, targetBox.ConnectionOrigin.Y); - return IntersectsConnection(ref start, ref end, ref mousePosition, MouseOverConnectionDistance); + return IntersectsConnection(ref start, ref end, ref mousePosition, distance); } /// @@ -182,7 +178,7 @@ namespace FlaxEditor.Surface.Elements { // Draw all the connections var style = Surface.Style; - var mouseOverDistance = MouseOverConnectionDistance; + var mouseOverDistance = Surface.MouseOverConnectionDistance; var startPos = ConnectionOrigin; var startHighlight = ConnectionsHighlightIntensity; for (int i = 0; i < Connections.Count; i++) diff --git a/Source/Editor/Surface/SurfaceUtils.cs b/Source/Editor/Surface/SurfaceUtils.cs index 6851e5d8b..60cd714c4 100644 --- a/Source/Editor/Surface/SurfaceUtils.cs +++ b/Source/Editor/Surface/SurfaceUtils.cs @@ -574,13 +574,13 @@ namespace FlaxEditor.Surface var showSearch = () => editor.ContentFinding.ShowSearch(window); // Toolstrip - saveButton = toolStrip.AddButton(editor.Icons.Save64, window.Save).LinkTooltip("Save", ref inputOptions.Save); + saveButton = toolStrip.AddButton(editor.Icons.Save64, window.Save).LinkTooltip("Save.", ref inputOptions.Save); toolStrip.AddSeparator(); - undoButton = toolStrip.AddButton(editor.Icons.Undo64, undo.PerformUndo).LinkTooltip("Undo", ref inputOptions.Undo); - redoButton = toolStrip.AddButton(editor.Icons.Redo64, undo.PerformRedo).LinkTooltip("Redo", ref inputOptions.Redo); + undoButton = toolStrip.AddButton(editor.Icons.Undo64, undo.PerformUndo).LinkTooltip("Undo.", ref inputOptions.Undo); + redoButton = toolStrip.AddButton(editor.Icons.Redo64, undo.PerformRedo).LinkTooltip("Redo.", ref inputOptions.Redo); toolStrip.AddSeparator(); - toolStrip.AddButton(editor.Icons.Search64, showSearch).LinkTooltip("Open content search tool", ref inputOptions.Search); - toolStrip.AddButton(editor.Icons.CenterView64, surface.ShowWholeGraph).LinkTooltip("Show whole graph"); + toolStrip.AddButton(editor.Icons.Search64, showSearch).LinkTooltip("Open content search tool.", ref inputOptions.Search); + toolStrip.AddButton(editor.Icons.CenterView64, surface.ShowWholeGraph).LinkTooltip("Show whole graph."); var gridSnapButton = toolStrip.AddButton(editor.Icons.Grid32, surface.ToggleGridSnapping); gridSnapButton.LinkTooltip("Toggle grid snapping for nodes."); gridSnapButton.AutoCheck = true; diff --git a/Source/Editor/Surface/VisjectSurface.ContextMenu.cs b/Source/Editor/Surface/VisjectSurface.ContextMenu.cs index d8dfb8ad3..ffb1c725a 100644 --- a/Source/Editor/Surface/VisjectSurface.ContextMenu.cs +++ b/Source/Editor/Surface/VisjectSurface.ContextMenu.cs @@ -410,8 +410,11 @@ namespace FlaxEditor.Surface } menu.AddSeparator(); - _cmFormatNodesMenu = menu.AddChildMenu("Format node(s)"); - _cmFormatNodesMenu.Enabled = CanEdit && HasNodesSelection; + bool allNodesNoMove = SelectedNodes.All(n => n.Archetype.Flags.HasFlag(NodeFlags.NoMove)); + bool clickedNodeNoMove = ((SelectedNodes.Count == 1 && controlUnderMouse is SurfaceNode n && n.Archetype.Flags.HasFlag(NodeFlags.NoMove))); + + _cmFormatNodesMenu = menu.AddChildMenu("Format nodes"); + _cmFormatNodesMenu.Enabled = CanEdit && HasNodesSelection && !(allNodesNoMove || clickedNodeNoMove); _cmFormatNodesConnectionButton = _cmFormatNodesMenu.ContextMenu.AddButton("Auto format", Editor.Instance.Options.Options.Input.NodesAutoFormat, () => { FormatGraph(SelectedNodes); }); _cmFormatNodesConnectionButton = _cmFormatNodesMenu.ContextMenu.AddButton("Straighten connections", Editor.Instance.Options.Options.Input.NodesStraightenConnections, () => { StraightenGraphConnections(SelectedNodes); }); diff --git a/Source/Editor/Surface/VisjectSurface.Draw.cs b/Source/Editor/Surface/VisjectSurface.Draw.cs index af5893907..a7d390132 100644 --- a/Source/Editor/Surface/VisjectSurface.Draw.cs +++ b/Source/Editor/Surface/VisjectSurface.Draw.cs @@ -213,6 +213,44 @@ namespace FlaxEditor.Surface } } + /// + /// Draw connection hints for lazy connect feature. + /// + protected virtual void DrawLazyConnect() + { + var style = FlaxEngine.GUI.Style.Current; + + if (_lazyConnectStartNode != null) + { + Float2 upperLeft = _rootControl.PointToParent(_lazyConnectStartNode.UpperLeft); + Rectangle startNodeOutline = new Rectangle(upperLeft + 1f, _lazyConnectStartNode.Size - 1f); + startNodeOutline.Size *= ViewScale; + Render2D.DrawRectangle(startNodeOutline.MakeExpanded(4f), style.BackgroundSelected, 4f); + } + + if (_lazyConnectEndNode != null) + { + Float2 upperLeft = _rootControl.PointToParent(_lazyConnectEndNode.UpperLeft); + Rectangle startNodeOutline = new Rectangle(upperLeft + 1f, _lazyConnectEndNode.Size - 1f); + startNodeOutline.Size *= ViewScale; + Render2D.DrawRectangle(startNodeOutline.MakeExpanded(4f), style.BackgroundSelected, 4f); + } + + Rectangle startRect = new Rectangle(_rightMouseDownPos - 6f, new Float2(12f)); + Rectangle endRect = new Rectangle(_mousePos - 6f, new Float2(12f)); + + // Start and end shadows/ outlines + Render2D.FillRectangle(startRect.MakeExpanded(2.5f), Color.Black); + Render2D.FillRectangle(endRect.MakeExpanded(2.5f), Color.Black); + + Render2D.DrawLine(_rightMouseDownPos, _mousePos, Color.Black, 7.5f); + Render2D.DrawLine(_rightMouseDownPos, _mousePos, style.ForegroundGrey, 5f); + + // Draw start and end boxes over the lines to hide ugly artifacts at the ends + Render2D.FillRectangle(startRect, style.ForegroundGrey); + Render2D.FillRectangle(endRect, style.ForegroundGrey); + } + /// /// Draws the contents of the surface (nodes, connections, comments, etc.). /// @@ -260,6 +298,9 @@ namespace FlaxEditor.Surface DrawContents(); + if (_isLazyConnecting) + DrawLazyConnect(); + //Render2D.DrawText(style.FontTitle, string.Format("Scale: {0}", _rootControl.Scale), rect, Enabled ? Color.Red : Color.Black); // Draw border diff --git a/Source/Editor/Surface/VisjectSurface.Formatting.cs b/Source/Editor/Surface/VisjectSurface.Formatting.cs index e1b9a6777..e55a4eb8a 100644 --- a/Source/Editor/Surface/VisjectSurface.Formatting.cs +++ b/Source/Editor/Surface/VisjectSurface.Formatting.cs @@ -39,6 +39,8 @@ namespace FlaxEditor.Surface if (nodes.Count <= 1) return; + List undoActions = new List(); + var nodesToVisit = new HashSet(nodes); // While we haven't formatted every node @@ -73,18 +75,23 @@ namespace FlaxEditor.Surface } } - FormatConnectedGraph(connectedNodes); + undoActions.AddRange(FormatConnectedGraph(connectedNodes)); } + + Undo?.AddAction(new MultiUndoAction(undoActions, "Format nodes")); + MarkAsEdited(false); } /// /// Formats a graph where all nodes are connected. /// /// List of connected nodes. - protected void FormatConnectedGraph(List nodes) + private List FormatConnectedGraph(List nodes) { + List undoActions = new List(); + if (nodes.Count <= 1) - return; + return undoActions; var boundingBox = GetNodesBounds(nodes); @@ -140,7 +147,6 @@ namespace FlaxEditor.Surface } // Set the node positions - var undoActions = new List(); var topRightPosition = endNodes[0].Location; for (int i = 0; i < nodes.Count; i++) { @@ -155,16 +161,18 @@ namespace FlaxEditor.Surface } } - MarkAsEdited(false); - Undo?.AddAction(new MultiUndoAction(undoActions, "Format nodes")); + return undoActions; } /// /// Straightens every connection between nodes in . /// /// List of nodes. + /// List of undo actions. public void StraightenGraphConnections(List nodes) - { + { + nodes = nodes.Where(n => !n.Archetype.Flags.HasFlag(NodeFlags.NoMove)).ToList(); + if (nodes.Count <= 1) return; @@ -350,8 +358,10 @@ namespace FlaxEditor.Surface /// List of nodes. /// Alignemnt type. public void AlignNodes(List nodes, NodeAlignmentType alignmentType) - { - if(nodes.Count <= 1) + { + nodes = nodes.Where(n => !n.Archetype.Flags.HasFlag(NodeFlags.NoMove)).ToList(); + + if (nodes.Count <= 1) return; var undoActions = new List(); @@ -392,6 +402,8 @@ namespace FlaxEditor.Surface /// If false will be done horizontally, if true will be done vertically. public void DistributeNodes(List nodes, bool vertically) { + nodes = nodes.Where(n => !n.Archetype.Flags.HasFlag(NodeFlags.NoMove)).ToList(); + if(nodes.Count <= 1) return; diff --git a/Source/Editor/Surface/VisjectSurface.Input.cs b/Source/Editor/Surface/VisjectSurface.Input.cs index 056987e52..c2f474865 100644 --- a/Source/Editor/Surface/VisjectSurface.Input.cs +++ b/Source/Editor/Surface/VisjectSurface.Input.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; using System.Linq; +using static FlaxEditor.Surface.Archetypes.Particles; using FlaxEditor.Options; using FlaxEditor.Surface.Elements; using FlaxEditor.Surface.Undo; @@ -23,12 +24,26 @@ namespace FlaxEditor.Surface /// public bool PanWithMiddleMouse = false; + /// + /// Distance for the mouse to be considered above the connection. + /// + public float MouseOverConnectionDistance => 100f / ViewScale; + + /// + /// Distance of a node from which it is able to be slotted into an existing connection. + /// + public float SlotNodeIntoConnectionDistance => 250f / ViewScale; + private string _currentInputText = string.Empty; private Float2 _movingNodesDelta; private Float2 _gridRoundingDelta; private HashSet _movingNodes; private HashSet _temporarySelectedNodes; private readonly Stack _inputBrackets = new Stack(); + private bool _isLazyConnecting; + private SurfaceNode _lazyConnectStartNode; + private SurfaceNode _lazyConnectEndNode; + private InputBinding _focusSelectedNodeBinding; private class InputBracket { @@ -250,8 +265,13 @@ namespace FlaxEditor.Surface // Cache mouse location _mousePos = location; + if (_isLazyConnecting && GetControlUnderMouse() is SurfaceNode nodeUnderMouse && !(nodeUnderMouse is SurfaceComment || nodeUnderMouse is ParticleEmitterNode)) + _lazyConnectEndNode = nodeUnderMouse; + else if (_isLazyConnecting && Nodes.Count > 0) + _lazyConnectEndNode = GetClosestNodeAtLocation(location); + // Moving around surface with mouse - if (_rightMouseDown) + if (_rightMouseDown && !_isLazyConnecting) { // Calculate delta var delta = location - _rightMouseDownPos; @@ -321,6 +341,33 @@ namespace FlaxEditor.Surface foreach (var node in _movingNodes) { + // Allow ripping the node from its current connection + if (RootWindow.GetKey(KeyboardKeys.Alt)) + { + InputBox nodeConnectedInput = null; + OutputBox nodeConnectedOuput = null; + + var boxes = node.GetBoxes(); + foreach (var box in boxes) + { + if (!box.IsOutput && box.Connections.Count > 0) + { + nodeConnectedInput = (InputBox)box; + continue; + } + if (box.IsOutput && box.Connections.Count > 0) + { + nodeConnectedOuput = (OutputBox)box; + continue; + } + } + + if (nodeConnectedInput != null && nodeConnectedOuput != null) + TryConnect(nodeConnectedOuput.Connections[0], nodeConnectedInput.Connections[0]); + + node.RemoveConnections(); + } + if (gridSnap) { Float2 unroundedLocation = node.Location; @@ -420,7 +467,7 @@ namespace FlaxEditor.Surface if (!handled && CanEdit && CanUseNodeType(7, 29)) { var mousePos = _rootControl.PointFromParent(ref _mousePos); - if (IntersectsConnection(mousePos, out InputBox inputBox, out OutputBox outputBox) && GetControlUnderMouse() == null) + if (IntersectsConnection(mousePos, out InputBox inputBox, out OutputBox outputBox, MouseOverConnectionDistance) && GetControlUnderMouse() == null) { if (Undo != null) { @@ -515,11 +562,17 @@ namespace FlaxEditor.Surface _middleMouseDownPos = location; } + if (root.GetKey(KeyboardKeys.Alt) && button == MouseButton.Right) + _isLazyConnecting = true; + // Check if any node is under the mouse SurfaceControl controlUnderMouse = GetControlUnderMouse(); var cLocation = _rootControl.PointFromParent(ref location); if (controlUnderMouse != null) { + if (controlUnderMouse is SurfaceNode node && _isLazyConnecting && !(controlUnderMouse is SurfaceComment || controlUnderMouse is ParticleEmitterNode)) + _lazyConnectStartNode = node; + // Check if mouse is over header and user is pressing mouse left button if (_leftMouseDown && controlUnderMouse.CanSelect(ref cLocation)) { @@ -554,6 +607,9 @@ namespace FlaxEditor.Surface } else { + if (_isLazyConnecting && Nodes.Count > 0) + _lazyConnectStartNode = GetClosestNodeAtLocation(location); + // Cache flags and state if (_leftMouseDown) { @@ -602,8 +658,71 @@ namespace FlaxEditor.Surface { if (_movingNodes != null && _movingNodes.Count > 0) { + // Allow dropping a single node onto an existing connection and connect it + if (_movingNodes.Count == 1) + { + var mousePos = _rootControl.PointFromParent(ref _mousePos); + InputBox intersectedConnectionInputBox; + OutputBox intersectedConnectionOutputBox; + if (IntersectsConnection(mousePos, out intersectedConnectionInputBox, out intersectedConnectionOutputBox, SlotNodeIntoConnectionDistance)) + { + SurfaceNode node = _movingNodes.First(); + InputBox nodeInputBox = (InputBox)node.GetBoxes().First(b => !b.IsOutput); + OutputBox nodeOutputBox = (OutputBox)node.GetBoxes().First(b => b.IsOutput); + TryConnect(intersectedConnectionOutputBox, nodeInputBox); + TryConnect(nodeOutputBox, intersectedConnectionInputBox); + + float intersectedConnectionNodesXDistance = intersectedConnectionInputBox.ParentNode.Left - intersectedConnectionOutputBox.ParentNode.Right; + float paddedNodeWidth = node.Width + 2f; + if (intersectedConnectionNodesXDistance < paddedNodeWidth) + { + List visitedNodes = new List{ node }; + List movedNodes = new List(); + Float2 locationDelta = new Float2(paddedNodeWidth, 0f); + + MoveConnectedNodes(intersectedConnectionInputBox.ParentNode); + + void MoveConnectedNodes(SurfaceNode node) + { + // Only move node if it is to the right of the node we have connected the moved node to + if (node.Right > intersectedConnectionInputBox.ParentNode.Left + 15f && !node.Archetype.Flags.HasFlag(NodeFlags.NoMove)) + { + node.Location += locationDelta; + movedNodes.Add(node); + } + + visitedNodes.Add(node); + + foreach (var box in node.GetBoxes()) + { + if (!box.HasAnyConnection || box == intersectedConnectionInputBox) + continue; + + foreach (var connectedBox in box.Connections) + { + SurfaceNode nextNode = connectedBox.ParentNode; + if (visitedNodes.Contains(nextNode)) + continue; + + MoveConnectedNodes(nextNode); + } + } + } + + Float2 nodeMoveOffset = new Float2(node.Width * 0.5f, 0f); + node.Location += nodeMoveOffset; + + var moveNodesAction = new MoveNodesAction(Context, movedNodes.Select(n => n.ID).ToArray(), locationDelta); + var moveNodeAction = new MoveNodesAction(Context, [node.ID], nodeMoveOffset); + var multiAction = new MultiUndoAction(moveNodeAction, moveNodesAction); + + AddBatchedUndoAction(multiAction); + } + } + } + if (Undo != null && !_movingNodesDelta.IsZero && CanEdit) - Undo.AddAction(new MoveNodesAction(Context, _movingNodes.Select(x => x.ID).ToArray(), _movingNodesDelta)); + AddBatchedUndoAction(new MoveNodesAction(Context, _movingNodes.Select(x => x.ID).ToArray(), _movingNodesDelta)); _movingNodes.Clear(); } _movingNodesDelta = Float2.Zero; @@ -630,12 +749,36 @@ namespace FlaxEditor.Surface { // Check if any control is under the mouse _cmStartPos = location; - if (controlUnderMouse == null) + if (controlUnderMouse == null && !_isLazyConnecting) { showPrimaryMenu = true; } } _mouseMoveAmount = 0; + + if (_isLazyConnecting) + { + if (_lazyConnectStartNode != null && _lazyConnectEndNode != null && _lazyConnectStartNode != _lazyConnectEndNode) + { + // First check if there is a type matching input and output where input + OutputBox startNodeOutput = (OutputBox)_lazyConnectStartNode.GetBoxes().FirstOrDefault(b => b.IsOutput, null); + InputBox endNodeInput = null; + + if (startNodeOutput != null) + endNodeInput = (InputBox)_lazyConnectEndNode.GetBoxes().FirstOrDefault(b => !b.IsOutput && b.CurrentType == startNodeOutput.CurrentType && !b.HasAnyConnection && b.IsActive && b.CanConnectWith(startNodeOutput), null); + + // Perform less strict checks (less ideal conditions for connection but still good) if the first checks failed + if (endNodeInput == null) + endNodeInput = (InputBox)_lazyConnectEndNode.GetBoxes().FirstOrDefault(b => !b.IsOutput && !b.HasAnyConnection && b.CanConnectWith(startNodeOutput), null); + + if (startNodeOutput != null && endNodeInput != null) + TryConnect(startNodeOutput, endNodeInput); + } + + _isLazyConnecting = false; + _lazyConnectStartNode = null; + _lazyConnectEndNode = null; + } } if (_middleMouseDown && button == MouseButton.Middle) { @@ -651,7 +794,7 @@ namespace FlaxEditor.Surface { // Surface was not moved with MMB so try to remove connection underneath var mousePos = _rootControl.PointFromParent(ref location); - if (IntersectsConnection(mousePos, out InputBox inputBox, out OutputBox outputBox)) + if (IntersectsConnection(mousePos, out InputBox inputBox, out OutputBox outputBox, MouseOverConnectionDistance)) { var action = new EditNodeConnections(inputBox.ParentNode.Context, inputBox.ParentNode); inputBox.BreakConnection(outputBox); @@ -704,13 +847,21 @@ namespace FlaxEditor.Surface private void MoveSelectedNodes(Float2 delta) { - // TODO: undo + List undoActions = new List(); + delta /= _targetScale; OnGetNodesToMove(); foreach (var node in _movingNodes) + { node.Location += delta; + if (Undo != null) + undoActions.Add(new MoveNodesAction(Context, new[] { node.ID }, delta)); + } _isMovingSelection = false; MarkAsEdited(false); + + if (undoActions.Count > 0) + Undo?.AddAction(new MultiUndoAction(undoActions, "Moved ")); } /// @@ -837,6 +988,29 @@ namespace FlaxEditor.Surface return false; } + private SurfaceNode GetClosestNodeAtLocation(Float2 location) + { + SurfaceNode currentClosestNode = null; + float currentClosestDistanceSquared = float.MaxValue; + + foreach (var node in Nodes) + { + if (node is SurfaceComment || node is ParticleEmitterNode) + continue; + + Float2 nodeSurfaceLocation = _rootControl.PointToParent(node.Center); + + float distanceSquared = Float2.DistanceSquared(location, nodeSurfaceLocation); + if (distanceSquared < currentClosestDistanceSquared) + { + currentClosestNode = node; + currentClosestDistanceSquared = distanceSquared; + } + } + + return currentClosestNode; + } + private void ResetInput() { InputText = ""; @@ -845,7 +1019,8 @@ namespace FlaxEditor.Surface private void CurrentInputTextChanged(string currentInputText) { - if (string.IsNullOrEmpty(currentInputText)) + // Check if focus selected nodes binding is being pressed to prevent it triggering primary menu + if (string.IsNullOrEmpty(currentInputText) || _focusSelectedNodeBinding.Process(RootWindow)) return; if (IsPrimaryMenuOpened || !CanEdit) { @@ -1025,7 +1200,7 @@ namespace FlaxEditor.Surface return new Float2(xLocation, yLocation); } - private bool IntersectsConnection(Float2 mousePosition, out InputBox inputBox, out OutputBox outputBox) + private bool IntersectsConnection(Float2 mousePosition, out InputBox inputBox, out OutputBox outputBox, float distance) { for (int i = 0; i < Nodes.Count; i++) { @@ -1035,7 +1210,7 @@ namespace FlaxEditor.Surface { for (int k = 0; k < ob.Connections.Count; k++) { - if (ob.IntersectsConnection(ob.Connections[k], ref mousePosition)) + if (ob.IntersectsConnection(ob.Connections[k], ref mousePosition, distance)) { outputBox = ob; inputBox = ob.Connections[k] as InputBox; diff --git a/Source/Editor/Surface/VisjectSurface.cs b/Source/Editor/Surface/VisjectSurface.cs index c4a8d31d3..9f326f36c 100644 --- a/Source/Editor/Surface/VisjectSurface.cs +++ b/Source/Editor/Surface/VisjectSurface.cs @@ -423,8 +423,9 @@ namespace FlaxEditor.Surface new InputActionsContainer.Binding(options => options.NodesAlignLeft, () => { AlignNodes(SelectedNodes, NodeAlignmentType.Left); }), new InputActionsContainer.Binding(options => options.NodesAlignCenter, () => { AlignNodes(SelectedNodes, NodeAlignmentType.Center); }), new InputActionsContainer.Binding(options => options.NodesAlignRight, () => { AlignNodes(SelectedNodes, NodeAlignmentType.Right); }), - new InputActionsContainer.Binding(options => options.NodesDistributeHorizontal, () => { DistributeNodes(SelectedNodes, false); }), - new InputActionsContainer.Binding(options => options.NodesDistributeVertical, () => { DistributeNodes(SelectedNodes, true); }), + new InputActionsContainer.Binding(options => options.NodesDistributeHorizontal, () => { DistributeNodes(SelectedNodes, false); }), + new InputActionsContainer.Binding(options => options.NodesDistributeVertical, () => { DistributeNodes(SelectedNodes, true); }), + new InputActionsContainer.Binding(options => options.FocusSelectedNodes, () => { FocusSelectionOrWholeGraph(); }), }); Context.ControlSpawned += OnSurfaceControlSpawned; @@ -436,7 +437,10 @@ namespace FlaxEditor.Surface DragHandlers.Add(_dragAssets = new DragAssets(ValidateDragItem)); DragHandlers.Add(_dragParameters = new DragNames(SurfaceParameter.DragPrefix, ValidateDragParameter)); + OnEditorOptionsChanged(Editor.Instance.Options.Options); + ScriptsBuilder.ScriptsReloadBegin += OnScriptsReloadBegin; + Editor.Instance.Options.OptionsChanged += OnEditorOptionsChanged; } private void OnScriptsReloadBegin() @@ -446,6 +450,11 @@ namespace FlaxEditor.Surface _cmPrimaryMenu = null; } + private void OnEditorOptionsChanged(EditorOptions options) + { + _focusSelectedNodeBinding = options.Input.FocusSelectedNodes; + } + /// /// Gets the display name of the connection type used in the surface. /// @@ -648,6 +657,37 @@ namespace FlaxEditor.Surface ViewCenterPosition = areaRect.Center; } + /// + /// Adjusts the view to focus on the currently selected nodes, or the entire graph if no nodes are selected. + /// + public void FocusSelectionOrWholeGraph() + { + if (SelectedNodes.Count > 0) + ShowSelection(); + else + ShowWholeGraph(); + } + + /// + /// Shows the selected controls by changing the view scale and the position. + /// + public void ShowSelection() + { + var selection = SelectedControls; + if (selection.Count == 0) + return; + + // Calculate the bounds of all selected controls + Rectangle bounds = selection[0].Bounds; + for (int i = 1; i < selection.Count; i++) + bounds = Rectangle.Union(bounds, selection[i].Bounds); + + // Add margin + bounds = bounds.MakeExpanded(250.0f); + + ShowArea(bounds); + } + /// /// Shows the given surface node by changing the view scale and the position and focuses the node. /// @@ -1071,6 +1111,7 @@ namespace FlaxEditor.Surface _cmPrimaryMenu?.Dispose(); ScriptsBuilder.ScriptsReloadBegin -= OnScriptsReloadBegin; + Editor.Instance.Options.OptionsChanged += OnEditorOptionsChanged; base.OnDestroy(); } diff --git a/Source/Editor/Tools/Terrain/EditTerrainGizmo.cs b/Source/Editor/Tools/Terrain/EditTerrainGizmo.cs index 5fc0e894f..c6926f32e 100644 --- a/Source/Editor/Tools/Terrain/EditTerrainGizmo.cs +++ b/Source/Editor/Tools/Terrain/EditTerrainGizmo.cs @@ -89,7 +89,7 @@ namespace FlaxEditor.Tools.Terrain if (!terrain.HasPatch(ref patchCoord) && _planeModel) { var planeSize = 100.0f; - var patchSize = terrain.ChunkSize * FlaxEngine.Terrain.UnitsPerVertex * FlaxEngine.Terrain.PatchEdgeChunksCount; + var patchSize = terrain.PatchSize; Matrix world = Matrix.RotationX(-Mathf.PiOverTwo) * Matrix.Scaling(patchSize / planeSize) * Matrix.Translation(patchSize * (0.5f + patchCoord.X), 0, patchSize * (0.5f + patchCoord.Y)) * diff --git a/Source/Editor/Tools/Terrain/Paint/Mode.cs b/Source/Editor/Tools/Terrain/Paint/Mode.cs index cee3157d3..624a74c3f 100644 --- a/Source/Editor/Tools/Terrain/Paint/Mode.cs +++ b/Source/Editor/Tools/Terrain/Paint/Mode.cs @@ -69,9 +69,9 @@ namespace FlaxEditor.Tools.Terrain.Paint var splatmapIndex = ActiveSplatmapIndex; var splatmapIndexOther = (splatmapIndex + 1) % 2; var chunkSize = terrain.ChunkSize; - var heightmapSize = chunkSize * FlaxEngine.Terrain.PatchEdgeChunksCount + 1; + var heightmapSize = terrain.HeightmapSize; var heightmapLength = heightmapSize * heightmapSize; - var patchSize = chunkSize * FlaxEngine.Terrain.UnitsPerVertex * FlaxEngine.Terrain.PatchEdgeChunksCount; + var patchSize = terrain.PatchSize; var tempBuffer = (Color32*)gizmo.GetSplatmapTempBuffer(heightmapLength * Color32.SizeInBytes, splatmapIndex).ToPointer(); var tempBufferOther = (Color32*)gizmo.GetSplatmapTempBuffer(heightmapLength * Color32.SizeInBytes, (splatmapIndex + 1) % 2).ToPointer(); var unitsPerVertexInv = 1.0f / FlaxEngine.Terrain.UnitsPerVertex; diff --git a/Source/Editor/Tools/Terrain/Sculpt/Mode.cs b/Source/Editor/Tools/Terrain/Sculpt/Mode.cs index f37902f39..55d11b21a 100644 --- a/Source/Editor/Tools/Terrain/Sculpt/Mode.cs +++ b/Source/Editor/Tools/Terrain/Sculpt/Mode.cs @@ -70,9 +70,9 @@ namespace FlaxEditor.Tools.Terrain.Sculpt // Prepare var chunkSize = terrain.ChunkSize; - var heightmapSize = chunkSize * FlaxEngine.Terrain.PatchEdgeChunksCount + 1; + var heightmapSize = terrain.HeightmapSize; var heightmapLength = heightmapSize * heightmapSize; - var patchSize = chunkSize * FlaxEngine.Terrain.UnitsPerVertex * FlaxEngine.Terrain.PatchEdgeChunksCount; + var patchSize = terrain.PatchSize; var tempBuffer = (float*)gizmo.GetHeightmapTempBuffer(heightmapLength * sizeof(float)).ToPointer(); var unitsPerVertexInv = 1.0f / FlaxEngine.Terrain.UnitsPerVertex; diff --git a/Source/Editor/Tools/Terrain/TerrainTools.cpp b/Source/Editor/Tools/Terrain/TerrainTools.cpp index 861cb975b..8e83e77cf 100644 --- a/Source/Editor/Tools/Terrain/TerrainTools.cpp +++ b/Source/Editor/Tools/Terrain/TerrainTools.cpp @@ -382,7 +382,8 @@ bool TerrainTools::ExportTerrain(Terrain* terrain, String outputFolder) const Int2 heightmapSize = size * Terrain::ChunksCountEdge * terrain->GetChunkSize() + 1; Array heightmap; heightmap.Resize(heightmapSize.X * heightmapSize.Y); - heightmap.SetAll(firstPatch->GetHeightmapData()[0]); + if (const float* heightmapData = firstPatch->GetHeightmapData()) + heightmap.SetAll(heightmapData[0]); // Fill heightmap with data from all patches const int32 rowSize = terrain->GetChunkSize() * Terrain::ChunksCountEdge + 1; @@ -392,8 +393,16 @@ bool TerrainTools::ExportTerrain(Terrain* terrain, String outputFolder) const Int2 pos(patch->GetX() - start.X, patch->GetZ() - start.Y); const float* src = patch->GetHeightmapData(); float* dst = heightmap.Get() + pos.X * (rowSize - 1) + pos.Y * heightmapSize.X * (rowSize - 1); - for (int32 row = 0; row < rowSize; row++) - Platform::MemoryCopy(dst + row * heightmapSize.X, src + row * rowSize, rowSize * sizeof(float)); + if (src) + { + for (int32 row = 0; row < rowSize; row++) + Platform::MemoryCopy(dst + row * heightmapSize.X, src + row * rowSize, rowSize * sizeof(float)); + } + else + { + for (int32 row = 0; row < rowSize; row++) + Platform::MemoryClear(dst + row * heightmapSize.X, rowSize * sizeof(float)); + } } // Interpolate to 16-bit int diff --git a/Source/Editor/Tools/Terrain/Undo/EditTerrainMapAction.cs b/Source/Editor/Tools/Terrain/Undo/EditTerrainMapAction.cs index afac0948e..93fc0470e 100644 --- a/Source/Editor/Tools/Terrain/Undo/EditTerrainMapAction.cs +++ b/Source/Editor/Tools/Terrain/Undo/EditTerrainMapAction.cs @@ -85,8 +85,7 @@ namespace FlaxEditor.Tools.Terrain.Undo { _terrain = terrain.ID; _patches = new List(4); - var chunkSize = terrain.ChunkSize; - var heightmapSize = chunkSize * FlaxEngine.Terrain.PatchEdgeChunksCount + 1; + var heightmapSize = terrain.HeightmapSize; _heightmapLength = heightmapSize * heightmapSize; _heightmapDataSize = _heightmapLength * stride; diff --git a/Source/Editor/Viewport/EditorViewport.cs b/Source/Editor/Viewport/EditorViewport.cs index 5413a25b0..78ccedd60 100644 --- a/Source/Editor/Viewport/EditorViewport.cs +++ b/Source/Editor/Viewport/EditorViewport.cs @@ -1229,7 +1229,7 @@ namespace FlaxEditor.Viewport /// Orients the viewport. /// /// The orientation. - protected void OrientViewport(Quaternion orientation) + public void OrientViewport(Quaternion orientation) { OrientViewport(ref orientation); } @@ -1238,7 +1238,7 @@ namespace FlaxEditor.Viewport /// Orients the viewport. /// /// The orientation. - protected virtual void OrientViewport(ref Quaternion orientation) + public virtual void OrientViewport(ref Quaternion orientation) { if (ViewportCamera is FPSCamera fpsCamera) { diff --git a/Source/Editor/Viewport/MainEditorGizmoViewport.cs b/Source/Editor/Viewport/MainEditorGizmoViewport.cs index 1b3078051..df10024cc 100644 --- a/Source/Editor/Viewport/MainEditorGizmoViewport.cs +++ b/Source/Editor/Viewport/MainEditorGizmoViewport.cs @@ -108,13 +108,14 @@ namespace FlaxEditor.Viewport private readonly ViewportDebugDrawData _debugDrawData = new ViewportDebugDrawData(32); private EditorSpritesRenderer _editorSpritesRenderer; private ViewportRubberBandSelector _rubberBandSelector; + private DirectionGizmo _directionGizmo; private bool _gameViewActive; private ViewFlags _preGameViewFlags; private ViewMode _preGameViewViewMode; private bool _gameViewWasGridShown; private bool _gameViewWasFpsCounterShown; - private bool _gameViewWasNagivationShown; + private bool _gameViewWasNavigationShown; /// /// Drag and drop handlers @@ -225,6 +226,12 @@ namespace FlaxEditor.Viewport // Add rubber band selector _rubberBandSelector = new ViewportRubberBandSelector(this); + _directionGizmo = new DirectionGizmo(this); + _directionGizmo.AnchorPreset = AnchorPresets.TopRight; + _directionGizmo.Parent = this; + _directionGizmo.LocalY += 25; + _directionGizmo.LocalX -= 150; + _directionGizmo.Size = new Float2(150, 150); // Add grid Grid = new GridGizmo(this); @@ -244,6 +251,12 @@ namespace FlaxEditor.Viewport _showNavigationButton = ViewWidgetShowMenu.AddButton("Navigation", inputOptions.ToggleNavMeshVisibility, () => ShowNavigation = !ShowNavigation); _showNavigationButton.CloseMenuOnClick = false; + // Show direction gizmo widget + var showDirectionGizmoButton = ViewWidgetShowMenu.AddButton("Direction Gizmo", () => _directionGizmo.Visible = !_directionGizmo.Visible); + showDirectionGizmoButton.AutoCheck = true; + showDirectionGizmoButton.CloseMenuOnClick = false; + showDirectionGizmoButton.Checked = _directionGizmo.Visible; + // Game View ViewWidgetButtonMenu.AddSeparator(); _toggleGameViewButton = ViewWidgetButtonMenu.AddButton("Game View", inputOptions.ToggleGameView, ToggleGameView); @@ -514,14 +527,14 @@ namespace FlaxEditor.Viewport _preGameViewViewMode = Task.ViewMode; _gameViewWasGridShown = Grid.Enabled; _gameViewWasFpsCounterShown = ShowFpsCounter; - _gameViewWasNagivationShown = ShowNavigation; + _gameViewWasNavigationShown = ShowNavigation; } // Set flags & values Task.ViewFlags = _gameViewActive ? _preGameViewFlags : ViewFlags.DefaultGame; Task.ViewMode = _gameViewActive ? _preGameViewViewMode : ViewMode.Default; ShowFpsCounter = _gameViewActive ? _gameViewWasFpsCounterShown : false; - ShowNavigation = _gameViewActive ? _gameViewWasNagivationShown : false; + ShowNavigation = _gameViewActive ? _gameViewWasNavigationShown : false; Grid.Enabled = _gameViewActive ? _gameViewWasGridShown : false; _gameViewActive = !_gameViewActive; @@ -647,7 +660,7 @@ namespace FlaxEditor.Viewport } /// - protected override void OrientViewport(ref Quaternion orientation) + public override void OrientViewport(ref Quaternion orientation) { if (TransformGizmo.SelectedParents.Count != 0) FocusSelection(ref orientation); diff --git a/Source/Editor/Viewport/PrefabWindowViewport.cs b/Source/Editor/Viewport/PrefabWindowViewport.cs index a8bdc8f20..a599615de 100644 --- a/Source/Editor/Viewport/PrefabWindowViewport.cs +++ b/Source/Editor/Viewport/PrefabWindowViewport.cs @@ -681,7 +681,7 @@ namespace FlaxEditor.Viewport } /// - protected override void OrientViewport(ref Quaternion orientation) + public override void OrientViewport(ref Quaternion orientation) { if (TransformGizmo.SelectedParents.Count != 0) FocusSelection(ref orientation); diff --git a/Source/Editor/Viewport/Previews/MaterialPreview.cs b/Source/Editor/Viewport/Previews/MaterialPreview.cs index 8ba8a00ee..e494df14a 100644 --- a/Source/Editor/Viewport/Previews/MaterialPreview.cs +++ b/Source/Editor/Viewport/Previews/MaterialPreview.cs @@ -303,8 +303,7 @@ namespace FlaxEditor.Viewport.Previews { _terrain = new Terrain(); _terrain.Setup(1, 63); - var chunkSize = _terrain.ChunkSize; - var heightMapSize = chunkSize * Terrain.PatchEdgeChunksCount + 1; + var heightMapSize = _terrain.HeightmapSize; var heightMapLength = heightMapSize * heightMapSize; var heightmap = new float[heightMapLength]; var patchCoord = new Int2(0, 0); diff --git a/Source/Editor/Windows/Assets/AnimationWindow.cs b/Source/Editor/Windows/Assets/AnimationWindow.cs index 8dbcc5921..69da1f9e9 100644 --- a/Source/Editor/Windows/Assets/AnimationWindow.cs +++ b/Source/Editor/Windows/Assets/AnimationWindow.cs @@ -431,6 +431,9 @@ namespace FlaxEditor.Windows.Assets _isWaitingForTimelineLoad = true; base.OnItemReimported(item); + + // Drop virtual asset state and get a new one from the reimported file + LoadFromOriginal(); } /// diff --git a/Source/Editor/Windows/Assets/AssetEditorWindow.cs b/Source/Editor/Windows/Assets/AssetEditorWindow.cs index 646112441..93d6c850c 100644 --- a/Source/Editor/Windows/Assets/AssetEditorWindow.cs +++ b/Source/Editor/Windows/Assets/AssetEditorWindow.cs @@ -53,7 +53,7 @@ namespace FlaxEditor.Windows.Assets { Parent = this }; - _toolstrip.AddButton(editor.Icons.Search64, () => Editor.Windows.ContentWin.Select(_item)).LinkTooltip("Show and select in Content Window"); + _toolstrip.AddButton(editor.Icons.Search64, () => Editor.Windows.ContentWin.Select(_item)).LinkTooltip("Show and select in Content Window."); InputActions.Add(options => options.Save, Save); @@ -527,6 +527,16 @@ namespace FlaxEditor.Windows.Assets return false; } + /// + /// Loads the asset from the original location to reflect the state (eg. after original asset reimport). + /// + protected virtual void LoadFromOriginal() + { + _asset = LoadAsset(); + OnAssetLoaded(); + ClearEditedFlag(); + } + /// protected override T LoadAsset() { diff --git a/Source/Editor/Windows/ContentWindow.Search.cs b/Source/Editor/Windows/ContentWindow.Search.cs index f28dc4834..5a0ed63aa 100644 --- a/Source/Editor/Windows/ContentWindow.Search.cs +++ b/Source/Editor/Windows/ContentWindow.Search.cs @@ -115,6 +115,7 @@ namespace FlaxEditor.Windows var root = _root; root.LockChildrenRecursive(); + PerformLayout(); // Update tree var query = _foldersSearchBox.Text; diff --git a/Source/Editor/Windows/ContentWindow.cs b/Source/Editor/Windows/ContentWindow.cs index 9b92b380f..6313a91b0 100644 --- a/Source/Editor/Windows/ContentWindow.cs +++ b/Source/Editor/Windows/ContentWindow.cs @@ -1126,6 +1126,8 @@ namespace FlaxEditor.Windows if (Editor.ContentDatabase.Find(_lastViewedFolderBeforeReload) is ContentFolder folder) _tree.Select(folder.Node); } + + OnFoldersSearchBoxTextChanged(); } private void Refresh() diff --git a/Source/Editor/Windows/SceneTreeWindow.cs b/Source/Editor/Windows/SceneTreeWindow.cs index b88a57fdc..fba4e5a71 100644 --- a/Source/Editor/Windows/SceneTreeWindow.cs +++ b/Source/Editor/Windows/SceneTreeWindow.cs @@ -67,6 +67,7 @@ namespace FlaxEditor.Windows TooltipText = "Search the scene tree.\n\nYou can prefix your search with different search operators:\ns: -> Actor with script of type\na: -> Actor type\nc: -> Control type", }; _searchBox.TextChanged += OnSearchBoxTextChanged; + ScriptsBuilder.ScriptsReloadEnd += OnSearchBoxTextChanged; // Scene tree panel _sceneTreePanel = new Panel @@ -111,7 +112,7 @@ namespace FlaxEditor.Windows InputActions.Add(options => options.LockFocusSelection, () => Editor.Windows.EditWin.Viewport.LockFocusSelection()); InputActions.Add(options => options.Rename, RenameSelection); } - + /// public override void OnPlayBeginning() { @@ -124,6 +125,7 @@ namespace FlaxEditor.Windows { base.OnPlayBegin(); _blockSceneTreeScroll = false; + OnSearchBoxTextChanged(); } /// @@ -138,6 +140,7 @@ namespace FlaxEditor.Windows { base.OnPlayEnd(); _blockSceneTreeScroll = true; + OnSearchBoxTextChanged(); } /// @@ -173,6 +176,7 @@ namespace FlaxEditor.Windows return; _tree.LockChildrenRecursive(); + PerformLayout(); // Update tree var query = _searchBox.Text; @@ -586,6 +590,7 @@ namespace FlaxEditor.Windows _dragHandlers = null; _tree = null; _searchBox = null; + ScriptsBuilder.ScriptsReloadEnd -= OnSearchBoxTextChanged; base.OnDestroy(); } diff --git a/Source/Engine/Content/Asset.cpp b/Source/Engine/Content/Asset.cpp index b77f39bb5..4d7c21b91 100644 --- a/Source/Engine/Content/Asset.cpp +++ b/Source/Engine/Content/Asset.cpp @@ -8,6 +8,8 @@ #include "Loading/Tasks/LoadAssetTask.h" #include "Engine/Core/Log.h" #include "Engine/Core/LogContext.h" +#include "Engine/Graphics/GPUDevice.h" +#include "Engine/Physics/Physics.h" #include "Engine/Profiler/ProfilerCPU.h" #include "Engine/Profiler/ProfilerMemory.h" #include "Engine/Scripting/ManagedCLR/MCore.h" @@ -703,6 +705,38 @@ void Asset::onUnload_MainThread() OnUnloaded(this); } +bool Asset::WaitForInitGraphics() +{ +#define IS_GPU_NOT_READY() (GPUDevice::Instance == nullptr || GPUDevice::Instance->GetState() != GPUDevice::DeviceState::Ready) + if (!IsInMainThread() && IS_GPU_NOT_READY()) + { + PROFILE_CPU(); + ZoneColor(TracyWaitZoneColor); + int32 timeout = 1000; + while (IS_GPU_NOT_READY() && timeout-- > 0) + Platform::Sleep(1); + if (IS_GPU_NOT_READY()) + return true; + } +#undef IS_GPU_NOT_READY + return false; +} + +bool Asset::WaitForInitPhysics() +{ + if (!IsInMainThread() && !Physics::DefaultScene) + { + PROFILE_CPU(); + ZoneColor(TracyWaitZoneColor); + int32 timeout = 1000; + while (!Physics::DefaultScene && timeout-- > 0) + Platform::Sleep(1); + if (!Physics::DefaultScene) + return true; + } + return false; +} + #if USE_EDITOR bool Asset::OnCheckSave(const StringView& path) const diff --git a/Source/Engine/Content/Asset.h b/Source/Engine/Content/Asset.h index e6607e62c..a7913a5a5 100644 --- a/Source/Engine/Content/Asset.h +++ b/Source/Engine/Content/Asset.h @@ -285,6 +285,10 @@ protected: virtual void onRename(const StringView& newPath) = 0; #endif + // Utilities to ensure specific engine systems are initialized before loading asset (eg. assets can be loaded during engine startup). + static bool WaitForInitGraphics(); + static bool WaitForInitPhysics(); + public: // [ManagedScriptingObject] String ToString() const override; diff --git a/Source/Engine/Content/Assets/Material.cpp b/Source/Engine/Content/Assets/Material.cpp index 3042f66da..a508cb2f8 100644 --- a/Source/Engine/Content/Assets/Material.cpp +++ b/Source/Engine/Content/Assets/Material.cpp @@ -6,7 +6,6 @@ #include "Engine/Content/Deprecated.h" #include "Engine/Content/Upgraders/ShaderAssetUpgrader.h" #include "Engine/Content/Factories/BinaryAssetFactory.h" -#include "Engine/Graphics/GPUDevice.h" #include "Engine/Graphics/RenderTools.h" #include "Engine/Graphics/Materials/MaterialShader.h" #include "Engine/Graphics/Shaders/Cache/ShaderCacheManager.h" @@ -157,16 +156,8 @@ Asset::LoadResult Material::load() FlaxChunk* materialParamsChunk; // Wait for the GPU Device to be ready (eg. case when loading material before GPU init) -#define IS_GPU_NOT_READY() (GPUDevice::Instance == nullptr || GPUDevice::Instance->GetState() != GPUDevice::DeviceState::Ready) - if (!IsInMainThread() && IS_GPU_NOT_READY()) - { - int32 timeout = 1000; - while (IS_GPU_NOT_READY() && timeout-- > 0) - Platform::Sleep(1); - if (IS_GPU_NOT_READY()) - return LoadResult::InvalidData; - } -#undef IS_GPU_NOT_READY + if (WaitForInitGraphics()) + return LoadResult::CannotLoadData; // If engine was compiled with shaders compiling service: // - Material should be changed in need to convert it to the newer version (via Visject Surface) diff --git a/Source/Engine/Content/Assets/MaterialBase.cpp b/Source/Engine/Content/Assets/MaterialBase.cpp index 6ef5f9429..1613ef59a 100644 --- a/Source/Engine/Content/Assets/MaterialBase.cpp +++ b/Source/Engine/Content/Assets/MaterialBase.cpp @@ -19,10 +19,10 @@ Variant MaterialBase::GetParameterValue(const StringView& name) if (!IsLoaded() && WaitForLoaded()) return Variant::Null; const auto param = Params.Get(name); + if (IsMaterialInstance() && param && !param->IsOverride() && ((MaterialInstance*)this)->GetBaseMaterial()) + return ((MaterialInstance*)this)->GetBaseMaterial()->GetParameterValue(name); if (param) - { return param->GetValue(); - } LOG(Warning, "Missing material parameter '{0}' in material {1}", String(name), ToString()); return Variant::Null; } diff --git a/Source/Engine/Content/Assets/MaterialBase.h b/Source/Engine/Content/Assets/MaterialBase.h index f0c32d66a..78f244cb7 100644 --- a/Source/Engine/Content/Assets/MaterialBase.h +++ b/Source/Engine/Content/Assets/MaterialBase.h @@ -57,6 +57,8 @@ public: /// /// Gets the material parameter value. /// + /// For material instances that inherit a base material, returned value might come from base material if the current one doesn't override it. + /// The parameter name. /// The parameter value. API_FUNCTION() Variant GetParameterValue(const StringView& name); diff --git a/Source/Engine/Content/Storage/ContentStorageManager.cpp b/Source/Engine/Content/Storage/ContentStorageManager.cpp index 80744ae45..8aa36c7be 100644 --- a/Source/Engine/Content/Storage/ContentStorageManager.cpp +++ b/Source/Engine/Content/Storage/ContentStorageManager.cpp @@ -46,6 +46,7 @@ namespace ContentStorageService ContentStorageServiceInstance; +TimeSpan ContentStorageManager::UnusedStorageLifetime = TimeSpan::FromSeconds(0.5f); TimeSpan ContentStorageManager::UnusedDataChunksLifetime = TimeSpan::FromSeconds(10); FlaxStorageReference ContentStorageManager::GetStorage(const StringView& path, bool loadIt) diff --git a/Source/Engine/Content/Storage/ContentStorageManager.h b/Source/Engine/Content/Storage/ContentStorageManager.h index 0d56855ea..fac27aa7f 100644 --- a/Source/Engine/Content/Storage/ContentStorageManager.h +++ b/Source/Engine/Content/Storage/ContentStorageManager.h @@ -15,7 +15,12 @@ class FLAXENGINE_API ContentStorageManager { public: /// - /// Auto-release timeout for unused asset chunks. + /// Auto-release timeout for unused asset files. + /// + static TimeSpan UnusedStorageLifetime; + + /// + /// Auto-release timeout for unused asset data chunks. /// static TimeSpan UnusedDataChunksLifetime; diff --git a/Source/Engine/Content/Storage/FlaxStorage.cpp b/Source/Engine/Content/Storage/FlaxStorage.cpp index 2fb670e94..0cdaa6a6e 100644 --- a/Source/Engine/Content/Storage/FlaxStorage.cpp +++ b/Source/Engine/Content/Storage/FlaxStorage.cpp @@ -286,14 +286,14 @@ FlaxStorage::LockData FlaxStorage::LockSafe() uint32 FlaxStorage::GetRefCount() const { - return (uint32)Platform::AtomicRead((intptr*)&_refCount); + return (uint32)Platform::AtomicRead(&_refCount); } bool FlaxStorage::ShouldDispose() const { - return Platform::AtomicRead((intptr*)&_refCount) == 0 && - Platform::AtomicRead((intptr*)&_chunksLock) == 0 && - Platform::GetTimeSeconds() - _lastRefLostTime >= 0.5; // TTL in seconds + return Platform::AtomicRead(&_refCount) == 0 && + Platform::AtomicRead(&_chunksLock) == 0 && + Platform::GetTimeSeconds() - _lastRefLostTime >= ContentStorageManager::UnusedStorageLifetime.GetTotalSeconds(); } uint32 FlaxStorage::GetMemoryUsage() const diff --git a/Source/Engine/ContentImporters/ImportModel.cpp b/Source/Engine/ContentImporters/ImportModel.cpp index f3548dc5c..b2a9434c9 100644 --- a/Source/Engine/ContentImporters/ImportModel.cpp +++ b/Source/Engine/ContentImporters/ImportModel.cpp @@ -19,6 +19,7 @@ #include "Engine/Animations/AnimEvent.h" #include "Engine/Level/Actors/EmptyActor.h" #include "Engine/Level/Actors/StaticModel.h" +#include "Engine/Level/Actors/AnimatedModel.h" #include "Engine/Level/Prefabs/Prefab.h" #include "Engine/Level/Prefabs/PrefabManager.h" #include "Engine/Level/Scripts/ModelPrefab.h" @@ -82,6 +83,11 @@ bool ImportModel::TryGetImportOptions(const StringView& path, Options& options) struct PrefabObject { + enum + { + Model, + SkinnedModel, + } Type; int32 NodeIndex; String Name; String AssetPath; @@ -280,7 +286,7 @@ CreateAssetResult ImportModel::Import(CreateAssetContext& context) options.SplitObjects = false; options.ObjectIndex = -1; - // Import all of the objects recursive but use current model data to skip loading file again + // Import all the objects recursive but use current model data to skip loading file again options.Cached = &cached; HashSet objectNames; Function splitImport = [&context, &autoImportOutput, &objectNames](Options& splitOptions, const StringView& objectName, String& outputPath, MeshData* meshData) @@ -335,12 +341,24 @@ CreateAssetResult ImportModel::Import(CreateAssetContext& context) auto& group = meshesByName[groupIndex]; // Cache object options (nested sub-object import removes the meshes) - prefabObject.NodeIndex = group.First()->NodeIndex; - prefabObject.Name = group.First()->Name; + MeshData* firstMesh = group.First(); + prefabObject.NodeIndex = firstMesh->NodeIndex; + prefabObject.Name = firstMesh->Name; - splitOptions.Type = ModelTool::ModelType::Model; + // Detect model type + if ((firstMesh->BlendIndices.HasItems() && firstMesh->BlendWeights.HasItems()) || firstMesh->BlendShapes.HasItems()) + { + splitOptions.Type = ModelTool::ModelType::SkinnedModel; + prefabObject.Type = PrefabObject::SkinnedModel; + } + else + { + splitOptions.Type = ModelTool::ModelType::Model; + prefabObject.Type = PrefabObject::Model; + } + splitOptions.ObjectIndex = groupIndex; - if (!splitImport(splitOptions, group.GetKey(), prefabObject.AssetPath, group.First())) + if (!splitImport(splitOptions, group.GetKey(), prefabObject.AssetPath, firstMesh)) { prefabObjects.Add(prefabObject); } @@ -734,24 +752,38 @@ CreateAssetResult ImportModel::CreatePrefab(CreateAssetContext& context, const M nodeActors.Clear(); for (const PrefabObject& e : prefabObjects) { - if (e.NodeIndex == nodeIndex) + if (e.NodeIndex != nodeIndex) + continue; + Actor* a = nullptr; + switch (e.Type) + { + case PrefabObject::Model: { auto* actor = New(); - actor->SetName(e.Name); if (auto* model = Content::LoadAsync(e.AssetPath)) - { actor->Model = model; - } - nodeActors.Add(actor); + a = actor; + break; } + case PrefabObject::SkinnedModel: + { + auto* actor = New(); + if (auto* skinnedModel = Content::LoadAsync(e.AssetPath)) + actor->SkinnedModel = skinnedModel; + a = actor; + break; + } + default: + continue; + } + a->SetName(e.Name); + nodeActors.Add(a); } Actor* nodeActor = nodeActors.Count() == 1 ? nodeActors[0] : New(); if (nodeActors.Count() > 1) { for (Actor* e : nodeActors) - { e->SetParent(nodeActor); - } } if (nodeActors.Count() != 1) { diff --git a/Source/Engine/Engine/Screen.cpp b/Source/Engine/Engine/Screen.cpp index 4faaeb685..db9e67aa9 100644 --- a/Source/Engine/Engine/Screen.cpp +++ b/Source/Engine/Engine/Screen.cpp @@ -155,6 +155,7 @@ void Screen::SetCursorLock(CursorLockMode mode) bool inRelativeMode = Input::Mouse->IsRelative(); if (mode == CursorLockMode::Clipped) win->StartClippingCursor(bounds); +#if PLATFORM_SDL else if (mode == CursorLockMode::Locked) { // Use mouse clip region to restrict the cursor in one spot @@ -162,6 +163,10 @@ void Screen::SetCursorLock(CursorLockMode mode) } else if (CursorLock == CursorLockMode::Locked || CursorLock == CursorLockMode::Clipped) win->EndClippingCursor(); +#else + else if (CursorLock == CursorLockMode::Clipped) + win->EndClippingCursor(); +#endif // Enable relative mode when cursor is restricted if (mode != CursorLockMode::None) diff --git a/Source/Engine/Graphics/Materials/TerrainMaterialShader.cpp b/Source/Engine/Graphics/Materials/TerrainMaterialShader.cpp index 8a28ab168..bb0796234 100644 --- a/Source/Engine/Graphics/Materials/TerrainMaterialShader.cpp +++ b/Source/Engine/Graphics/Materials/TerrainMaterialShader.cpp @@ -88,9 +88,8 @@ void TerrainMaterialShader::Bind(BindParameters& params) } // Bind terrain textures - const auto heightmap = drawCall.Terrain.Patch->Heightmap->GetTexture(); - const auto splatmap0 = drawCall.Terrain.Patch->Splatmap[0] ? drawCall.Terrain.Patch->Splatmap[0]->GetTexture() : nullptr; - const auto splatmap1 = drawCall.Terrain.Patch->Splatmap[1] ? drawCall.Terrain.Patch->Splatmap[1]->GetTexture() : nullptr; + GPUTexture* heightmap, *splatmap0, *splatmap1; + drawCall.Terrain.Patch->GetTextures(heightmap, splatmap0, splatmap1); context->BindSR(0, heightmap); context->BindSR(1, splatmap0); context->BindSR(2, splatmap1); diff --git a/Source/Engine/Input/Input.cpp b/Source/Engine/Input/Input.cpp index 8b9bd40bd..880e9d7e9 100644 --- a/Source/Engine/Input/Input.cpp +++ b/Source/Engine/Input/Input.cpp @@ -936,7 +936,9 @@ void InputService::Update() break; } } +#if PLATFORM_SDL WindowsManager::WindowsLocker.Unlock(); +#endif // Send input events for the focused window for (const auto& e : InputEvents) @@ -990,6 +992,9 @@ void InputService::Update() break; } } +#if !PLATFORM_SDL + WindowsManager::WindowsLocker.Unlock(); +#endif // Skip if game has no focus to handle the input if (!Engine::HasGameViewportFocus()) diff --git a/Source/Engine/Level/SceneObjectsFactory.cpp b/Source/Engine/Level/SceneObjectsFactory.cpp index 576b95b46..59d23a994 100644 --- a/Source/Engine/Level/SceneObjectsFactory.cpp +++ b/Source/Engine/Level/SceneObjectsFactory.cpp @@ -448,8 +448,7 @@ void SceneObjectsFactory::PrefabSyncData::InitNewObjects() void SceneObjectsFactory::SetupPrefabInstances(Context& context, const PrefabSyncData& data) { PROFILE_CPU_NAMED("SetupPrefabInstances"); - const int32 count = data.Data.Size(); - ASSERT(count <= data.SceneObjects.Count()); + const int32 count = Math::Min(data.Data.Size(), data.SceneObjects.Count()); Dictionary parentIdsLookup; for (int32 i = 0; i < count; i++) { diff --git a/Source/Engine/Physics/Colliders/Collider.cpp b/Source/Engine/Physics/Colliders/Collider.cpp index 96bd9c7ae..28588e2a6 100644 --- a/Source/Engine/Physics/Colliders/Collider.cpp +++ b/Source/Engine/Physics/Colliders/Collider.cpp @@ -209,6 +209,13 @@ void Collider::CreateShape() // Create shape const bool isTrigger = _isTrigger && CanBeTrigger(); _shape = PhysicsBackend::CreateShape(this, shape, Material, IsActiveInHierarchy(), isTrigger); + if (!_shape) + { + LOG(Error, "Failed to create physics shape for actor '{}'", GetNamePath()); + if (shape.Type == CollisionShape::Types::ConvexMesh && Float3(shape.ConvexMesh.Scale).MinValue() <= 0) + LOG(Warning, "Convex Mesh colliders cannot have negative scale"); + return; + } PhysicsBackend::SetShapeContactOffset(_shape, _contactOffset); UpdateLayerBits(); } @@ -293,18 +300,20 @@ void Collider::BeginPlay(SceneBeginData* data) if (_shape == nullptr) { CreateShape(); - - // Check if parent is a rigidbody - const auto rigidBody = dynamic_cast(GetParent()); - if (rigidBody && CanAttach(rigidBody)) + if (_shape) { - // Attach to the rigidbody - Attach(rigidBody); - } - else - { - // Be a static collider - CreateStaticActor(); + // Check if parent is a rigidbody + const auto rigidBody = dynamic_cast(GetParent()); + if (rigidBody && CanAttach(rigidBody)) + { + // Attach to the rigidbody + Attach(rigidBody); + } + else + { + // Be a static collider + CreateStaticActor(); + } } } diff --git a/Source/Engine/Physics/CollisionData.cpp b/Source/Engine/Physics/CollisionData.cpp index c65ea8a6b..94cef111e 100644 --- a/Source/Engine/Physics/CollisionData.cpp +++ b/Source/Engine/Physics/CollisionData.cpp @@ -257,6 +257,8 @@ Asset::LoadResult CollisionData::load() CollisionData::LoadResult CollisionData::load(const SerializedOptions* options, byte* dataPtr, int32 dataSize) { + if (WaitForInitPhysics()) + return LoadResult::CannotLoadData; PROFILE_MEM(Physics); // Load options diff --git a/Source/Engine/Physics/PhysX/PhysicsBackendPhysX.cpp b/Source/Engine/Physics/PhysX/PhysicsBackendPhysX.cpp index 7e1140bbc..21059ad9e 100644 --- a/Source/Engine/Physics/PhysX/PhysicsBackendPhysX.cpp +++ b/Source/Engine/Physics/PhysX/PhysicsBackendPhysX.cpp @@ -1204,6 +1204,8 @@ void ScenePhysX::PreSimulateCloth(int32 i) PROFILE_MEM(PhysicsCloth); auto clothPhysX = ClothsList[i]; auto& clothSettings = Cloths[clothPhysX]; + if (!clothSettings.Actor) + return; if (clothSettings.Actor->OnPreUpdate()) { @@ -2686,10 +2688,13 @@ void* PhysicsBackend::CreateShape(PhysicsColliderActor* collider, const Collisio PxGeometryHolder geometryPhysX; GetShapeGeometry(geometry, geometryPhysX); PxShape* shapePhysX = PhysX->createShape(geometryPhysX.any(), materialsPhysX.Get(), materialsPhysX.Count(), true, shapeFlags); - shapePhysX->userData = collider; + if (shapePhysX) + { + shapePhysX->userData = collider; #if PHYSX_DEBUG_NAMING - shapePhysX->setName("Shape"); + shapePhysX->setName("Shape"); #endif + } return shapePhysX; } diff --git a/Source/Engine/Renderer/Renderer.cpp b/Source/Engine/Renderer/Renderer.cpp index 31e013fd0..b76ec2f91 100644 --- a/Source/Engine/Renderer/Renderer.cpp +++ b/Source/Engine/Renderer/Renderer.cpp @@ -178,6 +178,27 @@ void RenderAntiAliasingPass(RenderContext& renderContext, GPUTexture* input, GPU } } +void RenderLightBuffer(const SceneRenderTask* task, GPUContext* context, RenderContext& renderContext, GPUTexture* lightBuffer, const GPUTextureDescription& tempDesc) +{ + context->ResetRenderTarget(); + auto colorGradingLUT = ColorGradingPass::Instance()->RenderLUT(renderContext); + auto tempBuffer = RenderTargetPool::Get(tempDesc); + RENDER_TARGET_POOL_SET_NAME(tempBuffer, "TempBuffer"); + EyeAdaptationPass::Instance()->Render(renderContext, lightBuffer); + PostProcessingPass::Instance()->Render(renderContext, lightBuffer, tempBuffer, colorGradingLUT); + context->ResetRenderTarget(); + if (renderContext.List->Settings.AntiAliasing.Mode == AntialiasingMode::TemporalAntialiasing) + { + TAA::Instance()->Render(renderContext, tempBuffer, lightBuffer->View()); + Swap(lightBuffer, tempBuffer); + } + RenderTargetPool::Release(lightBuffer); + context->SetRenderTarget(task->GetOutputView()); + context->SetViewportAndScissors(task->GetOutputViewport()); + context->Draw(tempBuffer); + RenderTargetPool::Release(tempBuffer); +} + bool Renderer::IsReady() { // Warm up first (state getters initialize content loading so do it for all first) @@ -350,10 +371,12 @@ void RenderInner(SceneRenderTask* task, RenderContext& renderContext, RenderCont // Perform postFx volumes blending and query before rendering task->CollectPostFxVolumes(renderContext); renderContext.List->BlendSettings(); - auto aaMode = EnumHasAnyFlags(renderContext.View.Flags, ViewFlags::AntiAliasing) ? renderContext.List->Settings.AntiAliasing.Mode : AntialiasingMode::None; - if (aaMode == AntialiasingMode::TemporalAntialiasing && view.IsOrthographicProjection()) - aaMode = AntialiasingMode::None; // TODO: support TAA in ortho projection (see RenderView::Prepare to jitter projection matrix better) - renderContext.List->Settings.AntiAliasing.Mode = aaMode; + { + auto aaMode = EnumHasAnyFlags(renderContext.View.Flags, ViewFlags::AntiAliasing) ? renderContext.List->Settings.AntiAliasing.Mode : AntialiasingMode::None; + if (aaMode == AntialiasingMode::TemporalAntialiasing && view.IsOrthographicProjection()) + aaMode = AntialiasingMode::None; // TODO: support TAA in ortho projection (see RenderView::Prepare to jitter projection matrix better) + renderContext.List->Settings.AntiAliasing.Mode = aaMode; + } // Initialize setup RenderSetup& setup = renderContext.List->Setup; @@ -375,7 +398,7 @@ void RenderInner(SceneRenderTask* task, RenderContext& renderContext, RenderCont (ssrSettings.Intensity > ZeroTolerance && ssrSettings.TemporalEffect && EnumHasAnyFlags(renderContext.View.Flags, ViewFlags::SSR)) || renderContext.List->Settings.AntiAliasing.Mode == AntialiasingMode::TemporalAntialiasing; } - setup.UseTemporalAAJitter = aaMode == AntialiasingMode::TemporalAntialiasing; + setup.UseTemporalAAJitter = renderContext.List->Settings.AntiAliasing.Mode == AntialiasingMode::TemporalAntialiasing; setup.UseGlobalSurfaceAtlas = renderContext.View.Mode == ViewMode::GlobalSurfaceAtlas || (EnumHasAnyFlags(renderContext.View.Flags, ViewFlags::GI) && renderContext.List->Settings.GlobalIllumination.Mode == GlobalIlluminationMode::DDGI); setup.UseGlobalSDF = (graphicsSettings->EnableGlobalSDF && EnumHasAnyFlags(view.Flags, ViewFlags::GlobalSDF)) || @@ -630,22 +653,7 @@ void RenderInner(SceneRenderTask* task, RenderContext& renderContext, RenderCont } if (renderContext.View.Mode == ViewMode::LightBuffer) { - auto colorGradingLUT = ColorGradingPass::Instance()->RenderLUT(renderContext); - auto tempBuffer = RenderTargetPool::Get(tempDesc); - RENDER_TARGET_POOL_SET_NAME(tempBuffer, "TempBuffer"); - EyeAdaptationPass::Instance()->Render(renderContext, lightBuffer); - PostProcessingPass::Instance()->Render(renderContext, lightBuffer, tempBuffer, colorGradingLUT); - context->ResetRenderTarget(); - if (aaMode == AntialiasingMode::TemporalAntialiasing) - { - TAA::Instance()->Render(renderContext, tempBuffer, lightBuffer->View()); - Swap(lightBuffer, tempBuffer); - } - RenderTargetPool::Release(lightBuffer); - context->SetRenderTarget(task->GetOutputView()); - context->SetViewportAndScissors(task->GetOutputViewport()); - context->Draw(tempBuffer); - RenderTargetPool::Release(tempBuffer); + RenderLightBuffer(task, context, renderContext, lightBuffer, tempDesc); return; } @@ -656,11 +664,13 @@ void RenderInner(SceneRenderTask* task, RenderContext& renderContext, RenderCont ReflectionsPass::Instance()->Render(renderContext, *lightBuffer); if (renderContext.View.Mode == ViewMode::Reflections) { - context->ResetRenderTarget(); - context->SetRenderTarget(task->GetOutputView()); - context->SetViewportAndScissors(task->GetOutputViewport()); - context->Draw(lightBuffer); - RenderTargetPool::Release(lightBuffer); + renderContext.List->Settings.ToneMapping.Mode = ToneMappingMode::Neutral; + renderContext.List->Settings.Bloom.Enabled = false; + renderContext.List->Settings.LensFlares.Intensity = 0.0f; + renderContext.List->Settings.CameraArtifacts.GrainAmount = 0.0f; + renderContext.List->Settings.CameraArtifacts.ChromaticDistortion = 0.0f; + renderContext.List->Settings.CameraArtifacts.VignetteIntensity = 0.0f; + RenderLightBuffer(task, context, renderContext, lightBuffer, tempDesc); return; } @@ -716,7 +726,7 @@ void RenderInner(SceneRenderTask* task, RenderContext& renderContext, RenderCont renderContext.List->RunCustomPostFxPass(context, renderContext, PostProcessEffectLocation::BeforePostProcessingPass, frameBuffer, tempBuffer); // Temporal Anti-Aliasing (goes before post processing) - if (aaMode == AntialiasingMode::TemporalAntialiasing) + if (renderContext.List->Settings.AntiAliasing.Mode == AntialiasingMode::TemporalAntialiasing) { TAA::Instance()->Render(renderContext, frameBuffer, tempBuffer->View()); Swap(frameBuffer, tempBuffer); diff --git a/Source/Engine/Serialization/Json.h b/Source/Engine/Serialization/Json.h index 1823935e5..692c42bc7 100644 --- a/Source/Engine/Serialization/Json.h +++ b/Source/Engine/Serialization/Json.h @@ -20,6 +20,7 @@ #define RAPIDJSON_NEW(x) New #define RAPIDJSON_DELETE(x) Delete(x) #define RAPIDJSON_NOMEMBERITERATORCLASS +#define RAPIDJSON_PARSE_DEFAULT_FLAGS kParseTrailingCommasFlag //#define RAPIDJSON_MALLOC(size) ::malloc(size) //#define RAPIDJSON_REALLOC(ptr, new_size) ::realloc(ptr, new_size) //#define RAPIDJSON_FREE(ptr) ::free(ptr) diff --git a/Source/Engine/Terrain/Terrain.Build.cs b/Source/Engine/Terrain/Terrain.Build.cs index 178938da8..55a590dd2 100644 --- a/Source/Engine/Terrain/Terrain.Build.cs +++ b/Source/Engine/Terrain/Terrain.Build.cs @@ -8,13 +8,22 @@ using Flax.Build.NativeCpp; /// public class Terrain : EngineModule { + /// + /// Enables terrain editing and changing at runtime. If your game doesn't use procedural terrain in game then disable this option to reduce build size. + /// + public static bool WithEditing = true; + /// public override void Setup(BuildOptions options) { base.Setup(options); - options.PrivateDependencies.Add("Physics"); + if (!WithEditing) + { + options.PublicDefinitions.Add("TERRAIN_EDITING=0"); + } + options.PrivateDependencies.Add("Physics"); if (options.Target.IsEditor) { options.PrivateDependencies.Add("ContentImporters"); diff --git a/Source/Engine/Terrain/Terrain.cpp b/Source/Engine/Terrain/Terrain.cpp index f0ad65894..9185f71a9 100644 --- a/Source/Engine/Terrain/Terrain.cpp +++ b/Source/Engine/Terrain/Terrain.cpp @@ -306,6 +306,16 @@ void Terrain::SetPhysicalMaterials(const Array(_chunkSize); } + /// + /// Gets the heightmap texture size (square) used by a single patch (shared by all chunks within that patch). + /// + /// ChunkSize * ChunksCountEdge + 1 + API_PROPERTY() int32 GetHeightmapSize() const; + + /// + /// Gets the size of the patch in world-units (square) without actor scale. + /// + /// UnitsPerVertex * ChunksCountEdge * ChunkSize + API_PROPERTY() float GetPatchSize() const; + /// /// Gets the terrain patches count. Each patch contains 16 chunks arranged into a 4x4 square. /// @@ -329,7 +344,6 @@ public: API_FUNCTION() void SetChunkOverrideMaterial(API_PARAM(Ref) const Int2& patchCoord, API_PARAM(Ref) const Int2& chunkCoord, MaterialBase* value); #if TERRAIN_EDITING - /// /// Setups the terrain patch using the specified heightmap data. /// @@ -352,10 +366,6 @@ public: /// True if failed, otherwise false. API_FUNCTION() bool SetupPatchSplatMap(API_PARAM(Ref) const Int2& patchCoord, int32 index, int32 splatMapLength, const Color32* splatMap, bool forceUseVirtualStorage = false); -#endif - -public: -#if TERRAIN_EDITING /// /// Setups the terrain. Clears the existing data. /// diff --git a/Source/Engine/Terrain/TerrainPatch.cpp b/Source/Engine/Terrain/TerrainPatch.cpp index 61bbf7e3c..cadc9821c 100644 --- a/Source/Engine/Terrain/TerrainPatch.cpp +++ b/Source/Engine/Terrain/TerrainPatch.cpp @@ -17,6 +17,7 @@ #include "Engine/Threading/Threading.h" #if TERRAIN_EDITING #include "Engine/Core/Math/Packed.h" +#include "Engine/Core/Collections/ArrayExtensions.h" #include "Engine/Graphics/PixelFormatExtensions.h" #include "Engine/Graphics/RenderTools.h" #include "Engine/Graphics/RenderView.h" @@ -27,11 +28,6 @@ #include "Editor/Editor.h" #include "Engine/ContentImporters/AssetsImportingManager.h" #endif -#endif -#if TERRAIN_EDITING || TERRAIN_UPDATING -#include "Engine/Core/Collections/ArrayExtensions.h" -#endif -#if USE_EDITOR #include "Engine/Debug/DebugDraw.h" #endif #if TERRAIN_USE_PHYSICS_DEBUG @@ -90,7 +86,7 @@ void TerrainPatch::Init(Terrain* terrain, int16 x, int16 z) Splatmap[i] = nullptr; } _heightfield = nullptr; -#if TERRAIN_UPDATING +#if TERRAIN_EDITING _cachedHeightMap.Resize(0); _cachedHolesMask.Resize(0); _wasHeightModified = false; @@ -114,7 +110,7 @@ void TerrainPatch::Init(Terrain* terrain, int16 x, int16 z) TerrainPatch::~TerrainPatch() { -#if TERRAIN_UPDATING +#if TERRAIN_EDITING SAFE_DELETE(_dataHeightmap); for (int32 i = 0; i < TERRAIN_MAX_SPLATMAPS_COUNT; i++) { @@ -134,6 +130,13 @@ RawDataAsset* TerrainPatch::GetHeightfield() const return _heightfield.Get(); } +void TerrainPatch::GetTextures(GPUTexture*& heightmap, GPUTexture*& splatmap0, GPUTexture*& splatmap1) const +{ + heightmap = Heightmap->GetTexture(); + splatmap0 = Splatmap[0] ? Splatmap[0]->GetTexture() : nullptr; + splatmap1 = Splatmap[1] ? Splatmap[1]->GetTexture() : nullptr; +} + void TerrainPatch::RemoveLightmap() { for (auto& chunk : Chunks) @@ -178,7 +181,7 @@ void TerrainPatch::UpdateTransform() _collisionVertices.Resize(0); } -#if TERRAIN_EDITING || TERRAIN_UPDATING +#if TERRAIN_EDITING bool IsValidMaterial(const JsonAssetReference& e) { @@ -217,7 +220,7 @@ struct TerrainDataUpdateInfo // When using physical materials, then get splatmaps data required for per-triangle material indices void GetSplatMaps() { -#if TERRAIN_UPDATING +#if TERRAIN_EDITING if (SplatMaps[0]) return; if (UsePhysicalMaterials()) @@ -1021,7 +1024,7 @@ bool TerrainPatch::SetupHeightMap(int32 heightMapLength, const float* heightMap, _terrain->UpdateBounds(); _terrain->UpdateLayerBits(); -#if TERRAIN_UPDATING +#if TERRAIN_EDITING // Invalidate cache _cachedHeightMap.Resize(0); _cachedHolesMask.Resize(0); @@ -1169,7 +1172,7 @@ bool TerrainPatch::SetupSplatMap(int32 index, int32 splatMapLength, const Color3 } #endif -#if TERRAIN_UPDATING +#if TERRAIN_EDITING // Invalidate cache _cachedSplatMap[index].Resize(0); _wasSplatmapModified[index] = false; @@ -1191,7 +1194,7 @@ bool TerrainPatch::InitializeHeightMap() return SetupHeightMap(heightmap.Count(), heightmap.Get()); } -#if TERRAIN_UPDATING +#if TERRAIN_EDITING float* TerrainPatch::GetHeightmapData() { @@ -2631,7 +2634,7 @@ void TerrainPatch::Serialize(SerializeStream& stream, const void* otherObj) } stream.EndArray(); -#if TERRAIN_UPDATING +#if TERRAIN_EDITING SaveHeightData(); SaveSplatData(); #endif diff --git a/Source/Engine/Terrain/TerrainPatch.h b/Source/Engine/Terrain/TerrainPatch.h index 7d85c5b1c..79e56e6da 100644 --- a/Source/Engine/Terrain/TerrainPatch.h +++ b/Source/Engine/Terrain/TerrainPatch.h @@ -12,6 +12,10 @@ struct RayCastHit; class TerrainMaterialShader; +#ifndef TERRAIN_EDITING +#define TERRAIN_EDITING 1 +#endif + /// /// Represents single terrain patch made of 16 terrain chunks. /// @@ -34,7 +38,7 @@ private: void* _physicsHeightField; CriticalSection _collisionLocker; float _collisionScaleXZ; -#if TERRAIN_UPDATING +#if TERRAIN_EDITING Array _cachedHeightMap; Array _cachedHolesMask; Array _cachedSplatMap[TERRAIN_MAX_SPLATMAPS_COUNT]; @@ -189,6 +193,8 @@ public: return _bounds; } + void GetTextures(GPUTexture*& heightmap, GPUTexture*& splatmap0, GPUTexture*& splatmap1) const; + public: /// /// Removes the lightmap data from the terrain patch. @@ -220,7 +226,7 @@ public: /// The holes mask (optional). Normalized to 0-1 range values with holes mask per-vertex. Must match the heightmap dimensions. /// If set to true patch will use virtual storage by force. Otherwise it can use normal texture asset storage on drive (valid only during Editor). Runtime-created terrain can only use virtual storage (in RAM). /// True if failed, otherwise false. - API_FUNCTION() bool SetupHeightMap(int32 heightMapLength, API_PARAM(Ref) const float* heightMap, API_PARAM(Ref) const byte* holesMask = nullptr, bool forceUseVirtualStorage = false); + API_FUNCTION() bool SetupHeightMap(int32 heightMapLength, const float* heightMap, const byte* holesMask = nullptr, bool forceUseVirtualStorage = false); /// /// Setups the terrain patch layer weights using the specified splatmaps data. @@ -230,14 +236,12 @@ public: /// The splat map. Each array item contains 4 layer weights. /// If set to true patch will use virtual storage by force. Otherwise it can use normal texture asset storage on drive (valid only during Editor). Runtime-created terrain can only use virtual storage (in RAM). /// True if failed, otherwise false. - API_FUNCTION() bool SetupSplatMap(int32 index, int32 splatMapLength, API_PARAM(Ref) const Color32* splatMap, bool forceUseVirtualStorage = false); -#endif + API_FUNCTION() bool SetupSplatMap(int32 index, int32 splatMapLength, const Color32* splatMap, bool forceUseVirtualStorage = false); -#if TERRAIN_UPDATING /// - /// Gets the raw pointer to the heightmap data. + /// Gets the raw pointer to the heightmap data. Array size is square of Terrain.HeightmapSize. /// - /// The heightmap data. + /// The heightmap data. Null if empty or failed to access it. API_FUNCTION() float* GetHeightmapData(); /// @@ -246,9 +250,9 @@ public: API_FUNCTION() void ClearHeightmapCache(); /// - /// Gets the raw pointer to the holes mask data. + /// Gets the raw pointer to the holes mask data. Array size is square of Terrain.HeightmapSize. /// - /// The holes mask data. + /// The holes mask data. Null if empty/unused or failed to access it. API_FUNCTION() byte* GetHolesMaskData(); /// @@ -257,10 +261,10 @@ public: API_FUNCTION() void ClearHolesMaskCache(); /// - /// Gets the raw pointer to the splat map data. + /// Gets the raw pointer to the splat map data. Array size is square of Terrain.HeightmapSize. /// /// The zero-based index of the splatmap texture. - /// The splat map data. + /// The splat map data. Null if empty/unused or failed to access it. API_FUNCTION() Color32* GetSplatMapData(int32 index); /// @@ -280,7 +284,7 @@ public: /// The offset from the first row and column of the heightmap data (offset destination x and z start position). /// The size of the heightmap to modify (x and z). Amount of samples in each direction. /// True if failed, otherwise false. - API_FUNCTION() bool ModifyHeightMap(API_PARAM(Ref) const float* samples, API_PARAM(Ref) const Int2& modifiedOffset, API_PARAM(Ref) const Int2& modifiedSize); + API_FUNCTION() bool ModifyHeightMap(const float* samples, const Int2& modifiedOffset, const Int2& modifiedSize); /// /// Modifies the terrain patch holes mask with the given samples. @@ -289,7 +293,7 @@ public: /// The offset from the first row and column of the holes map data (offset destination x and z start position). /// The size of the holes map to modify (x and z). Amount of samples in each direction. /// True if failed, otherwise false. - API_FUNCTION() bool ModifyHolesMask(API_PARAM(Ref) const byte* samples, API_PARAM(Ref) const Int2& modifiedOffset, API_PARAM(Ref) const Int2& modifiedSize); + API_FUNCTION() bool ModifyHolesMask(const byte* samples, const Int2& modifiedOffset, const Int2& modifiedSize); /// /// Modifies the terrain patch splat map (layers mask) with the given samples. @@ -299,7 +303,7 @@ public: /// The offset from the first row and column of the splat map data (offset destination x and z start position). /// The size of the splat map to modify (x and z). Amount of samples in each direction. /// True if failed, otherwise false. - API_FUNCTION() bool ModifySplatMap(int32 index, API_PARAM(Ref) const Color32* samples, API_PARAM(Ref) const Int2& modifiedOffset, API_PARAM(Ref) const Int2& modifiedSize); + API_FUNCTION() bool ModifySplatMap(int32 index, const Color32* samples, const Int2& modifiedOffset, const Int2& modifiedSize); private: bool UpdateHeightData(struct TerrainDataUpdateInfo& info, const Int2& modifiedOffset, const Int2& modifiedSize, bool wasHeightRangeChanged, bool wasHeightChanged); diff --git a/Source/Engine/Tools/ModelTool/ModelTool.cpp b/Source/Engine/Tools/ModelTool/ModelTool.cpp index 00fb98cf6..7370c3010 100644 --- a/Source/Engine/Tools/ModelTool/ModelTool.cpp +++ b/Source/Engine/Tools/ModelTool/ModelTool.cpp @@ -588,6 +588,7 @@ void ModelTool::Options::Serialize(SerializeStream& stream, const void* otherObj SERIALIZE(SloppyOptimization); SERIALIZE(LODTargetError); SERIALIZE(ImportMaterials); + SERIALIZE(CreateEmptyMaterialSlots); SERIALIZE(ImportMaterialsAsInstances); SERIALIZE(InstanceToImportAs); SERIALIZE(ImportTextures); @@ -643,6 +644,7 @@ void ModelTool::Options::Deserialize(DeserializeStream& stream, ISerializeModifi DESERIALIZE(SloppyOptimization); DESERIALIZE(LODTargetError); DESERIALIZE(ImportMaterials); + DESERIALIZE(CreateEmptyMaterialSlots); DESERIALIZE(ImportMaterialsAsInstances); DESERIALIZE(InstanceToImportAs); DESERIALIZE(ImportTextures); @@ -1019,7 +1021,7 @@ bool ModelTool::ImportModel(const String& path, ModelData& data, Options& option options.ImportTypes |= ImportDataTypes::Skeleton; break; case ModelType::Prefab: - options.ImportTypes = ImportDataTypes::Geometry | ImportDataTypes::Nodes | ImportDataTypes::Animations; + options.ImportTypes = ImportDataTypes::Geometry | ImportDataTypes::Nodes | ImportDataTypes::Skeleton | ImportDataTypes::Animations; if (options.ImportMaterials) options.ImportTypes |= ImportDataTypes::Materials; if (options.ImportTextures) @@ -1045,6 +1047,8 @@ bool ModelTool::ImportModel(const String& path, ModelData& data, Options& option { for (auto& mesh : lod.Meshes) { + if (mesh->BlendShapes.IsEmpty()) + continue; for (int32 blendShapeIndex = mesh->BlendShapes.Count() - 1; blendShapeIndex >= 0; blendShapeIndex--) { auto& blendShape = mesh->BlendShapes[blendShapeIndex]; @@ -1209,7 +1213,9 @@ bool ModelTool::ImportModel(const String& path, ModelData& data, Options& option for (int32 i = 0; i < meshesCount; i++) { const auto mesh = data.LODs[0].Meshes[i]; - if (mesh->BlendIndices.IsEmpty() || mesh->BlendWeights.IsEmpty()) + + // If imported mesh has skeleton but no indices or weights then need to setup those (except in Prefab mode when we conditionally import meshes based on type) + if ((mesh->BlendIndices.IsEmpty() || mesh->BlendWeights.IsEmpty()) && data.Skeleton.Bones.HasItems() && (options.Type != ModelType::Prefab)) { auto indices = Int4::Zero; auto weights = Float4::UnitX; @@ -1326,7 +1332,7 @@ bool ModelTool::ImportModel(const String& path, ModelData& data, Options& option auto& texture = data.Textures[i]; // Auto-import textures - if (autoImportOutput.IsEmpty() || EnumHasNoneFlags(options.ImportTypes, ImportDataTypes::Textures) || texture.FilePath.IsEmpty()) + if (autoImportOutput.IsEmpty() || EnumHasNoneFlags(options.ImportTypes, ImportDataTypes::Textures) || texture.FilePath.IsEmpty() || options.CreateEmptyMaterialSlots) continue; String assetPath = GetAdditionalImportPath(autoImportOutput, importedFileNames, StringUtils::GetFileNameWithoutExtension(texture.FilePath)); #if COMPILE_WITH_ASSETS_IMPORTER @@ -1384,6 +1390,10 @@ bool ModelTool::ImportModel(const String& path, ModelData& data, Options& option } } + // The rest of the steps this function performs become irrelevant when we're only creating slots. + if (options.CreateEmptyMaterialSlots) + continue; + if (options.ImportMaterialsAsInstances) { // Create material instance @@ -2021,12 +2031,11 @@ bool ModelTool::ImportModel(const String& path, ModelData& data, Options& option #undef REMAP_VERTEX_BUFFER // Remap blend shapes - dstMesh->BlendShapes.Resize(srcMesh->BlendShapes.Count()); + dstMesh->BlendShapes.EnsureCapacity(srcMesh->BlendShapes.Count(), false); for (int32 blendShapeIndex = 0; blendShapeIndex < srcMesh->BlendShapes.Count(); blendShapeIndex++) { const auto& srcBlendShape = srcMesh->BlendShapes[blendShapeIndex]; - auto& dstBlendShape = dstMesh->BlendShapes[blendShapeIndex]; - + BlendShape dstBlendShape; dstBlendShape.Name = srcBlendShape.Name; dstBlendShape.Weight = srcBlendShape.Weight; dstBlendShape.Vertices.EnsureCapacity(srcBlendShape.Vertices.Count()); @@ -2035,17 +2044,12 @@ bool ModelTool::ImportModel(const String& path, ModelData& data, Options& option auto v = srcBlendShape.Vertices[i]; v.VertexIndex = remap[v.VertexIndex]; if (v.VertexIndex != ~0u) - { dstBlendShape.Vertices.Add(v); - } } - } - // Remove empty blend shapes - for (int32 blendShapeIndex = dstMesh->BlendShapes.Count() - 1; blendShapeIndex >= 0; blendShapeIndex--) - { - if (dstMesh->BlendShapes[blendShapeIndex].Vertices.IsEmpty()) - dstMesh->BlendShapes.RemoveAt(blendShapeIndex); + // Add only valid blend shapes + if (dstBlendShape.Vertices.HasItems()) + dstMesh->BlendShapes.Add(dstBlendShape); } // Optimize generated LOD @@ -2092,6 +2096,8 @@ bool ModelTool::ImportModel(const String& path, ModelData& data, Options& option { for (auto& mesh : lod.Meshes) { + if (mesh->BlendShapes.IsEmpty()) + continue; for (auto& blendShape : mesh->BlendShapes) { // Compute min/max for used vertex indices diff --git a/Source/Engine/Tools/ModelTool/ModelTool.h b/Source/Engine/Tools/ModelTool/ModelTool.h index bc96e8308..00d7028cf 100644 --- a/Source/Engine/Tools/ModelTool/ModelTool.h +++ b/Source/Engine/Tools/ModelTool/ModelTool.h @@ -311,16 +311,19 @@ public: public: // Materials // If checked, the importer will create materials for model meshes as specified in the file. - API_FIELD(Attributes="EditorOrder(400), EditorDisplay(\"Materials\"), VisibleIf(nameof(ShowGeometry))") + API_FIELD(Attributes="EditorOrder(399), EditorDisplay(\"Materials\"), VisibleIf(nameof(ShowGeometry))") bool ImportMaterials = true; + // If checked, the importer will create empty material slots for every material without importing materials nor textures. + API_FIELD(Attributes="EditorOrder(400), EditorDisplay(\"Materials\"), VisibleIf(nameof(ShowGeometry))") + bool CreateEmptyMaterialSlots = false; // If checked, the importer will create the model's materials as instances of a base material. - API_FIELD(Attributes="EditorOrder(401), EditorDisplay(\"Materials\"), VisibleIf(nameof(ImportMaterials)), VisibleIf(nameof(ShowGeometry))") + API_FIELD(Attributes="EditorOrder(401), EditorDisplay(\"Materials\"), VisibleIf(nameof(ImportMaterials)), VisibleIf(nameof(ShowGeometry)), VisibleIf(nameof(CreateEmptyMaterialSlots), true)") bool ImportMaterialsAsInstances = false; // The material used as the base material that will be instanced as the imported model's material. - API_FIELD(Attributes="EditorOrder(402), EditorDisplay(\"Materials\"), VisibleIf(nameof(ImportMaterialsAsInstances)), VisibleIf(nameof(ShowGeometry))") + API_FIELD(Attributes="EditorOrder(402), EditorDisplay(\"Materials\"), VisibleIf(nameof(ImportMaterialsAsInstances)), VisibleIf(nameof(ShowGeometry)), VisibleIf(nameof(CreateEmptyMaterialSlots), true)") AssetReference InstanceToImportAs; // If checked, the importer will import texture files used by the model and any embedded texture resources. - API_FIELD(Attributes="EditorOrder(410), EditorDisplay(\"Materials\"), VisibleIf(nameof(ShowGeometry))") + API_FIELD(Attributes="EditorOrder(410), EditorDisplay(\"Materials\"), VisibleIf(nameof(ShowGeometry)), VisibleIf(nameof(CreateEmptyMaterialSlots), true)") bool ImportTextures = true; // If checked, the importer will try to keep the model's current overridden material slots, instead of importing materials from the source file. API_FIELD(Attributes="EditorOrder(420), EditorDisplay(\"Materials\", \"Keep Overridden Materials\"), VisibleIf(nameof(ShowGeometry))") diff --git a/Source/Engine/Tools/TextureTool/TextureTool.cpp b/Source/Engine/Tools/TextureTool/TextureTool.cpp index bf4069fbe..f0d5c071d 100644 --- a/Source/Engine/Tools/TextureTool/TextureTool.cpp +++ b/Source/Engine/Tools/TextureTool/TextureTool.cpp @@ -414,6 +414,7 @@ bool TextureTool::UpdateTexture(GPUContext* context, GPUTexture* texture, int32 Array tempData; if (textureFormat != dataFormat) { + PROFILE_CPU_NAMED("ConvertTexture"); auto dataSampler = PixelFormatSampler::Get(dataFormat); auto textureSampler = PixelFormatSampler::Get(textureFormat); if (!dataSampler || !textureSampler) diff --git a/Source/Engine/UI/GUI/Special/RadialMenu.cs b/Source/Engine/UI/GUI/Special/RadialMenu.cs index 060c4ef90..d52294da8 100644 --- a/Source/Engine/UI/GUI/Special/RadialMenu.cs +++ b/Source/Engine/UI/GUI/Special/RadialMenu.cs @@ -5,14 +5,14 @@ using System; namespace FlaxEngine.GUI { /// - /// Radial menu control that arranges child controls (of type Image) in a circle. + /// Radial menu control that arranges child controls (of type ) in a circle. /// /// public class RadialMenu : ContainerControl { private bool _materialIsDirty = true; private float _angle; - private float _selectedSegment; + private int _selectedSegment; private int _highlightSegment = -1; private MaterialBase _material; private MaterialInstance _materialInstance; @@ -27,7 +27,7 @@ namespace FlaxEngine.GUI private bool ShowMatProp => _material != null; /// - /// The material to use for menu background drawing. + /// The material used for menu background drawing. /// [EditorOrder(1)] public MaterialBase Material @@ -44,7 +44,7 @@ namespace FlaxEngine.GUI } /// - /// Gets or sets the edge offset. + /// Gets or sets the offset of the outer edge from the bounds of the Control. /// [EditorOrder(2), Range(0, 1)] public float EdgeOffset @@ -59,7 +59,7 @@ namespace FlaxEngine.GUI } /// - /// Gets or sets the thickness. + /// Gets or sets the thickness of the menu. /// [EditorOrder(3), Range(0, 1), VisibleIf(nameof(ShowMatProp))] public float Thickness @@ -74,7 +74,7 @@ namespace FlaxEngine.GUI } /// - /// Gets or sets control background color (transparent color (alpha=0) means no background rendering). + /// Gets or sets control background color (transparent color means no background rendering). /// [VisibleIf(nameof(ShowMatProp))] public new Color BackgroundColor @@ -88,7 +88,7 @@ namespace FlaxEngine.GUI } /// - /// Gets or sets the color of the highlight. + /// Gets or sets the color of the outer edge highlight. /// [VisibleIf(nameof(ShowMatProp))] public Color HighlightColor @@ -130,19 +130,43 @@ namespace FlaxEngine.GUI } /// - /// The selected callback + /// The material instance of used to draw the menu. + /// + [HideInEditor] + public MaterialInstance MaterialInstance => _materialInstance; + + /// + /// The selected callback. /// [HideInEditor] public Action Selected; /// - /// The allow change selection when inside + /// Invoked when the hovered segment is changed. + /// + [HideInEditor] + public Action HoveredSelectionChanged; + + /// + /// The selected segment. + /// + [HideInEditor] + public int SelectedSegment => _selectedSegment; + + /// + /// Allows the selected to change when the mouse is moved in the empty center of the menu. /// [VisibleIf(nameof(ShowMatProp))] public bool AllowChangeSelectionWhenInside; /// - /// The center as button + /// Allows the selected to change when the mouse is moved outside of the menu. + /// + [VisibleIf(nameof(ShowMatProp))] + public bool AllowChangeSelectionWhenOutside; + + /// + /// Wether the center is a button. /// [VisibleIf(nameof(ShowMatProp))] public bool CenterAsButton; @@ -225,7 +249,7 @@ namespace FlaxEngine.GUI var min = ((1 - _edgeOffset) - _thickness) * USize * 0.5f; var max = (1 - _edgeOffset) * USize * 0.5f; var val = ((USize * 0.5f) - location).Length; - if (Mathf.IsInRange(val, min, max) || val < min && AllowChangeSelectionWhenInside) + if (Mathf.IsInRange(val, min, max) || val < min && AllowChangeSelectionWhenInside || val > max && AllowChangeSelectionWhenOutside) { UpdateAngle(ref location); } @@ -276,7 +300,7 @@ namespace FlaxEngine.GUI var min = ((1 - _edgeOffset) - _thickness) * USize * 0.5f; var max = (1 - _edgeOffset) * USize * 0.5f; var val = ((USize * 0.5f) - location).Length; - if (Mathf.IsInRange(val, min, max) || val < min && AllowChangeSelectionWhenInside) + if (Mathf.IsInRange(val, min, max) || val < min && AllowChangeSelectionWhenInside || val > max && AllowChangeSelectionWhenOutside) { UpdateAngle(ref location); } @@ -347,6 +371,28 @@ namespace FlaxEngine.GUI base.PerformLayout(force); } + /// + /// Updates the current angle and selected segment of the radial menu based on the specified location inside of the control. + /// + /// The position used to determine the angle and segment selection within the radial menu. + public void UpdateAngle(ref Float2 location) + { + float previousSelectedSegment = _selectedSegment; + + var size = new Float2(USize); + var p = (size * 0.5f) - location; + var sa = (1.0f / _segmentCount) * Mathf.TwoPi; + _angle = Mathf.Atan2(p.X, p.Y); + _angle = Mathf.Ceil((_angle - (sa * 0.5f)) / sa) * sa; + _selectedSegment = Mathf.RoundToInt((_angle < 0 ? Mathf.TwoPi + _angle : _angle) / sa); + if (float.IsNaN(_angle) || float.IsInfinity(_angle)) + _angle = 0; + _materialInstance.SetParameterValue("RadialMenu_Rotation", -_angle + Mathf.Pi); + + if (previousSelectedSegment != _selectedSegment) + HoveredSelectionChanged?.Invoke((int)_selectedSegment); + } + private void UpdateSelectionColor() { Color color; @@ -368,20 +414,6 @@ namespace FlaxEngine.GUI _materialInstance.SetParameterValue("RadialMenu_SelectionColor", color); } - private void UpdateAngle(ref Float2 location) - { - var size = new Float2(USize); - var p = (size * 0.5f) - location; - var sa = (1.0f / _segmentCount) * Mathf.TwoPi; - _angle = Mathf.Atan2(p.X, p.Y); - _angle = Mathf.Ceil((_angle - (sa * 0.5f)) / sa) * sa; - _selectedSegment = _angle; - _selectedSegment = Mathf.RoundToInt((_selectedSegment < 0 ? Mathf.TwoPi + _selectedSegment : _selectedSegment) / sa); - if (float.IsNaN(_angle) || float.IsInfinity(_angle)) - _angle = 0; - _materialInstance.SetParameterValue("RadialMenu_Rotation", -_angle + Mathf.Pi); - } - private static Float2 Rotate2D(Float2 point, float angle) { return new Float2(Mathf.Cos(angle) * point.X + Mathf.Sin(angle) * point.Y, diff --git a/Source/Platforms/Android/Binaries/ThirdParty/ARM64/libPhysXCharacterKinematic_static_64.a b/Source/Platforms/Android/Binaries/ThirdParty/ARM64/libPhysXCharacterKinematic_static_64.a index 884943f02..b532d98dc 100644 --- a/Source/Platforms/Android/Binaries/ThirdParty/ARM64/libPhysXCharacterKinematic_static_64.a +++ b/Source/Platforms/Android/Binaries/ThirdParty/ARM64/libPhysXCharacterKinematic_static_64.a @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5c07afaaa62724f7f8f532a1bc65a5fc50753e266495248a608d9f4b9476aa0b -size 2025020 +oid sha256:7b0ec52b292d42f583822883a7275bf1f311bd46866dd5e9ab3489c025a4fd5b +size 2024932 diff --git a/Source/Platforms/Android/Binaries/ThirdParty/ARM64/libPhysXCommon_static_64.a b/Source/Platforms/Android/Binaries/ThirdParty/ARM64/libPhysXCommon_static_64.a index 59899248c..8ea5ddcca 100644 --- a/Source/Platforms/Android/Binaries/ThirdParty/ARM64/libPhysXCommon_static_64.a +++ b/Source/Platforms/Android/Binaries/ThirdParty/ARM64/libPhysXCommon_static_64.a @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9817730bdeae1cfeb5207470d721b27f448cdd36b9eba85da73dec59e8a5bf99 -size 52205712 +oid sha256:1f3e7321d444eb054d7ce10e71be4deee9d58133157557e7b839ecd9ac48fc98 +size 52204304 diff --git a/Source/Platforms/Android/Binaries/ThirdParty/ARM64/libPhysXCooking_static_64.a b/Source/Platforms/Android/Binaries/ThirdParty/ARM64/libPhysXCooking_static_64.a index 37e24dc4c..e0f395e4f 100644 --- a/Source/Platforms/Android/Binaries/ThirdParty/ARM64/libPhysXCooking_static_64.a +++ b/Source/Platforms/Android/Binaries/ThirdParty/ARM64/libPhysXCooking_static_64.a @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:524ded53592c687bc834c9db24c6ccfa8449f3392ec3fd99627c87cf7f069368 -size 111200 +oid sha256:d322d65623ce682730f28c0b390b36102eabc4ae453af9dfd813cc1e189c1628 +size 111192 diff --git a/Source/Platforms/Android/Binaries/ThirdParty/ARM64/libPhysXExtensions_static_64.a b/Source/Platforms/Android/Binaries/ThirdParty/ARM64/libPhysXExtensions_static_64.a index b13b12074..697046535 100644 --- a/Source/Platforms/Android/Binaries/ThirdParty/ARM64/libPhysXExtensions_static_64.a +++ b/Source/Platforms/Android/Binaries/ThirdParty/ARM64/libPhysXExtensions_static_64.a @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:759aa0379faa5f010c66654e46d352f24c1f837507ffa0178022546540f4ca0f -size 31683652 +oid sha256:5981d7ce59540fe829bab21485dbb83ab82f860ed15ca720f8c0d621bcd1296b +size 7873980 diff --git a/Source/Platforms/Android/Binaries/ThirdParty/ARM64/libPhysXFoundation_static_64.a b/Source/Platforms/Android/Binaries/ThirdParty/ARM64/libPhysXFoundation_static_64.a index 19216936b..7a4e611b4 100644 --- a/Source/Platforms/Android/Binaries/ThirdParty/ARM64/libPhysXFoundation_static_64.a +++ b/Source/Platforms/Android/Binaries/ThirdParty/ARM64/libPhysXFoundation_static_64.a @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6738a094c3dbe50a5820c83944b3992211bd3e372b1ccfbd2f3acb0541c04d2b -size 570452 +oid sha256:e915aa5a013c099fe6ced91c50f1fa8b5e46e3778b08d09d0a12d59f6252d87f +size 570404 diff --git a/Source/Platforms/Android/Binaries/ThirdParty/ARM64/libPhysXPvdSDK_static_64.a b/Source/Platforms/Android/Binaries/ThirdParty/ARM64/libPhysXPvdSDK_static_64.a deleted file mode 100644 index e681dfc1b..000000000 --- a/Source/Platforms/Android/Binaries/ThirdParty/ARM64/libPhysXPvdSDK_static_64.a +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:7369e2822a705f592c67942c0ba354694b857fa9d83db6bed4dc0dcf3a4875f0 -size 3405028 diff --git a/Source/Platforms/Android/Binaries/ThirdParty/ARM64/libPhysXVehicle2_static_64.a b/Source/Platforms/Android/Binaries/ThirdParty/ARM64/libPhysXVehicle2_static_64.a index edb89c6a0..78008e4b2 100644 --- a/Source/Platforms/Android/Binaries/ThirdParty/ARM64/libPhysXVehicle2_static_64.a +++ b/Source/Platforms/Android/Binaries/ThirdParty/ARM64/libPhysXVehicle2_static_64.a @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7bbb5ba66f5e47ee53fd099c805e94d66f47cb66c44ec2339f06b66ea89c6100 -size 1543576 +oid sha256:f6a43c86163970f92cb49f508c175e8cbd80ee83fc193d10011eb5905f20e0ab +size 1543440 diff --git a/Source/Platforms/Android/Binaries/ThirdParty/ARM64/libPhysXVehicle_static_64.a b/Source/Platforms/Android/Binaries/ThirdParty/ARM64/libPhysXVehicle_static_64.a index 815ea1e4a..81b2061c1 100644 --- a/Source/Platforms/Android/Binaries/ThirdParty/ARM64/libPhysXVehicle_static_64.a +++ b/Source/Platforms/Android/Binaries/ThirdParty/ARM64/libPhysXVehicle_static_64.a @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4eff2cf32d6bfed5f32d26b5478ec4e1eda31d3959e71f79fa6e205905b1e67c -size 12277380 +oid sha256:cd5d1083b5479d322b98b56b94dc42139c5965a5c7434fe058382f760708273b +size 2364570 diff --git a/Source/Platforms/Android/Binaries/ThirdParty/ARM64/libPhysX_static_64.a b/Source/Platforms/Android/Binaries/ThirdParty/ARM64/libPhysX_static_64.a index a37ba9028..7fc068ccb 100644 --- a/Source/Platforms/Android/Binaries/ThirdParty/ARM64/libPhysX_static_64.a +++ b/Source/Platforms/Android/Binaries/ThirdParty/ARM64/libPhysX_static_64.a @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ee3f4e2cc3160b7556dd8678c49f27160d401fbef729c81568dbc184b07fbab5 -size 37483230 +oid sha256:ae10a05b256896db3868df5d9914d5ffd10efbcc64dea046ba11946514625b7e +size 34853698 diff --git a/Source/Platforms/Windows/Binaries/ThirdParty/x64/FastXml_64.pdb b/Source/Platforms/Windows/Binaries/ThirdParty/x64/FastXml_64.pdb index 43a87a79d..ab822e5d2 100644 --- a/Source/Platforms/Windows/Binaries/ThirdParty/x64/FastXml_64.pdb +++ b/Source/Platforms/Windows/Binaries/ThirdParty/x64/FastXml_64.pdb @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:132883c177c47a532afd6fa33b5a2f498cd310001400062a6df33743c4d5e784 +oid sha256:dc758a8cf6f50caf75476128ceffc41afbb6b8aaa45e81cd17ef694c18bec1a3 size 118784 diff --git a/Source/Platforms/Windows/Binaries/ThirdParty/x64/LowLevelAABB_64.pdb b/Source/Platforms/Windows/Binaries/ThirdParty/x64/LowLevelAABB_64.pdb index 819f1d896..acf9b53bc 100644 --- a/Source/Platforms/Windows/Binaries/ThirdParty/x64/LowLevelAABB_64.pdb +++ b/Source/Platforms/Windows/Binaries/ThirdParty/x64/LowLevelAABB_64.pdb @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7668042f2a51ef982925d28ec57efe89f3adfc0f0bdd45c0d836ea810010d0a1 -size 593920 +oid sha256:0356328c34bc457903e70c6f3c9d984f288ff3917564cb5f5bd2efb84c8a0b9f +size 585728 diff --git a/Source/Platforms/Windows/Binaries/ThirdParty/x64/LowLevelDynamics_64.pdb b/Source/Platforms/Windows/Binaries/ThirdParty/x64/LowLevelDynamics_64.pdb index e96418eed..5c8c6b364 100644 --- a/Source/Platforms/Windows/Binaries/ThirdParty/x64/LowLevelDynamics_64.pdb +++ b/Source/Platforms/Windows/Binaries/ThirdParty/x64/LowLevelDynamics_64.pdb @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f5eb77e8531012b27e7616f097e17bd15a0d30f0314f311bacefa67610eb873c -size 1339392 +oid sha256:8a2a326575d5b6b468dc896399bb9bcb324a44f9097cb6b78457875737df7356 +size 1323008 diff --git a/Source/Platforms/Windows/Binaries/ThirdParty/x64/LowLevel_64.pdb b/Source/Platforms/Windows/Binaries/ThirdParty/x64/LowLevel_64.pdb index a6f6b58f8..2a28827ee 100644 --- a/Source/Platforms/Windows/Binaries/ThirdParty/x64/LowLevel_64.pdb +++ b/Source/Platforms/Windows/Binaries/ThirdParty/x64/LowLevel_64.pdb @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c5ed568ad39c0d14de2e51b413657cc965ee2e831b4b6bc7e6418d16fa697163 -size 1257472 +oid sha256:7313a497a5a851d86d7a2f2161f5d4a01274b031bb3d3d0ccb279fc9db199e24 +size 1241088 diff --git a/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXCharacterKinematic_static_64.lib b/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXCharacterKinematic_static_64.lib index c9e7b5dfe..4258cc709 100644 --- a/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXCharacterKinematic_static_64.lib +++ b/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXCharacterKinematic_static_64.lib @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:603e6025b22349137e7244bf6ca4056e09e6947660469269da1944d9f92e02fb -size 1306220 +oid sha256:46f69808637c2a42353e3935f2f6b3c3babb3bf0853e4551b70ce7e1f63a19ec +size 1319326 diff --git a/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXCharacterKinematic_static_64.pdb b/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXCharacterKinematic_static_64.pdb index b82c855c8..2f9cdc350 100644 --- a/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXCharacterKinematic_static_64.pdb +++ b/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXCharacterKinematic_static_64.pdb @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1c870f987df7407b2edc5615a5fb4d3cb942af801d0260a13de1d27e52eac4fb -size 675840 +oid sha256:4cfb89b2836af0efe36f6e062262d4720153c9c3c396bfc6adb5098eb89e46c3 +size 667648 diff --git a/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXCommon_static_64.lib b/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXCommon_static_64.lib index 56a97a846..130cfe8a1 100644 --- a/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXCommon_static_64.lib +++ b/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXCommon_static_64.lib @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d2b2093cf1f72c4135f653875f0fe0fa33d6696d34c29b6ac8d4ac3bb7aa92c1 -size 26130976 +oid sha256:935f6ea3bc1162acabadeaec6b34cc81445f8039afbdc0edd284e7200cf662d8 +size 24990306 diff --git a/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXCommon_static_64.pdb b/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXCommon_static_64.pdb index 4c3883939..24e7628a4 100644 --- a/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXCommon_static_64.pdb +++ b/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXCommon_static_64.pdb @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e0c09e6550a0ab319a7cde6382cc31ba31113d54d8d779444435e56213d07285 -size 1822720 +oid sha256:65352f47a3e32ca16ae6c5cb672ff9a5ea5098a4ade33a080accf5e57054f679 +size 1781760 diff --git a/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXCooking_static_64.lib b/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXCooking_static_64.lib index 7a47a2ddb..03a130cf4 100644 --- a/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXCooking_static_64.lib +++ b/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXCooking_static_64.lib @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ea85451d201ceeea71fd46bb01b5dc3501a05c77538bb85ae665ec6c0385c350 -size 130200 +oid sha256:3f786c5d71caa92ad50e186da67ea1564213d593c709134a25cbced5c1184338 +size 128984 diff --git a/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXCooking_static_64.pdb b/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXCooking_static_64.pdb index c79307c68..2ae2ecf13 100644 --- a/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXCooking_static_64.pdb +++ b/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXCooking_static_64.pdb @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7f18f710748a40b3be245f1027748ed6c9fe6058e049c3926632c0658dd983f4 -size 397312 +oid sha256:e22e8f2cf72d6c4d5fa35e426e6e0cc41e63402dc85aad5a1e65bc34bfd0146c +size 389120 diff --git a/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXExtensions_static_64.lib b/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXExtensions_static_64.lib index 7d543561d..4207c354d 100644 --- a/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXExtensions_static_64.lib +++ b/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXExtensions_static_64.lib @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f880ee07970656a19ac9a811f16be3edeed06834886606f2385bbcce9389ea4c -size 17300374 +oid sha256:0fce079940f5641113cb0d27c874923ab47a2e8ee286764c5711159c0e5f5bea +size 5639208 diff --git a/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXExtensions_static_64.pdb b/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXExtensions_static_64.pdb index b6401abd3..178fb18f8 100644 --- a/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXExtensions_static_64.pdb +++ b/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXExtensions_static_64.pdb @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:abd53b7899e7bce0b2180189d8c28df8b38107bb90cdd6078069c9d869169d9a -size 5156864 +oid sha256:7f28ef88132564ade7d813a9bfa667ce4c50e407ce3d6bcf2f386e8f5da98e16 +size 1740800 diff --git a/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXFoundation_static_64.lib b/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXFoundation_static_64.lib index dcb1935ee..3f08fa2c4 100644 --- a/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXFoundation_static_64.lib +++ b/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXFoundation_static_64.lib @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3f84c317ca2181e564fd7f6814898f9a1c738e3f157e5974da1fcf72d6abe8f9 -size 585126 +oid sha256:8685d51d5b7b69ee945da97e026cad77b98f91c564aae73176ca86c0d5f1d1b4 +size 571940 diff --git a/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXFoundation_static_64.pdb b/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXFoundation_static_64.pdb index f784a8e8e..e074d0436 100644 --- a/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXFoundation_static_64.pdb +++ b/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXFoundation_static_64.pdb @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4c4094a92b9252e8f0dee08600ef5f93fa01abb9b21fe4a2944114d41cd143ec -size 299008 +oid sha256:e0c44d3c2b487797eaefed7708d5389a366fa5217f3c5d6d06fdca00871de942 +size 315392 diff --git a/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXPvdSDK_static_64.lib b/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXPvdSDK_static_64.lib deleted file mode 100644 index 52b0147b0..000000000 --- a/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXPvdSDK_static_64.lib +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:747ed5f9e098e9c8e0a75cdfdfd09e82b338904b5916cc60df45d585f398b215 -size 2073834 diff --git a/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXPvdSDK_static_64.pdb b/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXPvdSDK_static_64.pdb deleted file mode 100644 index 3778728ef..000000000 --- a/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXPvdSDK_static_64.pdb +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:227c4cddf1a1326c6f6a9cfb106c592f75433fcf047971f63c8bc992c6f16369 -size 946176 diff --git a/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXTask_64.pdb b/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXTask_64.pdb index ed9215af3..06bee0418 100644 --- a/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXTask_64.pdb +++ b/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXTask_64.pdb @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ea9f74cc743bf588cbb9e6a5dbe544551eefe6be2ffa1c0232908159f3fd2612 +oid sha256:9898aebf247d35e2b27b0107fc7833abb5b2cc45a7bb5581f1f5d25a40e61fbf size 143360 diff --git a/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXVehicle2_static_64.lib b/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXVehicle2_static_64.lib index 1c7cb3bc0..b5abd5493 100644 --- a/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXVehicle2_static_64.lib +++ b/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXVehicle2_static_64.lib @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9c0c1865cba540abc026208ebf09653bb30f1ab9f800d65d53ea716e9b21ba64 -size 1125480 +oid sha256:56a251bf1d01a66e728618bbc53c3e54a8039c40465152f76868e37802137172 +size 1142828 diff --git a/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXVehicle2_static_64.pdb b/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXVehicle2_static_64.pdb index 11ae60a06..79025e37d 100644 --- a/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXVehicle2_static_64.pdb +++ b/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXVehicle2_static_64.pdb @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f83d273d0419023214b3f5094bfc23bd567c495bcf262fb6b2d696427a390f19 -size 733184 +oid sha256:32f10a769eb31b14338b8f858b7a4d462fbc02d3b96c9f3ecc12f12aa8ccb4ce +size 724992 diff --git a/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXVehicle_static_64.lib b/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXVehicle_static_64.lib index c71216d38..4784302b8 100644 --- a/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXVehicle_static_64.lib +++ b/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXVehicle_static_64.lib @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:64d9bd9c981905263b5d10ab9a9b2153785b58d6405ad99ed5119d4b70b55f51 -size 5843352 +oid sha256:5ae79e8eaee91bc6db4b0b99d5c814b9609d323f7b2c0ffaeeccf8e09b4cd4be +size 1550810 diff --git a/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXVehicle_static_64.pdb b/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXVehicle_static_64.pdb index 67447c121..47b31ec74 100644 --- a/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXVehicle_static_64.pdb +++ b/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXVehicle_static_64.pdb @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:eba825400abc8140c3d496ff0ad6df25ae9b4d4d2acb63db81171250d874e8b9 -size 3862528 +oid sha256:dcecb48dfed2969626a61197f6edc95dee1bdaf598c4567278704d0babffe812 +size 602112 diff --git a/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysX_static_64.lib b/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysX_static_64.lib index 8a0399bd7..beed86282 100644 --- a/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysX_static_64.lib +++ b/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysX_static_64.lib @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8311b062fb2153604f3eddee2d2a5720beb7f571159e8b1ed8c9fa89d3a3d6c5 -size 34826088 +oid sha256:4d2c83f579a3c96adc604f20ad0219016cad04c21a78ac539cd38686b2556de8 +size 33511310 diff --git a/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysX_static_64.pdb b/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysX_static_64.pdb index 6b3fbcb2d..1a0ffa3b8 100644 --- a/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysX_static_64.pdb +++ b/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysX_static_64.pdb @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a332f82798acafc6423c40e09223d7781abc43fbe5cbe1aed9069937ed7de180 -size 4558848 +oid sha256:0522612dd80ec1739c244136e42221e25a17db0d8d94bc860aa78469c0d0b329 +size 3502080 diff --git a/Source/Platforms/Windows/Binaries/ThirdParty/x64/SceneQuery_64.pdb b/Source/Platforms/Windows/Binaries/ThirdParty/x64/SceneQuery_64.pdb index c6196ff06..1ce7cb454 100644 --- a/Source/Platforms/Windows/Binaries/ThirdParty/x64/SceneQuery_64.pdb +++ b/Source/Platforms/Windows/Binaries/ThirdParty/x64/SceneQuery_64.pdb @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ac1251babb3766e7e9bc2efc40873b43aa1ae55b331918c9dc790f82b9e77d4f +oid sha256:b3c3204aaded5ae0ea1acc502ca15597a2884658957ec36c778395e70321e853 size 544768 diff --git a/Source/Platforms/Windows/Binaries/ThirdParty/x64/SimulationController_64.pdb b/Source/Platforms/Windows/Binaries/ThirdParty/x64/SimulationController_64.pdb index 92b08e4bc..d42fb5191 100644 --- a/Source/Platforms/Windows/Binaries/ThirdParty/x64/SimulationController_64.pdb +++ b/Source/Platforms/Windows/Binaries/ThirdParty/x64/SimulationController_64.pdb @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b11081d87071515d65544c9265b9bd09bb66af0810fbd6ec03bd28512892466b -size 2699264 +oid sha256:b2986a02de98efa5ae0f1883d6ed5af91d042d3bad136c8883f6e6a47d2e91a8 +size 2674688 diff --git a/Source/Platforms/Windows/Binaries/ThirdParty/x64/tint.exe b/Source/Platforms/Windows/Binaries/ThirdParty/x64/tint.exe index 6ea5660db..cd50d13f8 100644 --- a/Source/Platforms/Windows/Binaries/ThirdParty/x64/tint.exe +++ b/Source/Platforms/Windows/Binaries/ThirdParty/x64/tint.exe @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1e4603bb8a2731447e612892257067e2aff14ff02da5426f5020f1d1173c1748 -size 6757376 +oid sha256:70f019ebb99345bd721b381d8ebe7c18f5c708af56785b870273b5bce1a6e534 +size 5039616 diff --git a/Source/Tools/Flax.Build/CommandLine.cs b/Source/Tools/Flax.Build/CommandLine.cs index 226ba9913..bc54675f5 100644 --- a/Source/Tools/Flax.Build/CommandLine.cs +++ b/Source/Tools/Flax.Build/CommandLine.cs @@ -6,6 +6,8 @@ using System.ComponentModel; using System.IO; using System.Linq; using System.Reflection; +using System.Text.Json; +using System.Text.Json.Serialization; namespace Flax.Build { @@ -407,6 +409,15 @@ namespace Flax.Build Configure(GetMembers(obj), obj, configuration); } + internal static void ConfigureChild(Type type, Dictionary configuration, string name = null) + { + if (configuration.TryGetValue(name ?? type.Name, out var subConfig) && subConfig?.Length != 0) + { + var child = JsonSerializer.Deserialize>(subConfig.AsSpan(), ProjectInfo.JsonOptions); + Configure(type, child); + } + } + private static void Configure(Dictionary members, object instance, string commandLine) { if (commandLine == null) diff --git a/Source/Tools/Flax.Build/Configuration.cs b/Source/Tools/Flax.Build/Configuration.cs index 9f2796767..8a662df10 100644 --- a/Source/Tools/Flax.Build/Configuration.cs +++ b/Source/Tools/Flax.Build/Configuration.cs @@ -259,6 +259,42 @@ namespace Flax.Build } } + /// + /// Platform-specific configuration for Windows. + /// + public static partial class WindowsConfiguration + { + /// + /// [Windows] True if SDL support should be enabled. + /// + [CommandLine("useSdl", "1 to enable SDL support in build on Windows")] + public static bool UseSDL = false; + } + + /// + /// Platform-specific configuration for Linux. + /// + public static partial class LinuxConfiguration + { + /// + /// [Linux] True if SDL support should be enabled. + /// + [CommandLine("useSdl", "1 to enable SDL support in build on Linux")] + public static bool UseSDL = false; + } + + /// + /// Platform-specific configuration for Mac. + /// + public static partial class MacConfiguration + { + /// + /// [Mac] True if SDL support should be enabled. + /// + [CommandLine("useSdl", "1 to enable SDL support in build on Mac")] + public static bool UseSDL = false; + } + /// /// The engine configuration options. /// @@ -317,9 +353,11 @@ namespace Flax.Build switch (options.Platform.Target) { case TargetPlatform.Windows: - case TargetPlatform.Linux: + return UseSDL && WindowsConfiguration.UseSDL; case TargetPlatform.Mac: - return UseSDL; + return UseSDL && MacConfiguration.UseSDL; + case TargetPlatform.Linux: + return UseSDL && LinuxConfiguration.UseSDL; case TargetPlatform.Web: return true; default: return false; diff --git a/Source/Tools/Flax.Build/Deploy/Deployment.Editor.cs b/Source/Tools/Flax.Build/Deploy/Deployment.Editor.cs index f786bdc51..15f42c3ae 100644 --- a/Source/Tools/Flax.Build/Deploy/Deployment.Editor.cs +++ b/Source/Tools/Flax.Build/Deploy/Deployment.Editor.cs @@ -201,7 +201,7 @@ namespace Flax.Deploy Log.Info("Building disk image..."); if (File.Exists(dmgPath)) File.Delete(dmgPath); - Utilities.Run("hdiutil", $"create -srcFolder \"{appPath}\" -o \"{dmgPath}\"", null, null, Utilities.RunOptions.Default | Utilities.RunOptions.ThrowExceptionOnError); + Utilities.Run("hdiutil", $"create -srcFolder \"{appPath}\" -o \"{dmgPath}\" -force", null, null, Utilities.RunOptions.Default | Utilities.RunOptions.ThrowExceptionOnError); CodeSign(dmgPath); Log.Info("Output disk image size: " + Utilities.GetFileSize(dmgPath)); diff --git a/Source/Tools/Flax.Build/Deps/Dependencies/Assimp.cs b/Source/Tools/Flax.Build/Deps/Dependencies/Assimp.cs index 629a69070..9efcd84de 100644 --- a/Source/Tools/Flax.Build/Deps/Dependencies/Assimp.cs +++ b/Source/Tools/Flax.Build/Deps/Dependencies/Assimp.cs @@ -153,9 +153,9 @@ namespace Flax.Deps.Dependencies { var envVars = new Dictionary { - { "CC", "clang-" + Configuration.LinuxClangMinVer }, - { "CC_FOR_BUILD", "clang-" + Configuration.LinuxClangMinVer }, - { "CXX", "clang-" + Configuration.LinuxClangMinVer }, + { "CC", "clang-" + LinuxConfiguration.ClangMinVer }, + { "CC_FOR_BUILD", "clang-" + LinuxConfiguration.ClangMinVer }, + { "CXX", "clang-" + LinuxConfiguration.ClangMinVer }, { "CMAKE_BUILD_PARALLEL_LEVEL", CmakeBuildParallel }, }; diff --git a/Source/Tools/Flax.Build/Deps/Dependencies/NvCloth.cs b/Source/Tools/Flax.Build/Deps/Dependencies/NvCloth.cs index 2458eceec..a622db17d 100644 --- a/Source/Tools/Flax.Build/Deps/Dependencies/NvCloth.cs +++ b/Source/Tools/Flax.Build/Deps/Dependencies/NvCloth.cs @@ -148,13 +148,13 @@ namespace Flax.Deps.Dependencies cmakeArgs += " -DTARGET_BUILD_PLATFORM=linux -DNVCLOTH_CXX_FLAGS=\"-Wno-error=poison-system-directories -Wno-error=missing-include-dirs\""; cmakeName = "linux"; binariesPrefix = "lib"; - envVars.Add("CC", "clang-" + Configuration.LinuxClangMinVer); - envVars.Add("CXX", "clang++-" + Configuration.LinuxClangMinVer); + envVars.Add("CC", "clang-" + LinuxConfiguration.ClangMinVer); + envVars.Add("CXX", "clang++-" + LinuxConfiguration.ClangMinVer); break; case TargetPlatform.Web: cmakeArgs += " -DTARGET_BUILD_PLATFORM=web"; cmakeArgs += $" -DCMAKE_TOOLCHAIN_FILE=\"{EmscriptenSdk.Instance.CMakeToolchainPath}\""; - if (Configuration.WebThreads) + if (WebConfiguration.Threads) cmakeArgs += " -DNVCLOTH_CXX_FLAGS=\"-pthread\""; cmakeName = "web"; binariesPrefix = "lib"; diff --git a/Source/Tools/Flax.Build/Deps/Dependencies/OpenAL.cs b/Source/Tools/Flax.Build/Deps/Dependencies/OpenAL.cs index 37e446ce1..6477cf712 100644 --- a/Source/Tools/Flax.Build/Deps/Dependencies/OpenAL.cs +++ b/Source/Tools/Flax.Build/Deps/Dependencies/OpenAL.cs @@ -160,9 +160,9 @@ namespace Flax.Deps.Dependencies }; var envVars = new Dictionary { - { "CC", "clang-" + Configuration.LinuxClangMinVer }, - { "CC_FOR_BUILD", "clang-" + Configuration.LinuxClangMinVer }, - { "CXX", "clang++-" + Configuration.LinuxClangMinVer }, + { "CC", "clang-" + LinuxConfiguration.ClangMinVer }, + { "CC_FOR_BUILD", "clang-" + LinuxConfiguration.ClangMinVer }, + { "CXX", "clang++-" + LinuxConfiguration.ClangMinVer }, { "CMAKE_BUILD_PARALLEL_LEVEL", CmakeBuildParallel }, }; var config = $"-DALSOFT_REQUIRE_ALSA=ON " + diff --git a/Source/Tools/Flax.Build/Deps/Dependencies/PhysX.cs b/Source/Tools/Flax.Build/Deps/Dependencies/PhysX.cs index a636c3647..a7be407f0 100644 --- a/Source/Tools/Flax.Build/Deps/Dependencies/PhysX.cs +++ b/Source/Tools/Flax.Build/Deps/Dependencies/PhysX.cs @@ -48,6 +48,7 @@ namespace Flax.Deps.Dependencies throw new Exception(string.Format("Missing PhysX preset {0} (file: {1})", preset, presetPath)); var presetXml = new XmlDocument(); presetXml.Load(presetPath); + var usePVD = false; // Configure preset var cmakeSwitches = presetXml["preset"]["CMakeSwitches"]; @@ -59,6 +60,11 @@ namespace Flax.Deps.Dependencies ConfigureCmakeSwitch(cmakeSwitches, "NV_USE_STATIC_WINCRT", "False"); ConfigureCmakeSwitch(cmakeSwitches, "NV_USE_DEBUG_WINCRT", "False"); ConfigureCmakeSwitch(cmakeSwitches, "PX_FLOAT_POINT_PRECISE_MATH", "False"); + if (usePVD) + { + // PVD depends on metadata and serialization code striped from shipping builds + ConfigureCmakeSwitch(cmakeSwitches, "PX_SERIALIZATION", "True"); + } var cmakeParams = presetXml["preset"]["CMakeParams"]; switch (targetPlatform) { @@ -74,20 +80,20 @@ namespace Flax.Deps.Dependencies ConfigureCmakeSwitch(cmakeParams, "PHYSX_CXX_FLAGS", "\"-Wno-error=format -Wno-error=unused-but-set-variable -Wno-error=switch-default -Wno-error=invalid-offsetof -Wno-error=unsafe-buffer-usage -Wno-error=unsafe-buffer-usage-in-libc-call -Wno-error=missing-include-dirs\""); break; case TargetPlatform.Android: - ConfigureCmakeSwitch(cmakeParams, "CMAKE_INSTALL_PREFIX", $"install/android-{Configuration.AndroidPlatformApi}/PhysX"); - ConfigureCmakeSwitch(cmakeParams, "ANDROID_NATIVE_API_LEVEL", $"android-{Configuration.AndroidPlatformApi}"); + ConfigureCmakeSwitch(cmakeParams, "CMAKE_INSTALL_PREFIX", $"install/android-{AndroidConfiguration.PlatformApi}/PhysX"); + ConfigureCmakeSwitch(cmakeParams, "ANDROID_NATIVE_API_LEVEL", $"android-{AndroidConfiguration.PlatformApi}"); ConfigureCmakeSwitch(cmakeParams, "ANDROID_ABI", AndroidToolchain.GetAbiName(architecture)); break; case TargetPlatform.Mac: - ConfigureCmakeSwitch(cmakeParams, "CMAKE_OSX_DEPLOYMENT_TARGET", Configuration.MacOSXMinVer); + ConfigureCmakeSwitch(cmakeParams, "CMAKE_OSX_DEPLOYMENT_TARGET", MacConfiguration.MacOSXMinVer); ConfigureCmakeSwitch(cmakeParams, "PHYSX_CXX_FLAGS", "\"-Wno-error=format -Wno-error=unused-but-set-variable -Wno-error=switch-default -Wno-error=invalid-offsetof -Wno-error=unsafe-buffer-usage -Wno-error=unsafe-buffer-usage-in-libc-call -Wno-error=missing-include-dirs\""); break; case TargetPlatform.iOS: - ConfigureCmakeSwitch(cmakeParams, "CMAKE_OSX_DEPLOYMENT_TARGET", Configuration.iOSMinVer); - ConfigureCmakeSwitch(cmakeParams, "CMAKE_XCODE_ATTRIBUTE_IPHONEOS_DEPLOYMENT_TARGET", Configuration.iOSMinVer); + ConfigureCmakeSwitch(cmakeParams, "CMAKE_OSX_DEPLOYMENT_TARGET", iOSConfiguration.MinVer); + ConfigureCmakeSwitch(cmakeParams, "CMAKE_XCODE_ATTRIBUTE_IPHONEOS_DEPLOYMENT_TARGET", iOSConfiguration.MinVer); break; case TargetPlatform.Web: - ConfigureCmakeSwitch(cmakeParams, "PHYSX_CXX_FLAGS", Configuration.WebThreads ? "\"-pthread\"" : ""); + ConfigureCmakeSwitch(cmakeParams, "PHYSX_CXX_FLAGS", WebConfiguration.Threads ? "\"-pthread\"" : ""); break; } @@ -181,12 +187,12 @@ namespace Flax.Deps.Dependencies default: throw new InvalidArchitectureException(architecture); } binariesPrefix = "lib"; - envVars.Add("MACOSX_DEPLOYMENT_TARGET", Configuration.MacOSXMinVer); + envVars.Add("MACOSX_DEPLOYMENT_TARGET", MacConfiguration.MacOSXMinVer); break; case TargetPlatform.iOS: binariesSubDir = "ios.arm_64"; binariesPrefix = "lib"; - envVars.Add("IPHONEOS_DEPLOYMENT_TARGET", Configuration.iOSMinVer); + envVars.Add("IPHONEOS_DEPLOYMENT_TARGET", iOSConfiguration.MinVer); break; case TargetPlatform.Web: binariesSubDir = "web32"; @@ -211,9 +217,9 @@ namespace Flax.Deps.Dependencies break; } case TargetPlatform.Linux: - envVars.Add("CC", "clang-" + Configuration.LinuxClangMinVer); - envVars.Add("CC_FOR_BUILD", "clang-" + Configuration.LinuxClangMinVer); - envVars.Add("CXX", "clang++-" + Configuration.LinuxClangMinVer); + envVars.Add("CC", "clang-" + LinuxConfiguration.ClangMinVer); + envVars.Add("CC_FOR_BUILD", "clang-" + LinuxConfiguration.ClangMinVer); + envVars.Add("CXX", "clang++-" + LinuxConfiguration.ClangMinVer); break; case TargetPlatform.Mac: break; default: throw new InvalidPlatformException(BuildPlatform); @@ -298,14 +304,20 @@ namespace Flax.Deps.Dependencies Log.Verbose("Copy PhysX binaries from " + srcBinaries); foreach (var physXLib in defaultPhysXLibs) { + var remove = !usePVD && physXLib.Contains("Pvd"); var filename = suppressBitsPostfix ? string.Format("{0}{1}_static", binariesPrefix, physXLib) : string.Format("{0}{1}_static_{2}", binariesPrefix, physXLib, bits); if (targetPlatform == TargetPlatform.Web) filename = binariesPrefix + physXLib; filename += binariesExtension; - Utilities.FileCopy(Path.Combine(srcBinaries, filename), Path.Combine(dstBinaries, filename)); + if (remove) + Utilities.FileDelete(Path.Combine(dstBinaries, filename)); + else + Utilities.FileCopy(Path.Combine(srcBinaries, filename), Path.Combine(dstBinaries, filename)); var filenamePdb = Path.ChangeExtension(filename, "pdb"); - if (File.Exists(Path.Combine(srcBinaries, filenamePdb))) + if (remove) + Utilities.FileDelete(Path.Combine(dstBinaries, filenamePdb)); + else if (File.Exists(Path.Combine(srcBinaries, filenamePdb))) Utilities.FileCopy(Path.Combine(srcBinaries, filenamePdb), Path.Combine(dstBinaries, filenamePdb)); // Strip debug symbols to reduce binaries size diff --git a/Source/Tools/Flax.Build/Deps/Dependencies/basis_universal.cs b/Source/Tools/Flax.Build/Deps/Dependencies/basis_universal.cs index b56cbb83d..4bb7635fe 100644 --- a/Source/Tools/Flax.Build/Deps/Dependencies/basis_universal.cs +++ b/Source/Tools/Flax.Build/Deps/Dependencies/basis_universal.cs @@ -123,7 +123,7 @@ namespace Flax.Deps.Dependencies foreach (var define in defines) cmakeArgs += $"-D{define.Key}={define.Value} "; } - if (platform == TargetPlatform.Web && Configuration.WebThreads) + if (platform == TargetPlatform.Web && WebConfiguration.Threads) cmakeArgs += "-pthread "; cmakeArgs += "\""; diff --git a/Source/Tools/Flax.Build/Deps/Dependencies/curl.cs b/Source/Tools/Flax.Build/Deps/Dependencies/curl.cs index 2d25fed3d..0fa9ff839 100644 --- a/Source/Tools/Flax.Build/Deps/Dependencies/curl.cs +++ b/Source/Tools/Flax.Build/Deps/Dependencies/curl.cs @@ -134,8 +134,8 @@ namespace Flax.Deps.Dependencies }; var envVars = new Dictionary { - { "CC", "clang-" + Configuration.LinuxClangMinVer }, - { "CC_FOR_BUILD", "clang-" + Configuration.LinuxClangMinVer }, + { "CC", "clang-" + LinuxConfiguration.ClangMinVer }, + { "CC_FOR_BUILD", "clang-" + LinuxConfiguration.ClangMinVer }, { "CMAKE_BUILD_PARALLEL_LEVEL", CmakeBuildParallel }, }; var buildDir = Path.Combine(root, "build"); @@ -168,7 +168,7 @@ namespace Flax.Deps.Dependencies var archName = arch + "-apple-darwin19"; if (architecture == TargetArchitecture.ARM64) archName = "arm-apple-darwin19"; // for configure - var compilerFlags = string.Format("-mmacosx-version-min={0} -arch {1}", Configuration.MacOSXMinVer, arch); + var compilerFlags = string.Format("-mmacosx-version-min={0} -arch {1}", MacConfiguration.MacOSXMinVer, arch); var envVars = new Dictionary { { "CC", "clang" }, @@ -178,7 +178,7 @@ namespace Flax.Deps.Dependencies { "CPPFLAGS", compilerFlags }, { "ARCH", arch }, { "SDK", "macosx" }, - { "DEPLOYMENT_TARGET", Configuration.MacOSXMinVer }, + { "DEPLOYMENT_TARGET", MacConfiguration.MacOSXMinVer }, }; var buildDir = Path.Combine(root, "build"); SetupDirectory(buildDir, true); diff --git a/Source/Tools/Flax.Build/Deps/Dependencies/freetype.cs b/Source/Tools/Flax.Build/Deps/Dependencies/freetype.cs index d43c73770..c59006673 100644 --- a/Source/Tools/Flax.Build/Deps/Dependencies/freetype.cs +++ b/Source/Tools/Flax.Build/Deps/Dependencies/freetype.cs @@ -81,8 +81,8 @@ namespace Flax.Deps.Dependencies { var envVars = new Dictionary { - { "CC", "clang-" + Configuration.LinuxClangMinVer }, - { "CC_FOR_BUILD", "clang-" + Configuration.LinuxClangMinVer }, + { "CC", "clang-" + LinuxConfiguration.ClangMinVer }, + { "CC_FOR_BUILD", "clang-" + LinuxConfiguration.ClangMinVer }, { "CMAKE_BUILD_PARALLEL_LEVEL", CmakeBuildParallel }, }; diff --git a/Source/Tools/Flax.Build/Deps/Dependencies/mono.cs b/Source/Tools/Flax.Build/Deps/Dependencies/mono.cs index a90d1c2a0..edcc39671 100644 --- a/Source/Tools/Flax.Build/Deps/Dependencies/mono.cs +++ b/Source/Tools/Flax.Build/Deps/Dependencies/mono.cs @@ -591,8 +591,8 @@ namespace Flax.Deps.Dependencies { var envVars = new Dictionary { - { "CC", "clang-" + Configuration.LinuxClangMinVer }, - { "CXX", "clang++-" + Configuration.LinuxClangMinVer } + { "CC", "clang-" + LinuxConfiguration.ClangMinVer }, + { "CXX", "clang++-" + LinuxConfiguration.ClangMinVer } }; var monoOptions = new[] { @@ -662,7 +662,7 @@ namespace Flax.Deps.Dependencies { var sdk = AndroidSdk.Instance.RootPath; var ndk = AndroidNdk.Instance.RootPath; - var apiLevel = Configuration.AndroidPlatformApi.ToString(); + var apiLevel = AndroidConfiguration.PlatformApi.ToString(); var archName = UnixToolchain.GetToolchainName(platform, TargetArchitecture.ARM64); var toolchainRoot = Path.Combine(ndk, "toolchains", "llvm", "prebuilt", AndroidSdk.GetHostName()); var ndkBin = Path.Combine(toolchainRoot, "bin"); @@ -779,7 +779,7 @@ namespace Flax.Deps.Dependencies } case TargetPlatform.Mac: { - var compilerFlags = string.Format("-mmacosx-version-min={0}", Configuration.MacOSXMinVer); + var compilerFlags = string.Format("-mmacosx-version-min={0}", MacConfiguration.MacOSXMinVer); var envVars = new Dictionary { { "CFLAGS", compilerFlags }, diff --git a/Source/Tools/Flax.Build/Deps/Dependencies/msdfgen.cs b/Source/Tools/Flax.Build/Deps/Dependencies/msdfgen.cs index 89ef9eb4b..670554004 100644 --- a/Source/Tools/Flax.Build/Deps/Dependencies/msdfgen.cs +++ b/Source/Tools/Flax.Build/Deps/Dependencies/msdfgen.cs @@ -136,9 +136,9 @@ namespace Flax.Deps.Dependencies libName = "msdfgen-core.lib"; break; case TargetPlatform.Linux: - envVars["CC"] = "clang-" + Configuration.LinuxClangMinVer; - envVars["CC_FOR_BUILD"] = "clang-" + Configuration.LinuxClangMinVer; - envVars["CXX"] = "clang++-" + Configuration.LinuxClangMinVer; + envVars["CC"] = "clang-" + LinuxConfiguration.ClangMinVer; + envVars["CC_FOR_BUILD"] = "clang-" + LinuxConfiguration.ClangMinVer; + envVars["CXX"] = "clang++-" + LinuxConfiguration.ClangMinVer; cmakeArgs += " -DCMAKE_POSITION_INDEPENDENT_CODE=ON"; break; } diff --git a/Source/Tools/Flax.Build/Deps/Dependencies/tint.cs b/Source/Tools/Flax.Build/Deps/Dependencies/tint.cs new file mode 100644 index 000000000..a0960da55 --- /dev/null +++ b/Source/Tools/Flax.Build/Deps/Dependencies/tint.cs @@ -0,0 +1,135 @@ +// Copyright (c) Wojciech Figat. All rights reserved. + +using Flax.Build; +using System.IO; + +namespace Flax.Deps.Dependencies +{ + /// + /// Tint is a compiler for the WebGPU Shader Language (WGSL) that can be used in standalone to convert shaders from and to WGSL. + /// https://github.com/google/dawn (mirrors https://dawn.googlesource.com/dawn) + /// + /// + class tint : Dependency + { + /// + public override TargetPlatform[] Platforms + { + get + { + switch (BuildPlatform) + { + case TargetPlatform.Windows: + return new[] + { + TargetPlatform.Windows, + }; + case TargetPlatform.Mac: + return new[] + { + TargetPlatform.Mac, + }; + default: return new TargetPlatform[0]; + } + } + } + + /// + public override TargetArchitecture[] Architectures + { + get + { + switch (BuildPlatform) + { + case TargetPlatform.Windows: + return new[] + { + TargetArchitecture.x64, + }; + case TargetPlatform.Mac: + return new[] + { + TargetArchitecture.ARM64, + }; + default: return new TargetArchitecture[0]; + } + } + } + + /// + public override void Build(BuildOptions options) + { + var root = options.IntermediateFolder; + var config = "Release"; + var buildOptions = new[] + { + "DAWN_BUILD_SAMPLES=OFF", + "DAWN_BUILD_TESTS=OFF", + "DAWN_BUILD_PROTOBUF=OFF", + "DAWN_ENABLE_D3D11=OFF", + "DAWN_ENABLE_D3D12=OFF", + "DAWN_ENABLE_METAL=OFF", + "DAWN_ENABLE_NULL=OFF", + "DAWN_ENABLE_DESKTOP_GL=OFF", + "DAWN_ENABLE_OPENGLES=OFF", + "DAWN_ENABLE_VULKAN=OFF", + "DAWN_ENABLE_SPIRV_VALIDATION=OFF", + "DAWN_FORCE_SYSTEM_COMPONENT_LOAD=OFF", + "TINT_BUILD_CMD_TOOLS=ON", + "TINT_BUILD_SPV_READER=ON", + "TINT_BUILD_WGSL_READER=OFF", + "TINT_BUILD_GLSL_WRITER=OFF", + "TINT_BUILD_GLSL_VALIDATOR=OFF", + "TINT_BUILD_HLSL_WRITER=OFF", + "TINT_BUILD_SPV_WRITER=OFF", + "TINT_BUILD_WGSL_WRITER=ON", + "TINT_BUILD_NULL_WRITER=OFF", + "TINT_BUILD_IR_BINARY=OFF", + "TINT_BUILD_TESTS=OFF", + }; + + // Get the source + var firstTime = !GitRepositoryExists(root); + var commit = "536c572abab2602db0b652eda401ebd01e046c11"; // v20260219.200501 + CloneGitRepo(root, "https://github.com/google/dawn.git", commit, null, true); + if (firstTime) + { + // This repo is bloated with submodules and sometimes git fails to properly checkout them all + for (int i = 0; i < 3; i++) + Utilities.Run("git", "submodule update --recursive", null, root, Utilities.RunOptions.ConsoleLogOutput); + } + + // Build tint (get rid of unused stuff) + foreach (var platform in options.Platforms) + { + foreach (var architecture in options.Architectures) + { + BuildStarted(platform, architecture); + + // Build + var buildDir = Path.Combine(root, "build"); + SetupDirectory(buildDir, true); + var cmakeArgs = $"-DCMAKE_BUILD_TYPE={config}"; + foreach (var option in buildOptions) + cmakeArgs += $" -D{option}"; + RunCmake(buildDir, platform, architecture, ".. " + cmakeArgs); + BuildCmake(buildDir, config, options: Utilities.RunOptions.ConsoleLogOutput); + + // Deploy executable + var depsFolder = GetThirdPartyFolder(options, platform, architecture); + switch (platform) + { + case TargetPlatform.Windows: + Utilities.FileCopy(Path.Combine(buildDir, config, "tint.exe"), Path.Combine(depsFolder, "tint.exe")); + break; + default: + Utilities.FileCopy(Path.Combine(buildDir, config, "tint"), Path.Combine(depsFolder, "tint")); + if (BuildPlatform != TargetPlatform.Windows) + Utilities.Run("chmod", "+x tint", null, depsFolder, Utilities.RunOptions.ConsoleLogOutput); + break; + } + } + } + } + } +} diff --git a/Source/Tools/Flax.Build/Deps/Dependencies/vorbis.cs b/Source/Tools/Flax.Build/Deps/Dependencies/vorbis.cs index 15ca415da..33ef9b159 100644 --- a/Source/Tools/Flax.Build/Deps/Dependencies/vorbis.cs +++ b/Source/Tools/Flax.Build/Deps/Dependencies/vorbis.cs @@ -250,9 +250,9 @@ namespace Flax.Deps.Dependencies vorbisConfig += " -DCMAKE_POSITION_INDEPENDENT_CODE=ON"; envVars = new Dictionary { - { "CC", "clang-" + Configuration.LinuxClangMinVer }, - { "CC_FOR_BUILD", "clang-" + Configuration.LinuxClangMinVer }, - { "CXX", "clang++-" + Configuration.LinuxClangMinVer }, + { "CC", "clang-" + LinuxConfiguration.ClangMinVer }, + { "CC_FOR_BUILD", "clang-" + LinuxConfiguration.ClangMinVer }, + { "CXX", "clang++-" + LinuxConfiguration.ClangMinVer }, { "CMAKE_BUILD_PARALLEL_LEVEL", CmakeBuildParallel }, }; ext = ".a"; diff --git a/Source/Tools/Flax.Build/Deps/Dependency.cs b/Source/Tools/Flax.Build/Deps/Dependency.cs index de78ff7ce..6362b939b 100644 --- a/Source/Tools/Flax.Build/Deps/Dependency.cs +++ b/Source/Tools/Flax.Build/Deps/Dependency.cs @@ -547,19 +547,19 @@ namespace Flax.Deps var ndk = AndroidNdk.Instance.RootPath; var abi = AndroidToolchain.GetAbiName(architecture); var hostName = AndroidSdk.GetHostName(); - cmdLine = string.Format("-DCMAKE_TOOLCHAIN_FILE=\"{0}/build/cmake/android.toolchain.cmake\" -DANDROID_NDK=\"{0}\" -DANDROID_STL=c++_shared -DANDROID_ABI={1} -DANDROID_PLATFORM=android-{2} -G \"MinGW Makefiles\" -DCMAKE_MAKE_PROGRAM=\"{0}/prebuilt/{3}/bin/make.exe\"", ndk, abi, Configuration.AndroidPlatformApi, hostName); + cmdLine = string.Format("-DCMAKE_TOOLCHAIN_FILE=\"{0}/build/cmake/android.toolchain.cmake\" -DANDROID_NDK=\"{0}\" -DANDROID_STL=c++_shared -DANDROID_ABI={1} -DANDROID_PLATFORM=android-{2} -G \"MinGW Makefiles\" -DCMAKE_MAKE_PROGRAM=\"{0}/prebuilt/{3}/bin/make.exe\"", ndk, abi, AndroidConfiguration.PlatformApi, hostName); break; } case TargetPlatform.Mac: { var arch = GetAppleArchName(architecture); - cmdLine = string.Format("CMakeLists.txt -DCMAKE_OSX_DEPLOYMENT_TARGET=\"{0}\" -DCMAKE_OSX_ARCHITECTURES={1}", Configuration.MacOSXMinVer, arch); + cmdLine = string.Format("CMakeLists.txt -DCMAKE_OSX_DEPLOYMENT_TARGET=\"{0}\" -DCMAKE_OSX_ARCHITECTURES={1}", MacConfiguration.MacOSXMinVer, arch); break; } case TargetPlatform.iOS: { var arch = GetAppleArchName(architecture); - cmdLine = string.Format("CMakeLists.txt -DCMAKE_SYSTEM_NAME=iOS -DCMAKE_OSX_DEPLOYMENT_TARGET=\"{0}\" -DCMAKE_OSX_ARCHITECTURES={1}", Configuration.iOSMinVer, arch); + cmdLine = string.Format("CMakeLists.txt -DCMAKE_SYSTEM_NAME=iOS -DCMAKE_OSX_DEPLOYMENT_TARGET=\"{0}\" -DCMAKE_OSX_ARCHITECTURES={1}", iOSConfiguration.MinVer, arch); break; } case TargetPlatform.Web: diff --git a/Source/Tools/Flax.Build/Platforms/Android/AndroidToolchain.cs b/Source/Tools/Flax.Build/Platforms/Android/AndroidToolchain.cs index d88cdbd51..ab332fdb1 100644 --- a/Source/Tools/Flax.Build/Platforms/Android/AndroidToolchain.cs +++ b/Source/Tools/Flax.Build/Platforms/Android/AndroidToolchain.cs @@ -8,13 +8,13 @@ using Flax.Build.NativeCpp; namespace Flax.Build { - partial class Configuration + partial class AndroidConfiguration { /// /// Specifies the Android API level to use (eg. 24). /// - [CommandLine("androidPlatformApi", "", "Specifies the Android API level to use (eg. 24).")] - public static int AndroidPlatformApi = 24; + [CommandLine("platformApi", "", "Specifies the Android API level to use (eg. 24).")] + public static int PlatformApi = 24; } } @@ -66,7 +66,7 @@ namespace Flax.Build.Platforms base.SetupEnvironment(options); options.CompileEnv.PreprocessorDefinitions.Add("PLATFORM_ANDROID"); - options.CompileEnv.PreprocessorDefinitions.Add(string.Format("__ANDROID_API__={0}", Configuration.AndroidPlatformApi)); + options.CompileEnv.PreprocessorDefinitions.Add(string.Format("__ANDROID_API__={0}", AndroidConfiguration.PlatformApi)); options.LinkEnv.InputLibraries.Add("c"); options.LinkEnv.InputLibraries.Add("z"); @@ -108,7 +108,7 @@ namespace Flax.Build.Platforms { case TargetArchitecture.x86: args.Add("-march=atom"); - if (Configuration.AndroidPlatformApi < 24) + if (AndroidConfiguration.PlatformApi < 24) args.Add("-mstackrealign"); break; case TargetArchitecture.x64: @@ -167,7 +167,7 @@ namespace Flax.Build.Platforms var toolchain = ToolsetRoot.Replace('\\', '/'); args.Add(string.Format("--sysroot=\"{0}/sysroot\"", toolchain)); args.Add(string.Format("--gcc-toolchain=\"{0}\"", toolchain)); - args.Add("--target=" + target + Configuration.AndroidPlatformApi); + args.Add("--target=" + target + AndroidConfiguration.PlatformApi); } /// diff --git a/Source/Tools/Flax.Build/Platforms/Linux/LinuxToolchain.cs b/Source/Tools/Flax.Build/Platforms/Linux/LinuxToolchain.cs index 65283e894..a53cfcfc7 100644 --- a/Source/Tools/Flax.Build/Platforms/Linux/LinuxToolchain.cs +++ b/Source/Tools/Flax.Build/Platforms/Linux/LinuxToolchain.cs @@ -8,13 +8,13 @@ using System.IO; namespace Flax.Build { - partial class Configuration + partial class LinuxConfiguration { /// /// Specifies the minimum Clang compiler version to use on Linux (eg. 10). /// - [CommandLine("linuxClangMinVer", "", "Specifies the minimum Clang compiler version to use on Linux (eg. 10).")] - public static string LinuxClangMinVer = "14"; + [CommandLine("clangMinVer", "", "Specifies the minimum Clang compiler version to use on Linux (eg. 10).")] + public static string ClangMinVer = "14"; } } @@ -36,7 +36,7 @@ namespace Flax.Build.Platforms : base(platform, architecture, platform.ToolchainRoot, platform.Compiler) { // Check version - if (Utilities.ParseVersion(Configuration.LinuxClangMinVer, out var minClangVer) && ClangVersion < minClangVer) + if (Utilities.ParseVersion(LinuxConfiguration.ClangMinVer, out var minClangVer) && ClangVersion < minClangVer) Log.Error($"Old Clang version {ClangVersion}. Minimum supported is {minClangVer}."); // Setup system paths diff --git a/Source/Tools/Flax.Build/Platforms/Mac/MacToolchain.cs b/Source/Tools/Flax.Build/Platforms/Mac/MacToolchain.cs index 25c540d2d..891c9e38e 100644 --- a/Source/Tools/Flax.Build/Platforms/Mac/MacToolchain.cs +++ b/Source/Tools/Flax.Build/Platforms/Mac/MacToolchain.cs @@ -5,7 +5,7 @@ using Flax.Build.NativeCpp; namespace Flax.Build { - partial class Configuration + partial class MacConfiguration { /// /// Specifies the minimum Mac OSX version to use (eg. 10.14). @@ -69,7 +69,7 @@ namespace Flax.Build.Platforms { base.AddArgsCommon(options, args); - args.Add("-mmacosx-version-min=" + Configuration.MacOSXMinVer); + args.Add("-mmacosx-version-min=" + MacConfiguration.MacOSXMinVer); } } } diff --git a/Source/Tools/Flax.Build/Platforms/Unix/UnixToolchain.cs b/Source/Tools/Flax.Build/Platforms/Unix/UnixToolchain.cs index 2c0929c72..0008988cc 100644 --- a/Source/Tools/Flax.Build/Platforms/Unix/UnixToolchain.cs +++ b/Source/Tools/Flax.Build/Platforms/Unix/UnixToolchain.cs @@ -244,14 +244,14 @@ namespace Flax.Build.Platforms case TargetPlatform.Mac: switch (architecture) { - case TargetArchitecture.x64: return "x86_64-apple-macos" + Configuration.MacOSXMinVer; - case TargetArchitecture.ARM64: return "aarch64-apple-macos" + Configuration.MacOSXMinVer; + case TargetArchitecture.x64: return "x86_64-apple-macos" + MacConfiguration.MacOSXMinVer; + case TargetArchitecture.ARM64: return "aarch64-apple-macos" + MacConfiguration.MacOSXMinVer; default: throw new InvalidArchitectureException(architecture); } case TargetPlatform.iOS: switch (architecture) { - case TargetArchitecture.ARM64: return "aarch64-apple-ios" + Configuration.iOSMinVer; + case TargetArchitecture.ARM64: return "aarch64-apple-ios" + iOSConfiguration.MinVer; default: throw new InvalidArchitectureException(architecture); } default: throw new InvalidPlatformException(platform); diff --git a/Source/Tools/Flax.Build/Platforms/Web/WebToolchain.cs b/Source/Tools/Flax.Build/Platforms/Web/WebToolchain.cs index ee086c2b9..a465ccac0 100644 --- a/Source/Tools/Flax.Build/Platforms/Web/WebToolchain.cs +++ b/Source/Tools/Flax.Build/Platforms/Web/WebToolchain.cs @@ -9,19 +9,19 @@ using Flax.Build.NativeCpp; namespace Flax.Build { - partial class Configuration + partial class WebConfiguration { /// /// Specifies the initial memory size (in MB) to use by Web app. /// - [CommandLine("webInitialMemory", "", "Specifies the initial memory size (in MB) to use by Web app.")] - public static int WebInitialMemory = 32; + [CommandLine("initialMemory", "", "Specifies the initial memory size (in MB) to use by Web app.")] + public static int InitialMemory = 32; /// /// Enables pthreads support for multithreading using SharedArrayBuffer in browsers. Changing it requires rebuilding deps for Web. /// - [CommandLine("webThreads", "0/1", "Enables pthreads support for multithreading using SharedArrayBuffer in browsers. Changing it requires rebuilding deps for Web.")] - public static bool WebThreads = false; + [CommandLine("threads", "0/1", "Enables pthreads support for multithreading using SharedArrayBuffer in browsers. Changing it requires rebuilding deps for Web.")] + public static bool Threads = false; } } @@ -105,7 +105,7 @@ namespace Flax.Build.Platforms options.CompileEnv.PreprocessorDefinitions.Add("PLATFORM_WEB"); options.CompileEnv.PreprocessorDefinitions.Add("PLATFORM_UNIX"); options.CompileEnv.PreprocessorDefinitions.Add("__EMSCRIPTEN__"); - if (Configuration.WebThreads) + if (WebConfiguration.Threads) options.CompileEnv.PreprocessorDefinitions.Add("__EMSCRIPTEN_PTHREADS__"); options.CompileEnv.EnableExceptions = WithExceptions(options); options.CompileEnv.CpuArchitecture = CpuArchitecture.SSE4_2; @@ -183,7 +183,7 @@ namespace Flax.Build.Platforms if (sanitizers == Sanitizer.None && options.Configuration != TargetConfiguration.Release) args.Add("-fsanitize=null -fsanitize-minimal-runtime"); // Minimal Runtime - if (Configuration.WebThreads) + if (WebConfiguration.Threads) args.Add("-pthread"); } @@ -308,7 +308,7 @@ namespace Flax.Build.Platforms args.Add("-sERROR_ON_UNDEFINED_SYMBOLS=0"); // Setup memory - var initialMemory = Configuration.WebInitialMemory; + var initialMemory = WebConfiguration.InitialMemory; if (options.CompileEnv.Sanitizers.HasFlag(Sanitizer.Address)) initialMemory = Math.Max(initialMemory, 64); // Address Sanitizer needs more memory args.Add($"-sINITIAL_MEMORY={initialMemory}MB"); diff --git a/Source/Tools/Flax.Build/Platforms/Windows/WindowsToolchain.cs b/Source/Tools/Flax.Build/Platforms/Windows/WindowsToolchain.cs index 0c2d44c5d..da6c868f3 100644 --- a/Source/Tools/Flax.Build/Platforms/Windows/WindowsToolchain.cs +++ b/Source/Tools/Flax.Build/Platforms/Windows/WindowsToolchain.cs @@ -8,19 +8,19 @@ using Flax.Build.NativeCpp; namespace Flax.Build { - partial class Configuration + partial class WindowsConfiguration { /// /// Specifies the minimum Windows version to use (eg. 10). /// - [CommandLine("winMinVer", "", "Specifies the minimum Windows version to use (eg. 10).")] - public static string WindowsMinVer = "10"; + [CommandLine("minVer", "", "Specifies the minimum Windows version to use (eg. 10).")] + public static string MinVer = "10"; /// /// Specifies the minimum CPU architecture type to support (on x86/x64). /// - [CommandLine("winCpuArch", "", "Specifies the minimum CPU architecture type to support (on x86/x64).")] - public static CpuArchitecture WindowsCpuArch = CpuArchitecture.SSE4_2; // 99.78% support on PC according to Steam Hardware & Software Survey: September 2025 (https://store.steampowered.com/hwsurvey/) + [CommandLine("cpuArch", "", "Specifies the minimum CPU architecture type to support (on x86/x64).")] + public static CpuArchitecture CpuArch = CpuArchitecture.SSE4_2; // 99.78% support on PC according to Steam Hardware & Software Survey: September 2025 (https://store.steampowered.com/hwsurvey/) } } @@ -44,7 +44,7 @@ namespace Flax.Build.Platforms : base(platform, architecture, WindowsPlatformToolset.Latest, WindowsPlatformSDK.Latest) { // Select minimum Windows version - if (!Utilities.ParseVersion(Configuration.WindowsMinVer, out _minVersion)) + if (!Utilities.ParseVersion(WindowsConfiguration.MinVer, out _minVersion)) _minVersion = new Version(7, 0); } @@ -76,7 +76,7 @@ namespace Flax.Build.Platforms options.LinkEnv.InputLibraries.Add("oleaut32.lib"); options.LinkEnv.InputLibraries.Add("delayimp.lib"); - options.CompileEnv.CpuArchitecture = Configuration.WindowsCpuArch; + options.CompileEnv.CpuArchitecture = WindowsConfiguration.CpuArch; if (options.Architecture == TargetArchitecture.x64) { diff --git a/Source/Tools/Flax.Build/Platforms/iOS/iOSToolchain.cs b/Source/Tools/Flax.Build/Platforms/iOS/iOSToolchain.cs index 44647a607..a54035a69 100644 --- a/Source/Tools/Flax.Build/Platforms/iOS/iOSToolchain.cs +++ b/Source/Tools/Flax.Build/Platforms/iOS/iOSToolchain.cs @@ -6,13 +6,13 @@ using Flax.Build.NativeCpp; namespace Flax.Build { - partial class Configuration + partial class iOSConfiguration { /// /// Specifies the minimum iOS version to use (eg. 14). /// - [CommandLine("iOSMinVer", "", "Specifies the minimum iOS version to use (eg. 14).")] - public static string iOSMinVer = "15"; + [CommandLine("MinVer", "", "Specifies the minimum iOS version to use (eg. 14).")] + public static string MinVer = "15"; } } @@ -60,7 +60,7 @@ namespace Flax.Build.Platforms { base.AddArgsCommon(options, args); - args.Add("-miphoneos-version-min=" + Configuration.iOSMinVer); + args.Add("-miphoneos-version-min=" + iOSConfiguration.MinVer); } public override bool CompileCSharp(ref CSharpOptions options) diff --git a/Source/Tools/Flax.Build/Program.cs b/Source/Tools/Flax.Build/Program.cs index 711ac5ccb..5387a3b38 100644 --- a/Source/Tools/Flax.Build/Program.cs +++ b/Source/Tools/Flax.Build/Program.cs @@ -85,6 +85,11 @@ namespace Flax.Build { CommandLine.Configure(typeof(EngineConfiguration), engineProject.Configuration); CommandLine.Configure(typeof(Configuration), engineProject.Configuration); + CommandLine.ConfigureChild(typeof(WindowsConfiguration), engineProject.Configuration, "Windows"); + CommandLine.ConfigureChild(typeof(LinuxConfiguration), engineProject.Configuration, "Linux"); + CommandLine.ConfigureChild(typeof(MacConfiguration), engineProject.Configuration, "Mac"); + CommandLine.ConfigureChild(typeof(iOSConfiguration), engineProject.Configuration, "iOS"); + CommandLine.ConfigureChild(typeof(WebConfiguration), engineProject.Configuration, "Web"); } CommandLine.Configure(typeof(EngineConfiguration)); } diff --git a/Source/Tools/Flax.Build/ProjectInfo.cs b/Source/Tools/Flax.Build/ProjectInfo.cs index 00bdbf938..37f01531a 100644 --- a/Source/Tools/Flax.Build/ProjectInfo.cs +++ b/Source/Tools/Flax.Build/ProjectInfo.cs @@ -113,6 +113,55 @@ namespace Flax.Build value = "true"; else if (reader.TokenType == JsonTokenType.False) value = "false"; + else if (reader.TokenType == JsonTokenType.StartObject) + { + value = "{"; + int depth = 1; + while (depth > 0 && reader.Read()) + { + switch (reader.TokenType) + { + case JsonTokenType.StartObject: + depth++; + value += "{"; + break; + case JsonTokenType.EndObject: + if (value.Last() == ',') + value = value.Substring(0, value.Length - 1); + value += "}"; + if (depth != 1) + value += ","; + depth--; + break; + case JsonTokenType.StartArray: + value += "["; + break; + case JsonTokenType.EndArray: + value += "],"; + break; + case JsonTokenType.PropertyName: + value += $"\"{reader.GetString()}\":"; + break; + case JsonTokenType.String: + value += $"\"{reader.GetString()}\","; + break; + case JsonTokenType.Number: + value += $"{reader.GetString()},"; + break; + case JsonTokenType.True: + value += "true,"; + break; + case JsonTokenType.False: + value += "false,"; + break; + case JsonTokenType.Null: + value += "null,"; + break; + default: + throw new ArgumentOutOfRangeException(); + } + } + } else value = reader.GetString(); dictionary.Add(key, value); @@ -147,6 +196,19 @@ namespace Flax.Build private static List _projectsCache; private string _versionControlCommit, _versionControlBranch; + internal static JsonSerializerOptions JsonOptions = new JsonSerializerOptions + { + Converters = + { + new FlaxVersionConverter(), + new ConfigurationDictionaryConverter(), + }, + IncludeFields = true, + AllowTrailingCommas = true, + ReadCommentHandling = JsonCommentHandling.Skip, + TypeInfoResolver = ProjectInfoSourceGenerationContext.Default, + }; + /// /// The project reference. /// @@ -230,7 +292,6 @@ namespace Flax.Build /// /// The custom build configuration entries loaded from project file. /// - [System.Text.Json.Serialization.JsonConverter(typeof(ConfigurationDictionaryConverter))] public Dictionary Configuration; /// @@ -264,7 +325,6 @@ namespace Flax.Build /// public string VersionControlInfo { - get { if (_versionControlCommit == null) @@ -356,7 +416,7 @@ namespace Flax.Build /// public void Save() { - var contents = JsonSerializer.Serialize(this, new JsonSerializerOptions() { Converters = { new FlaxVersionConverter() }, TypeInfoResolver = ProjectInfoSourceGenerationContext.Default }); + var contents = JsonSerializer.Serialize(this, JsonOptions); File.WriteAllText(ProjectPath, contents); } @@ -382,8 +442,7 @@ namespace Flax.Build // Load Log.Verbose("Loading project file from \"" + path + "\"..."); var contents = File.ReadAllText(path); - var project = JsonSerializer.Deserialize(contents.AsSpan(), - new JsonSerializerOptions() { Converters = { new FlaxVersionConverter() }, IncludeFields = true, TypeInfoResolver = ProjectInfoSourceGenerationContext.Default }); + var project = JsonSerializer.Deserialize(contents.AsSpan(), JsonOptions); project.ProjectPath = path; project.ProjectFolderPath = Path.GetDirectoryName(path); diff --git a/Source/Tools/Flax.Build/Projects/VisualStudio/VisualStudioProjectGenerator.cs b/Source/Tools/Flax.Build/Projects/VisualStudio/VisualStudioProjectGenerator.cs index be9f1235f..86ab6256f 100644 --- a/Source/Tools/Flax.Build/Projects/VisualStudio/VisualStudioProjectGenerator.cs +++ b/Source/Tools/Flax.Build/Projects/VisualStudio/VisualStudioProjectGenerator.cs @@ -49,7 +49,7 @@ namespace Flax.Build.Projects.VisualStudio vcProjectFileContent.AppendLine(string.Format(" {0}", ProjectGuid.ToString("B").ToUpperInvariant())); vcProjectFileContent.AppendLine(string.Format(" {0}", BaseName)); vcProjectFileContent.AppendLine(string.Format(" {0}", projectFileToolVersion)); - vcProjectFileContent.AppendLine(string.Format(" {0}", Configuration.AndroidPlatformApi)); + vcProjectFileContent.AppendLine(string.Format(" {0}", AndroidConfiguration.PlatformApi)); vcProjectFileContent.AppendLine(string.Format(" {0}", "arm64-v8a")); vcProjectFileContent.AppendLine(" Application"); vcProjectFileContent.AppendLine(" "); diff --git a/Source/Tools/Flax.Build/global.json b/Source/Tools/Flax.Build/global.json index 9498eeef6..51ccb9126 100644 --- a/Source/Tools/Flax.Build/global.json +++ b/Source/Tools/Flax.Build/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "8.0.0", + "version": "8.0.419", "rollForward": "latestMajor" } -} \ No newline at end of file +} diff --git a/global.json b/global.json index 7da276347..51ccb9126 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "8.0.100", + "version": "8.0.419", "rollForward": "latestMajor" } -} \ No newline at end of file +}