diff --git a/.github/workflows/build_linux.todo b/.github/workflows/build_linux.todo new file mode 100644 index 000000000..9c3b21624 --- /dev/null +++ b/.github/workflows/build_linux.todo @@ -0,0 +1,19 @@ +name: Build Linux +on: [push, pull_request] + +jobs: + + # Game + game-linux: + name: Game (Linux, Release x64) + runs-on: "ubuntu-20.04" + steps: + - name: Checkout repo + uses: actions/checkout@v2 + - name: Checkout LFS + run: | + git lfs version + git lfs pull + - name: Build + run: | + ./Development/Scripts/Linux/CallBuildTool.sh -build -log -arch=x64 -platform=Linux -configuration=Release -buildtargets=FlaxGame diff --git a/.github/workflows/build_windows.yml b/.github/workflows/build_windows.yml new file mode 100644 index 000000000..690d6747b --- /dev/null +++ b/.github/workflows/build_windows.yml @@ -0,0 +1,34 @@ +name: Build Windows +on: [push, pull_request] + +jobs: + + # Editor + editor-windows: + name: Editor (Windows, Development x64) + runs-on: "windows-latest" + steps: + - name: Checkout repo + uses: actions/checkout@v2 + - name: Checkout LFS + run: | + git lfs version + git lfs pull + - name: Build + run: | + .\Development\Scripts\Windows\CallBuildTool.bat -build -log -arch=x64 -platform=Windows -configuration=Development -buildtargets=FlaxEditor + + # Game + game-windows: + name: Game (Windows, Release x64) + runs-on: "windows-latest" + steps: + - name: Checkout repo + uses: actions/checkout@v2 + - name: Checkout LFS + run: | + git lfs version + git lfs pull + - name: Build + run: | + .\Development\Scripts\Windows\CallBuildTool.bat -build -log -arch=x64 -platform=Windows -configuration=Release -buildtargets=FlaxGame diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 000000000..236fa6d67 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,34 @@ +# How to contribute to the FlaxEngine + +For any questions, suggestions or help join our discord! + + + +Want to see whats planned for Flax? + +Go check out our [Trello](https://trello.com/b/NQjLXRCP/flax-roadmap). + +## **Found a bug?** + +* Avoid opening any new issues without having checked if your problem has already been reported. If there are no currently open issues that fit your problem's description, feel free to [add it](https://github.com/FlaxEngine/FlaxEngine/issues/new). + +* When writing an issue make sure to include a clear title and description as well as having filled out all the necessary information, depending on the severity of the issue also include the necessary log files and minidump. + +* Try to following the given template when writing a new issue if possible. + +## **Want to contribute?** + +* Fork the FlaxEngine, create a new branch and push your changes there. Then, create a pull request. + +* When creating a PR for fixing an issue/bug make sure to describe as to what led to the fix for better understanding, for small and obvious fixes this is not really needed. + However make sure to mention the relevant issue where it was first reported if possible. + +* For feature PR's the first thing you should evaluate is the value of your contribution, as in, what would it bring to this engine? Is it really required? + If its a small change you could preferably suggest it to us on our discord, else feel free to open up a PR for it. + +* Ensure when creating a PR that your contribution is well explained with a adequate description and title. + +* Generally, good code quality is expected, make sure your contribution works as intended and is appropriately commented where necessary. + + +Thank you for taking interest in contributing to Flax! diff --git a/Content/Editor/Primitives/Plane.flax b/Content/Editor/Primitives/Plane.flax index bf2052f02..1e5eaf76b 100644 --- a/Content/Editor/Primitives/Plane.flax +++ b/Content/Editor/Primitives/Plane.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8ab6d87b9f36f4ad231f38f415223130eea7b411be7ff8fd174b6ff7790258fb -size 2232 +oid sha256:5683a761f198d01d7189490d526b55e393eb01fffbd6761fb969d11067b3db73 +size 2277 diff --git a/Content/Shaders/BakeLightmap.flax b/Content/Shaders/BakeLightmap.flax index 6e813205f..fac992469 100644 --- a/Content/Shaders/BakeLightmap.flax +++ b/Content/Shaders/BakeLightmap.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:17295767d488dc2b64a1794ceda8b4d68f20dd2c5f1a8fdbe6f7940a070f9724 +oid sha256:cc4b141137661d995ff571191150c5997fa6f6576572b5c2281c395a12772d7c size 16095 diff --git a/Content/Shaders/BitonicSort.flax b/Content/Shaders/BitonicSort.flax index c8a9e901c..1d5b8a581 100644 --- a/Content/Shaders/BitonicSort.flax +++ b/Content/Shaders/BitonicSort.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c8371a1f4e0631e69f6dc6dbb8e11c8618351a7083293078270d60599f739e9f -size 6725 +oid sha256:f46a61cf8d5183230176e661a51208bfeece16cc7238655f406288ff448af64e +size 6721 diff --git a/Development/Scripts/Linux/CallBuildTool.sh b/Development/Scripts/Linux/CallBuildTool.sh old mode 100644 new mode 100755 index 3b4568918..1a8affe20 --- a/Development/Scripts/Linux/CallBuildTool.sh +++ b/Development/Scripts/Linux/CallBuildTool.sh @@ -3,6 +3,12 @@ set -e +testfilesize=$(wc -c < 'Source/Logo.png') +if [ $testfilesize -le 1000 ]; then + echo "CallBuildTool ERROR: Repository was not cloned using Git LFS" 1>&2 + exit 1 +fi + # Compile the build tool. xbuild /nologo /verbosity:quiet "Source/Tools/Flax.Build/Flax.Build.csproj" /property:Configuration=Release /property:Platform=AnyCPU /target:Build diff --git a/Development/Scripts/Windows/CallBuildTool.bat b/Development/Scripts/Windows/CallBuildTool.bat index 4d9124776..c9729cf55 100644 --- a/Development/Scripts/Windows/CallBuildTool.bat +++ b/Development/Scripts/Windows/CallBuildTool.bat @@ -4,6 +4,10 @@ rem Copyright (c) 2012-2020 Wojciech Figat. All rights reserved. if not exist "Development\Scripts\Windows\GetMSBuildPath.bat" goto Error_InvalidLocation +for %%I in (Source\Logo.png) do if %%~zI LSS 2000 ( + goto Error_MissingLFS +) + call "Development\Scripts\Windows\GetMSBuildPath.bat" if errorlevel 1 goto Error_NoVisualStudioEnvironment @@ -33,6 +37,9 @@ Binaries\Tools\Flax.Build.exe %* if errorlevel 1 goto Error_FlaxBuildFailed exit /B 0 +:Error_MissingLFS +echo CallBuildTool ERROR: Repository was not cloned using Git LFS +goto Exit :Error_InvalidLocation echo CallBuildTool ERROR: The script is in invalid directory. goto Exit diff --git a/Flax.flaxproj b/Flax.flaxproj index 26fb340ba..0c2683afb 100644 --- a/Flax.flaxproj +++ b/Flax.flaxproj @@ -3,7 +3,7 @@ "Version": { "Major": 1, "Minor": 0, - "Build": 6215 + "Build": 6216 }, "Company": "Flax", "Copyright": "Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.", diff --git a/GenerateProjectFiles.bat b/GenerateProjectFiles.bat index 5db8aaf55..aeeac1c67 100644 --- a/GenerateProjectFiles.bat +++ b/GenerateProjectFiles.bat @@ -16,6 +16,7 @@ exit /B 0 :BuildToolFailed echo Flax.Build tool failed. +pause goto Exit :Exit diff --git a/GenerateProjectFiles.sh b/GenerateProjectFiles.sh old mode 100644 new mode 100755 diff --git a/PackagePlatforms.sh b/PackagePlatforms.sh old mode 100644 new mode 100755 diff --git a/README.md b/README.md index 1ad9f7ce4..9c5b83aae 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,10 @@ This repository contains full source code of the Flax (excluding NDA-protected p Follow the instructions below to compile and run the engine from source. +## Flax plugin for Visual Studio + +Flax Visual Studio extension provides better programming workflow, C# scripts debugging functionality and allows to attach to running engine instance to debug C# source. This extension is available to download [here](https://marketplace.visualstudio.com/items?itemName=Flax.FlaxVS). + ## Windows * Install Visual Studio 2015 or newer @@ -34,7 +38,8 @@ Follow the instructions below to compile and run the engine from source. * Clone repo (with LFS) * Run **GenerateProjectFiles.bat** * Open `Flax.sln` and set solution configuration to **Editor.Development** and solution platform to **Win64** -* Compile Flax project (hit F7 key) +* Set Flax or FlaxEngine as startup project +* Compile Flax project (hit F7 or CTRL+Shift+B) * Run Flax (hit F5 key) ## Linux @@ -49,10 +54,6 @@ Follow the instructions below to compile and run the engine from source. * Open workspace with Visual Code * Build and run -# Flax plugin for Visual Studio - -Flax Visual Studio extension provides better programming workflow, C# scripts debugging functionality and allows to attach to running engine instance to debug C# source. This extension is available to download [here](https://marketplace.visualstudio.com/items?itemName=Flax.FlaxVS). - ## Workspace directory - **Binaries/** - executable files diff --git a/Source/Editor/Content/Import/ModelImportEntry.cs b/Source/Editor/Content/Import/ModelImportEntry.cs index e7cbe96ea..37525e6d3 100644 --- a/Source/Editor/Content/Import/ModelImportEntry.cs +++ b/Source/Editor/Content/Import/ModelImportEntry.cs @@ -262,9 +262,9 @@ namespace FlaxEditor.Content.Import public int BaseLOD { get; set; } = 0; /// - /// The amount of LODs to include in the model (all reaming ones starting from Base LOD will be generated). + /// The amount of LODs to include in the model (all remaining ones starting from Base LOD will be generated). /// - [EditorOrder(1120), DefaultValue(4), Limit(1, Model.MaxLODs), EditorDisplay("Level Of Detail", "LOD Count"), Tooltip("The amount of LODs to include in the model (all reaming ones starting from Base LOD will be generated).")] + [EditorOrder(1120), DefaultValue(4), Limit(1, Model.MaxLODs), EditorDisplay("Level Of Detail", "LOD Count"), Tooltip("The amount of LODs to include in the model (all remaining ones starting from Base LOD will be generated).")] public int LODCount { get; set; } = 4; /// diff --git a/Source/Editor/Content/Proxy/PrefabProxy.cs b/Source/Editor/Content/Proxy/PrefabProxy.cs index 39d77f25c..276bc0d30 100644 --- a/Source/Editor/Content/Proxy/PrefabProxy.cs +++ b/Source/Editor/Content/Proxy/PrefabProxy.cs @@ -162,8 +162,7 @@ namespace FlaxEditor.Content // Auto fit actor to camera float targetSize = 30.0f; - BoundingBox bounds; - Editor.GetActorEditorBox(_preview.Instance, out bounds); + Editor.GetActorEditorBox(_preview.Instance, out var bounds); float maxSize = Mathf.Max(0.001f, bounds.Size.MaxValue); _preview.Instance.Scale = new Vector3(targetSize / maxSize); _preview.Instance.Position = Vector3.Zero; @@ -175,6 +174,7 @@ namespace FlaxEditor.Content /// public override void OnThumbnailDrawEnd(ThumbnailRequest request, ContainerControl guiRoot) { + _preview.RemoveChildren(); _preview.Prefab = null; _preview.Parent = null; } diff --git a/Source/Editor/Content/Tree/ContentTreeNode.cs b/Source/Editor/Content/Tree/ContentTreeNode.cs index 7df407e08..4370bc8e7 100644 --- a/Source/Editor/Content/Tree/ContentTreeNode.cs +++ b/Source/Editor/Content/Tree/ContentTreeNode.cs @@ -295,7 +295,8 @@ namespace FlaxEditor.Content StartRenaming(); return true; case KeyboardKeys.Delete: - Editor.Instance.Windows.ContentWin.Delete(Folder); + if (Folder.Exists) + Editor.Instance.Windows.ContentWin.Delete(Folder); return true; } if (RootWindow.GetKey(KeyboardKeys.Control)) @@ -303,7 +304,8 @@ namespace FlaxEditor.Content switch (key) { case KeyboardKeys.D: - Editor.Instance.Windows.ContentWin.Duplicate(Folder); + if (Folder.Exists) + Editor.Instance.Windows.ContentWin.Duplicate(Folder); return true; } } diff --git a/Source/Editor/Cooker/Platform/Android/AndroidPlatformTools.cpp b/Source/Editor/Cooker/Platform/Android/AndroidPlatformTools.cpp index bce6aa860..b4365b4bd 100644 --- a/Source/Editor/Cooker/Platform/Android/AndroidPlatformTools.cpp +++ b/Source/Editor/Cooker/Platform/Android/AndroidPlatformTools.cpp @@ -141,7 +141,7 @@ bool AndroidPlatformTools::OnPostProcess(CookingData& data) const auto c = packageName[i]; if (c != '_' && c != '.' && !StringUtils::IsAlnum(c)) { - LOG(Error, "Android Package Name \'{0}\' contains invalid chaarcter. Only letters, numbers, dots and underscore characters are allowed.", packageName); + LOG(Error, "Android Package Name \'{0}\' contains invalid character. Only letters, numbers, dots and underscore characters are allowed.", packageName); return true; } } diff --git a/Source/Editor/Cooker/Platform/UWP/UWPPlatformTools.cpp b/Source/Editor/Cooker/Platform/UWP/UWPPlatformTools.cpp index f6c846fed..d9d2fe612 100644 --- a/Source/Editor/Cooker/Platform/UWP/UWPPlatformTools.cpp +++ b/Source/Editor/Cooker/Platform/UWP/UWPPlatformTools.cpp @@ -24,7 +24,7 @@ bool UWPPlatformTools::OnScriptsStepDone(CookingData& data) const String assembliesPath = data.OutputPath; if (FileSystem::CopyFile(assembliesPath / TEXT("Newtonsoft.Json.dll"), customBinPath)) { - data.Error(TEXT("Failed to copy deloy custom assembly.")); + data.Error(TEXT("Failed to copy deploy custom assembly.")); return true; } FileSystem::DeleteFile(assembliesPath / TEXT("Newtonsoft.Json.pdb")); diff --git a/Source/Editor/Cooker/PlatformTools.h b/Source/Editor/Cooker/PlatformTools.h index 28f6346ca..c98c008bc 100644 --- a/Source/Editor/Cooker/PlatformTools.h +++ b/Source/Editor/Cooker/PlatformTools.h @@ -48,7 +48,7 @@ public: /// /// Gets the value indicating whenever platform requires AOT. /// - /// True if platform uses AOT and needs C# assemblies to be be precompiled, otherwise false. + /// True if platform uses AOT and needs C# assemblies to be precompiled, otherwise false. virtual bool UseAOT() const { return false; diff --git a/Source/Editor/Cooker/Steps/ValidateStep.cpp b/Source/Editor/Cooker/Steps/ValidateStep.cpp index 219301544..fdebca0a7 100644 --- a/Source/Editor/Cooker/Steps/ValidateStep.cpp +++ b/Source/Editor/Cooker/Steps/ValidateStep.cpp @@ -60,7 +60,7 @@ bool ValidateStep::Perform(CookingData& data) AssetInfo info; if (!Content::GetAssetInfo(GameSettings::FirstScene, info)) { - data.Error(TEXT("Missing first scene.")); + data.Error(TEXT("Missing first scene. Set it in the game settings.")); return true; } } diff --git a/Source/Editor/CustomEditors/CustomEditorsUtil.cs b/Source/Editor/CustomEditors/CustomEditorsUtil.cs index 2f97da333..9b83dd820 100644 --- a/Source/Editor/CustomEditors/CustomEditorsUtil.cs +++ b/Source/Editor/CustomEditors/CustomEditorsUtil.cs @@ -71,7 +71,7 @@ namespace FlaxEditor.CustomEditors // Space before word starting with uppercase letter if (char.IsUpper(c) && i > 0) { - if (i + 2 < length && !char.IsUpper(name[i + 1]) && !char.IsUpper(name[i + 2])) + if (i + 1 < length && !char.IsUpper(name[i + 1])) sb.Append(' '); } // Space instead of underscore diff --git a/Source/Editor/CustomEditors/Dedicated/LayersMatrixEditor.cs b/Source/Editor/CustomEditors/Dedicated/LayersMatrixEditor.cs index 44a273ee4..6c825f46d 100644 --- a/Source/Editor/CustomEditors/Dedicated/LayersMatrixEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/LayersMatrixEditor.cs @@ -76,7 +76,7 @@ namespace FlaxEditor.CustomEditors.Dedicated upperRightCell.AddChild(new Label { Height = labelsHeight, - Text = layerNames[layerIndex], + Text = layerNames[layerNames.Length - layerIndex - 1], HorizontalAlignment = TextAlignment.Near, }); bottomLeftCell.AddChild(new Label diff --git a/Source/Editor/CustomEditors/Editors/ActorStaticFlagsEditor.cs b/Source/Editor/CustomEditors/Editors/ActorStaticFlagsEditor.cs index 86dd45812..cf649552a 100644 --- a/Source/Editor/CustomEditors/Editors/ActorStaticFlagsEditor.cs +++ b/Source/Editor/CustomEditors/Editors/ActorStaticFlagsEditor.cs @@ -23,7 +23,7 @@ namespace FlaxEditor.CustomEditors.Editors /// protected override void OnValueChanged() { - var value = (StaticFlags)element.EnumComboBox.EnumTypeValue; + var value = (StaticFlags)element.ComboBox.EnumTypeValue; // If selected is single actor that has children, ask if apply flags to the sub objects as well if (Values.IsSingleObject && (StaticFlags)Values[0] != value && ParentEditor.Values[0] is Actor actor && actor.HasChildren) diff --git a/Source/Editor/CustomEditors/Editors/EnumEditor.cs b/Source/Editor/CustomEditors/Editors/EnumEditor.cs index e277324e5..7954e18bf 100644 --- a/Source/Editor/CustomEditors/Editors/EnumEditor.cs +++ b/Source/Editor/CustomEditors/Editors/EnumEditor.cs @@ -40,7 +40,7 @@ namespace FlaxEditor.CustomEditors.Editors { var enumType = Values.Type.Type != typeof(object) || Values[0] == null ? TypeUtils.GetType(Values.Type) : Values[0].GetType(); element = layout.Enum(enumType, null, mode); - element.EnumComboBox.ValueChanged += OnValueChanged; + element.ComboBox.ValueChanged += OnValueChanged; } } @@ -49,7 +49,7 @@ namespace FlaxEditor.CustomEditors.Editors /// protected virtual void OnValueChanged() { - SetValue(element.EnumComboBox.EnumTypeValue); + SetValue(element.ComboBox.EnumTypeValue); } /// @@ -63,7 +63,7 @@ namespace FlaxEditor.CustomEditors.Editors } else { - element.EnumComboBox.EnumTypeValue = Values[0]; + element.ComboBox.EnumTypeValue = Values[0]; } } } diff --git a/Source/Editor/CustomEditors/Elements/EnumElement.cs b/Source/Editor/CustomEditors/Elements/EnumElement.cs index 27b4dfdb9..be4085f40 100644 --- a/Source/Editor/CustomEditors/Elements/EnumElement.cs +++ b/Source/Editor/CustomEditors/Elements/EnumElement.cs @@ -16,7 +16,7 @@ namespace FlaxEditor.CustomEditors.Elements /// /// The combo box used to show enum values. /// - public EnumComboBox EnumComboBox; + public EnumComboBox ComboBox; /// /// Initializes a new instance of the class. @@ -26,10 +26,10 @@ namespace FlaxEditor.CustomEditors.Elements /// The formatting mode. public EnumElement(Type type, EnumComboBox.BuildEntriesDelegate customBuildEntriesDelegate = null, EnumDisplayAttribute.FormatMode formatMode = EnumDisplayAttribute.FormatMode.Default) { - EnumComboBox = new EnumComboBox(type, customBuildEntriesDelegate, formatMode); + ComboBox = new EnumComboBox(type, customBuildEntriesDelegate, formatMode); } /// - public override Control Control => EnumComboBox; + public override Control Control => ComboBox; } } diff --git a/Source/Editor/Editor.cs b/Source/Editor/Editor.cs index 59810043d..f81f81ddd 100644 --- a/Source/Editor/Editor.cs +++ b/Source/Editor/Editor.cs @@ -254,7 +254,7 @@ namespace FlaxEditor if (loadingPreview != null) { // Link it to the prefab preview to see it in the editor - loadingPreview.customControlLinked = control.Control; + loadingPreview.customControlLinked = control; return loadingPreview; } return null; diff --git a/Source/Editor/Editor.h b/Source/Editor/Editor.h index 761e4b059..8c56043d9 100644 --- a/Source/Editor/Editor.h +++ b/Source/Editor/Editor.h @@ -43,7 +43,7 @@ public: public: /// - /// The flag used to determine if a project was used with the older engine version last time it was opened. Some cached data should be regenerated to prevent version difference issues. The version number comparision is based on major and minor part of the version. Build number is ignored. + /// The flag used to determine if a project was used with the older engine version last time it was opened. Some cached data should be regenerated to prevent version difference issues. The version number comparison is based on major and minor part of the version. Build number is ignored. /// static bool IsOldProjectOpened; diff --git a/Source/Editor/GUI/AssetPicker.cs b/Source/Editor/GUI/AssetPicker.cs index 63c1276ab..db012ab1e 100644 --- a/Source/Editor/GUI/AssetPicker.cs +++ b/Source/Editor/GUI/AssetPicker.cs @@ -355,12 +355,10 @@ namespace FlaxEditor.GUI _mousePos = location; // Check if start drag drop - if (_isMouseDown && Vector2.Distance(location, _mouseDownPos) > 10.0f) + if (_isMouseDown && Vector2.Distance(location, _mouseDownPos) > 10.0f && IconRect.Contains(_mouseDownPos)) { - // Clear flag - _isMouseDown = false; - // Do the drag + _isMouseDown = false; DoDrag(); } @@ -370,35 +368,35 @@ namespace FlaxEditor.GUI /// public override bool OnMouseUp(Vector2 location, MouseButton button) { - if (button == MouseButton.Left) + if (button == MouseButton.Left && _isMouseDown) { _isMouseDown = false; - } - // Buttons logic - if (Button1Rect.Contains(location)) - { - // Show asset picker popup - Focus(); - AssetSearchPopup.Show(this, Button1Rect.BottomLeft, IsValid, assetItem => + // Buttons logic + if (Button1Rect.Contains(location)) { - SelectedItem = assetItem; - RootWindow.Focus(); + // Show asset picker popup Focus(); - }); - } - else if (_selected != null || _selectedItem != null) - { - if (Button2Rect.Contains(location) && _selectedItem != null) - { - // Select asset - Editor.Instance.Windows.ContentWin.Select(_selectedItem); + AssetSearchPopup.Show(this, Button1Rect.BottomLeft, IsValid, assetItem => + { + SelectedItem = assetItem; + RootWindow.Focus(); + Focus(); + }); } - else if (Button3Rect.Contains(location)) + else if (_selected != null || _selectedItem != null) { - // Deselect asset - Focus(); - SelectedItem = null; + if (Button2Rect.Contains(location) && _selectedItem != null) + { + // Select asset + Editor.Instance.Windows.ContentWin.Select(_selectedItem); + } + else if (Button3Rect.Contains(location)) + { + // Deselect asset + Focus(); + SelectedItem = null; + } } } @@ -409,8 +407,7 @@ namespace FlaxEditor.GUI /// public override bool OnMouseDown(Vector2 location, MouseButton button) { - // Set flag for dragging asset - if (button == MouseButton.Left && IconRect.Contains(location)) + if (button == MouseButton.Left) { _isMouseDown = true; _mouseDownPos = location; diff --git a/Source/Editor/GUI/CurveEditor.cs b/Source/Editor/GUI/CurveEditor.cs index 8840a7f06..cef9a9dec 100644 --- a/Source/Editor/GUI/CurveEditor.cs +++ b/Source/Editor/GUI/CurveEditor.cs @@ -221,7 +221,7 @@ namespace FlaxEditor.GUI } /// - /// Filters teh given value using the the . + /// Filters the given value using the . /// /// The mode. /// The value to process. diff --git a/Source/Editor/GUI/Dialogs/Dialog.cs b/Source/Editor/GUI/Dialogs/Dialog.cs index b8897b2e1..173dd9128 100644 --- a/Source/Editor/GUI/Dialogs/Dialog.cs +++ b/Source/Editor/GUI/Dialogs/Dialog.cs @@ -186,7 +186,7 @@ namespace FlaxEditor.GUI.Dialogs // Clean up _window = null; - // Check if any thead is blocked during ShowDialog, then wait for it + // Check if any thread is blocked during ShowDialog, then wait for it bool wait = true; while (wait) { diff --git a/Source/Editor/GUI/Docking/DockWindow.cs b/Source/Editor/GUI/Docking/DockWindow.cs index aa6d94ca8..56c78efed 100644 --- a/Source/Editor/GUI/Docking/DockWindow.cs +++ b/Source/Editor/GUI/Docking/DockWindow.cs @@ -307,6 +307,14 @@ namespace FlaxEditor.GUI.Docking _dockedTo?.SelectTab(this, autoFocus); } + /// + /// Brings the window to the front of the Z order. + /// + public void BringToFront() + { + _dockedTo?.RootWindow?.BringToFront(); + } + internal void OnUnlinkInternal() { OnUnlink(); @@ -412,6 +420,7 @@ namespace FlaxEditor.GUI.Docking base.Focus(); SelectTab(); + BringToFront(); } /// diff --git a/Source/Editor/GUI/Docking/MasterDockPanel.cs b/Source/Editor/GUI/Docking/MasterDockPanel.cs index f7acdbed4..a560de195 100644 --- a/Source/Editor/GUI/Docking/MasterDockPanel.cs +++ b/Source/Editor/GUI/Docking/MasterDockPanel.cs @@ -64,7 +64,7 @@ namespace FlaxEditor.GUI.Docking for (int i = 0; i < childPanels.Length; i++) childPanels[i].Dispose(); - // Delete reaming controls (except tabs proxy) + // Delete remaining controls (except tabs proxy) if (TabsProxy != null) TabsProxy.Parent = null; DisposeChildren(); diff --git a/Source/Editor/GUI/Input/SliderControl.cs b/Source/Editor/GUI/Input/SliderControl.cs index 90288cfd0..77e8a5da8 100644 --- a/Source/Editor/GUI/Input/SliderControl.cs +++ b/Source/Editor/GUI/Input/SliderControl.cs @@ -168,6 +168,7 @@ namespace FlaxEditor.GUI.Input { if (button == MouseButton.Left) { + Focus(); float mousePosition = location.X; if (_thumbRect.Contains(ref location)) @@ -208,7 +209,6 @@ namespace FlaxEditor.GUI.Input { if (button == MouseButton.Left && _isSliding) { - // End sliding EndSliding(); return true; } diff --git a/Source/Editor/GUI/Input/UIntValueBox.cs b/Source/Editor/GUI/Input/UIntValueBox.cs index 9f1b2c1f4..d691be116 100644 --- a/Source/Editor/GUI/Input/UIntValueBox.cs +++ b/Source/Editor/GUI/Input/UIntValueBox.cs @@ -143,6 +143,8 @@ namespace FlaxEditor.GUI.Input try { var value = ShuntingYard.Parse(Text); + if (value < 0) + value = 0; Value = (uint)value; } catch (Exception ex) diff --git a/Source/Editor/GUI/Input/ValueBox.cs b/Source/Editor/GUI/Input/ValueBox.cs index 8ee1c3887..d34acd17b 100644 --- a/Source/Editor/GUI/Input/ValueBox.cs +++ b/Source/Editor/GUI/Input/ValueBox.cs @@ -49,6 +49,11 @@ namespace FlaxEditor.GUI.Input /// protected T _startSlideValue; + /// + /// The text cached on editing start. Used to compare with the end result to detect changes. + /// + protected string _startEditText; + private Vector2 _startSlideLocation; /// @@ -257,11 +262,23 @@ namespace FlaxEditor.GUI.Input return base.OnMouseUp(location, button); } + /// + protected override void OnEditBegin() + { + base.OnEditBegin(); + + _startEditText = _text; + } + /// protected override void OnEditEnd() { - // Update value - TryGetValue(); + if (_startEditText != _text) + { + // Update value + TryGetValue(); + } + _startEditText = null; base.OnEditEnd(); } diff --git a/Source/Editor/GUI/Table.cs b/Source/Editor/GUI/Table.cs index e070cba17..cca26cadc 100644 --- a/Source/Editor/GUI/Table.cs +++ b/Source/Editor/GUI/Table.cs @@ -120,7 +120,7 @@ namespace FlaxEditor.GUI /// /// Draws the column. /// - /// The the header area rectangle. + /// The header area rectangle. /// The zero-based index of the column. protected virtual void DrawColumn(ref Rectangle rect, int columnIndex) { diff --git a/Source/Editor/GUI/Timeline/GUI/KeyframesEditor.cs b/Source/Editor/GUI/Timeline/GUI/KeyframesEditor.cs index abd3e78a8..a12a71e21 100644 --- a/Source/Editor/GUI/Timeline/GUI/KeyframesEditor.cs +++ b/Source/Editor/GUI/Timeline/GUI/KeyframesEditor.cs @@ -401,7 +401,7 @@ namespace FlaxEditor.GUI } /// - /// Converts the input point from editor editor contents control space into the keyframes time/value coordinates. + /// Converts the input point from editor contents control space into the keyframes time/value coordinates. /// /// The point. /// The keyframes contents area bounds. diff --git a/Source/Editor/GUI/Timeline/Track.cs b/Source/Editor/GUI/Timeline/Track.cs index 4f5278251..6ba8191d6 100644 --- a/Source/Editor/GUI/Timeline/Track.cs +++ b/Source/Editor/GUI/Timeline/Track.cs @@ -16,6 +16,7 @@ namespace FlaxEditor.GUI.Timeline /// The Timeline track that contains a header and custom timeline events/media. /// /// + [HideInEditor] public class Track : ContainerControl { /// diff --git a/Source/Editor/GUI/Timeline/Tracks/MemberTrack.cs b/Source/Editor/GUI/Timeline/Tracks/MemberTrack.cs index 57de46ee4..250fb73c3 100644 --- a/Source/Editor/GUI/Timeline/Tracks/MemberTrack.cs +++ b/Source/Editor/GUI/Timeline/Tracks/MemberTrack.cs @@ -179,7 +179,7 @@ namespace FlaxEditor.GUI.Timeline.Tracks } /// - /// Evaluates the member value value at the specified time. + /// Evaluates the member value at the specified time. /// /// The time to evaluate the member at. /// The member value at provided time. diff --git a/Source/Editor/GUI/Tree/TreeNode.cs b/Source/Editor/GUI/Tree/TreeNode.cs index 727da3edd..f7e5f115a 100644 --- a/Source/Editor/GUI/Tree/TreeNode.cs +++ b/Source/Editor/GUI/Tree/TreeNode.cs @@ -21,7 +21,7 @@ namespace FlaxEditor.GUI.Tree /// /// The default node offset on Y axis. /// - public const float DefaultNodeOffsetY = 1; + public const float DefaultNodeOffsetY = 0; private Tree _tree; @@ -539,7 +539,7 @@ namespace FlaxEditor.GUI.Tree { if (new Rectangle(_headerRect.X, _headerRect.Y - DefaultDragInsertPositionMargin - DefaultNodeOffsetY, _headerRect.Width, DefaultDragInsertPositionMargin * 2.0f).Contains(location)) _dragOverMode = DragItemPositioning.Above; - else if (IsCollapsed && new Rectangle(_headerRect.X, _headerRect.Bottom - DefaultDragInsertPositionMargin, _headerRect.Width, DefaultDragInsertPositionMargin * 2.0f).Contains(location)) + else if ((IsCollapsed || !HasAnyVisibleChild) && new Rectangle(_headerRect.X, _headerRect.Bottom - DefaultDragInsertPositionMargin, _headerRect.Width, DefaultDragInsertPositionMargin * 2.0f).Contains(location)) _dragOverMode = DragItemPositioning.Below; else _dragOverMode = DragItemPositioning.At; diff --git a/Source/Editor/Gizmo/GizmosCollection.cs b/Source/Editor/Gizmo/GizmosCollection.cs index a246aeeb0..5530a41a3 100644 --- a/Source/Editor/Gizmo/GizmosCollection.cs +++ b/Source/Editor/Gizmo/GizmosCollection.cs @@ -7,7 +7,7 @@ using FlaxEngine; namespace FlaxEditor.Gizmo { /// - /// Represents collection of Gizmo tools where one is active and in use. + /// Represents collection of gizmo tools where one is active and in use. /// /// [HideInEditor] diff --git a/Source/Editor/Gizmo/TransformGizmoBase.Draw.cs b/Source/Editor/Gizmo/TransformGizmoBase.Draw.cs index 8f871c635..0a11ecd77 100644 --- a/Source/Editor/Gizmo/TransformGizmoBase.Draw.cs +++ b/Source/Editor/Gizmo/TransformGizmoBase.Draw.cs @@ -45,7 +45,7 @@ namespace FlaxEditor.Gizmo ) { // Error - Platform.Fatal("Failed to load Transform Gizmo resources."); + Platform.Fatal("Failed to load transform gizmo resources."); } } diff --git a/Source/Editor/Gizmo/TransformGizmoBase.Settings.cs b/Source/Editor/Gizmo/TransformGizmoBase.Settings.cs index 786e001e6..2955fe6d7 100644 --- a/Source/Editor/Gizmo/TransformGizmoBase.Settings.cs +++ b/Source/Editor/Gizmo/TransformGizmoBase.Settings.cs @@ -104,7 +104,7 @@ namespace FlaxEditor.Gizmo public Axis ActiveAxis => _activeAxis; /// - /// Gets or sts the current gizmo mode. + /// Gets or sets the current gizmo mode. /// public Mode ActiveMode { diff --git a/Source/Editor/Gizmo/TransformGizmoBase.cs b/Source/Editor/Gizmo/TransformGizmoBase.cs index 87dcb6fa8..014143685 100644 --- a/Source/Editor/Gizmo/TransformGizmoBase.cs +++ b/Source/Editor/Gizmo/TransformGizmoBase.cs @@ -151,7 +151,7 @@ namespace FlaxEditor.Gizmo // Set positions of the gizmo UpdateGizmoPosition(); - // Scale Gizmo to fit on-screen + // Scale gizmo to fit on-screen Vector3 vLength = Owner.ViewPosition - Position; float gizmoSize = Editor.Instance.Options.Options.Visual.GizmoSize; _screenScale = vLength.Length / GizmoScaleFactor * gizmoSize; @@ -196,12 +196,10 @@ namespace FlaxEditor.Gizmo private void UpdateTranslateScale() { bool isScaling = _activeMode == Mode.Scale; - Vector3 delta = Vector3.Zero; Ray ray = Owner.MouseRay; - Matrix invRotationMatrix; - Matrix.Invert(ref _rotationMatrix, out invRotationMatrix); + Matrix.Invert(ref _rotationMatrix, out var invRotationMatrix); ray.Position = Vector3.Transform(ray.Position, invRotationMatrix); Vector3.TransformNormal(ref ray.Direction, ref invRotationMatrix, out ray.Direction); @@ -211,9 +209,7 @@ namespace FlaxEditor.Gizmo case Axis.X: { var plane = new Plane(Vector3.Backward, Vector3.Transform(Position, invRotationMatrix).Z); - - float intersection; - if (ray.Intersects(ref plane, out intersection)) + if (ray.Intersects(ref plane, out float intersection)) { _intersectPosition = ray.Position + ray.Direction * intersection; if (_lastIntersectionPosition != Vector3.Zero) @@ -222,18 +218,14 @@ namespace FlaxEditor.Gizmo ? new Vector3(_tDelta.X, 0, 0) : new Vector3(_tDelta.X, _tDelta.Y, 0); } - break; } - case Axis.Z: case Axis.YZ: case Axis.Y: { var plane = new Plane(Vector3.Left, Vector3.Transform(Position, invRotationMatrix).X); - - float intersection; - if (ray.Intersects(ref plane, out intersection)) + if (ray.Intersects(ref plane, out float intersection)) { _intersectPosition = ray.Position + ray.Direction * intersection; if (_lastIntersectionPosition != Vector3.Zero) @@ -251,41 +243,31 @@ namespace FlaxEditor.Gizmo break; } } - break; } - case Axis.ZX: { var plane = new Plane(Vector3.Down, Vector3.Transform(Position, invRotationMatrix).Y); - - float intersection; - if (ray.Intersects(ref plane, out intersection)) + if (ray.Intersects(ref plane, out float intersection)) { _intersectPosition = ray.Position + ray.Direction * intersection; if (_lastIntersectionPosition != Vector3.Zero) _tDelta = _intersectPosition - _lastIntersectionPosition; delta = new Vector3(_tDelta.X, 0, _tDelta.Z); } - break; } - case Axis.Center: { - Vector3 gizmoToView = Position - Owner.ViewPosition; + var gizmoToView = Position - Owner.ViewPosition; var plane = new Plane(-Vector3.Normalize(gizmoToView), gizmoToView.Length); - - float intersection; - if (ray.Intersects(ref plane, out intersection)) + if (ray.Intersects(ref plane, out float intersection)) { _intersectPosition = ray.Position + ray.Direction * intersection; if (_lastIntersectionPosition != Vector3.Zero) _tDelta = _intersectPosition - _lastIntersectionPosition; } - delta = _tDelta; - break; } } @@ -299,14 +281,11 @@ namespace FlaxEditor.Gizmo if ((isScaling ? ScaleSnapEnabled : TranslationSnapEnable) || Owner.UseSnapping) { float snapValue = isScaling ? ScaleSnapValue : TranslationSnapValue; - _translationScaleSnapDelta += delta; - delta = new Vector3( (int)(_translationScaleSnapDelta.X / snapValue) * snapValue, (int)(_translationScaleSnapDelta.Y / snapValue) * snapValue, (int)(_translationScaleSnapDelta.Z / snapValue) * snapValue); - _translationScaleSnapDelta -= delta; } @@ -318,7 +297,30 @@ namespace FlaxEditor.Gizmo } else if (_activeMode == Mode.Scale) { - // Apply Scale + // Scale + if (_activeTransformSpace == TransformSpace.World && _activeAxis != Axis.Center) + { + var deltaLocal = delta; + Quaternion orientation = GetSelectedObject(0).Orientation; + delta = Vector3.Transform(delta, orientation); + + // Fix axis sign of delta movement for rotated object in some cases (eg. rotated object by 90 deg on Y axis and scale in world space with Red/X axis) + switch (_activeAxis) + { + case Axis.X: + if (deltaLocal.X < 0) + delta *= -1; + break; + case Axis.Y: + if (deltaLocal.Y < 0) + delta *= -1; + break; + case Axis.Z: + if (deltaLocal.Z < 0) + delta *= -1; + break; + } + } _scaleDelta = delta; } } @@ -382,7 +384,7 @@ namespace FlaxEditor.Gizmo // Snap to ground if (_activeAxis == Axis.None && SelectionCount != 0 && Owner.SnapToGround) { - if (Physics.RayCast(Position, Vector3.Down, out var hit, float.MaxValue, int.MaxValue, false)) + if (Physics.RayCast(Position, Vector3.Down, out var hit, float.MaxValue, uint.MaxValue, false)) { StartTransforming(); var translationDelta = hit.Point - Position; @@ -408,7 +410,6 @@ namespace FlaxEditor.Gizmo case Mode.Translate: UpdateTranslateScale(); break; - case Mode.Rotate: UpdateRotate(dt); break; @@ -437,7 +438,7 @@ namespace FlaxEditor.Gizmo translationDelta = _translationDelta; _translationDelta = Vector3.Zero; - // Prevent from moving objects too far away, like to different galaxy or sth + // Prevent from moving objects too far away, like to a different galaxy or sth Vector3 prevMoveDelta = _accMoveDelta; _accMoveDelta += _translationDelta; if (_accMoveDelta.Length > Owner.ViewFarPlane * 0.7f) diff --git a/Source/Editor/History/HistoryStack.cs b/Source/Editor/History/HistoryStack.cs index c7a63efc4..e2d828ce9 100644 --- a/Source/Editor/History/HistoryStack.cs +++ b/Source/Editor/History/HistoryStack.cs @@ -74,7 +74,7 @@ namespace FlaxEditor.History _reverseActions.PushBack(reverse[i]); } - // Cleanup reaming actions + // Cleanup remaining actions for (int i = _historyActionsLimit; i < history.Length; i++) { history[i].Dispose(); diff --git a/Source/Editor/Modules/ContentFindingModule.cs b/Source/Editor/Modules/ContentFindingModule.cs index 88b5832af..9be15e0d1 100644 --- a/Source/Editor/Modules/ContentFindingModule.cs +++ b/Source/Editor/Modules/ContentFindingModule.cs @@ -108,7 +108,7 @@ namespace FlaxEditor.Modules /// /// Removes a quick action by name. /// - /// Thr action's name. + /// The action's name. /// True when it succeed, false if there is no Quick Action with this name. public bool RemoveQuickAction(string name) { diff --git a/Source/Editor/Modules/ContentImportingModule.cs b/Source/Editor/Modules/ContentImportingModule.cs index 03f434b9c..0c934f130 100644 --- a/Source/Editor/Modules/ContentImportingModule.cs +++ b/Source/Editor/Modules/ContentImportingModule.cs @@ -410,7 +410,7 @@ namespace FlaxEditor.Modules { if (request.Settings != null && entry.TryOverrideSettings(request.Settings)) { - // Use overriden settings + // Use overridden settings } else if (!request.SkipSettingsDialog) { diff --git a/Source/Editor/Modules/WindowsModule.cs b/Source/Editor/Modules/WindowsModule.cs index 9b80b664c..a8058b9d8 100644 --- a/Source/Editor/Modules/WindowsModule.cs +++ b/Source/Editor/Modules/WindowsModule.cs @@ -276,8 +276,8 @@ namespace FlaxEditor.Modules // Get metadata int version = int.Parse(root.Attributes["Version"].Value, CultureInfo.InvariantCulture); var virtualDesktopBounds = Platform.VirtualDesktopBounds; - var virtualDesktopSafeLeftCorner = virtualDesktopBounds.Location + new Vector2(0, 23); // 23 is a window strip size - var virtualDesktopSafeRightCorner = virtualDesktopBounds.BottomRight - new Vector2(50, 50); // apply some safe area + var virtualDesktopSafeLeftCorner = virtualDesktopBounds.Location; + var virtualDesktopSafeRightCorner = virtualDesktopBounds.BottomRight; switch (version) { @@ -971,7 +971,7 @@ namespace FlaxEditor.Modules } #region Window Events - + private void OnEditorStateChanged() { for (int i = 0; i < Windows.Count; i++) diff --git a/Source/Editor/Options/InputBinding.cs b/Source/Editor/Options/InputBinding.cs index e28e8fa0c..eddd76882 100644 --- a/Source/Editor/Options/InputBinding.cs +++ b/Source/Editor/Options/InputBinding.cs @@ -143,7 +143,31 @@ namespace FlaxEditor.Options { var root = control.Root; - if (root.GetKeyDown(Key)) + if (root.GetKey(Key)) + { + if (Modifier1 == KeyboardKeys.None || root.GetKey(Modifier1)) + { + if (Modifier2 == KeyboardKeys.None || root.GetKey(Modifier2)) + { + return true; + } + } + } + + return false; + } + + /// + /// Processes this input binding to check if state matches. + /// + /// The input providing control. + /// The input key. + /// True if input has been processed, otherwise false. + public bool Process(Control control, KeyboardKeys key) + { + var root = control.Root; + + if (key == Key) { if (Modifier1 == KeyboardKeys.None || root.GetKey(Modifier1)) { @@ -435,16 +459,10 @@ namespace FlaxEditor.Options for (int i = 0; i < _bindings.Count; i++) { var binding = _bindings[i].Binder(options); - if (binding.Key == key) + if (binding.Process(control, key)) { - if (binding.Modifier1 == KeyboardKeys.None || root.GetKey(binding.Modifier1)) - { - if (binding.Modifier2 == KeyboardKeys.None || root.GetKey(binding.Modifier2)) - { - _bindings[i].Callback(); - return true; - } - } + _bindings[i].Callback(); + return true; } } diff --git a/Source/Editor/Options/OptionsModule.cs b/Source/Editor/Options/OptionsModule.cs index 7c0cd11da..3c501de85 100644 --- a/Source/Editor/Options/OptionsModule.cs +++ b/Source/Editor/Options/OptionsModule.cs @@ -41,7 +41,7 @@ namespace FlaxEditor.Options private readonly Dictionary _customSettings = new Dictionary(); /// - /// Gets the custom settings factories. Each entry defines the custom settings type identified by teh given key name. The value si a factory function that returns the default options fpr a given type. + /// Gets the custom settings factories. Each entry defines the custom settings type identified by the given key name. The value is a factory function that returns the default options for a given type. /// public IReadOnlyDictionary CustomSettings => _customSettings; diff --git a/Source/Editor/Options/ViewportOptions.cs b/Source/Editor/Options/ViewportOptions.cs index 6e1f8a9d6..5558ac89d 100644 --- a/Source/Editor/Options/ViewportOptions.cs +++ b/Source/Editor/Options/ViewportOptions.cs @@ -45,5 +45,12 @@ namespace FlaxEditor.Options [DefaultValue(60.0f), Limit(35.0f, 160.0f, 0.1f)] [EditorDisplay("Defaults", "Default Field Of View"), EditorOrder(140), Tooltip("The default field of view angle (in degrees) for the viewport camera.")] public float DefaultFieldOfView { get; set; } = 60.0f; + + /// + /// Gets or sets if the panning direction is inverted for the viewport camera. + /// + [DefaultValue(false)] + [EditorDisplay("Defaults"), EditorOrder(150), Tooltip( "Invert the panning direction for the viewport camera." )] + public bool DefaultInvertPanning { get; set; } = false; } } diff --git a/Source/Editor/Progress/ProgressHandler.cs b/Source/Editor/Progress/ProgressHandler.cs index da2782d16..958612e51 100644 --- a/Source/Editor/Progress/ProgressHandler.cs +++ b/Source/Editor/Progress/ProgressHandler.cs @@ -79,7 +79,7 @@ namespace FlaxEditor.Progress } /// - /// Called when progress action gets updated (changed nfo text or progress value). + /// Called when progress action gets updated (changed info text or progress value). /// /// The progress (normalized to range [0;1]). /// The information text. diff --git a/Source/Editor/ProjectInfo.cpp b/Source/Editor/ProjectInfo.cpp index 0f916241e..47782ab9d 100644 --- a/Source/Editor/ProjectInfo.cpp +++ b/Source/Editor/ProjectInfo.cpp @@ -204,7 +204,7 @@ bool ProjectInfo::LoadProject(const String& projectPath) reference.Project = Load(referencePath); if (reference.Project == nullptr) { - LOG(Error, "Faield to load referenced project ({0}, from {1})", reference.Name, referencePath); + LOG(Error, "Failed to load referenced project ({0}, from {1})", reference.Name, referencePath); return true; } } @@ -277,7 +277,7 @@ bool ProjectInfo::LoadOldProject(const String& projectPath) flaxReference.Project = Load(Globals::StartupFolder / TEXT("Flax.flaxproj")); if (!flaxReference.Project) { - ShowProjectLoadError(TEXT("Failed to load Flax Engien project."), projectPath); + ShowProjectLoadError(TEXT("Failed to load Flax Engine project."), projectPath); return true; } diff --git a/Source/Editor/Scripting/CodeEditors/VisualStudio/VisualStudioConnection.cpp b/Source/Editor/Scripting/CodeEditors/VisualStudio/VisualStudioConnection.cpp index d73c5279a..54ba119f2 100644 --- a/Source/Editor/Scripting/CodeEditors/VisualStudio/VisualStudioConnection.cpp +++ b/Source/Editor/Scripting/CodeEditors/VisualStudio/VisualStudioConnection.cpp @@ -49,7 +49,7 @@ public: { if (dwRejectType == SERVERCALL_RETRYLATER) { - // Retry immediatey + // Retry immediately return 99; } diff --git a/Source/Editor/Scripting/ScriptsBuilder.cpp b/Source/Editor/Scripting/ScriptsBuilder.cpp index 7d581fa8a..7cced3e36 100644 --- a/Source/Editor/Scripting/ScriptsBuilder.cpp +++ b/Source/Editor/Scripting/ScriptsBuilder.cpp @@ -567,7 +567,7 @@ bool ScriptsBuilderService::Init() LOG(Warning, "Missing EditorTarget property in opened project, using deducted target name {0}", Editor::Project->EditorTarget); } - // Remove any reaming files from previous Editor run hot-reloads + // Remove any remaining files from previous Editor run hot-reloads const Char *target, *platform, *architecture, *configuration; ScriptsBuilder::GetBinariesConfiguration(target, platform, architecture, configuration); if (target) diff --git a/Source/Editor/Surface/AnimGraphSurface.cs b/Source/Editor/Surface/AnimGraphSurface.cs index 8621ed257..abffc4432 100644 --- a/Source/Editor/Surface/AnimGraphSurface.cs +++ b/Source/Editor/Surface/AnimGraphSurface.cs @@ -65,8 +65,8 @@ namespace FlaxEditor.Surface NodeElementArchetype.Factory.Output(0, "Length", typeof(float), 0), NodeElementArchetype.Factory.Output(1, "Time", typeof(float), 1), NodeElementArchetype.Factory.Output(2, "Normalized Time", typeof(float), 2), - NodeElementArchetype.Factory.Output(3, "Reaming Time", typeof(float), 3), - NodeElementArchetype.Factory.Output(4, "Reaming Normalized Time", typeof(float), 4), + NodeElementArchetype.Factory.Output(3, "Remaining Time", typeof(float), 3), + NodeElementArchetype.Factory.Output(4, "Remaining Normalized Time", typeof(float), 4), } }, } diff --git a/Source/Editor/Surface/Archetypes/Animation.StateMachine.cs b/Source/Editor/Surface/Archetypes/Animation.StateMachine.cs index 9f9e21f4f..2faa9feef 100644 --- a/Source/Editor/Surface/Archetypes/Animation.StateMachine.cs +++ b/Source/Editor/Surface/Archetypes/Animation.StateMachine.cs @@ -1046,7 +1046,7 @@ namespace FlaxEditor.Surface.Archetypes startPos += nrm; endPos += nrm; - // Swap fo the other arrow + // Swap to the other arrow if (!diff) { var tmp = startPos; diff --git a/Source/Editor/Surface/Archetypes/Animation.cs b/Source/Editor/Surface/Archetypes/Animation.cs index b27fb9bd6..15096e696 100644 --- a/Source/Editor/Surface/Archetypes/Animation.cs +++ b/Source/Editor/Surface/Archetypes/Animation.cs @@ -764,8 +764,8 @@ namespace FlaxEditor.Surface.Archetypes NodeElementArchetype.Factory.Output(0, "Length", typeof(float), 0), NodeElementArchetype.Factory.Output(1, "Time", typeof(float), 1), NodeElementArchetype.Factory.Output(2, "Normalized Time", typeof(float), 2), - NodeElementArchetype.Factory.Output(3, "Reaming Time", typeof(float), 3), - NodeElementArchetype.Factory.Output(4, "Reaming Normalized Time", typeof(float), 4), + NodeElementArchetype.Factory.Output(3, "Remaining Time", typeof(float), 3), + NodeElementArchetype.Factory.Output(4, "Remaining Normalized Time", typeof(float), 4), } }, new NodeArchetype @@ -791,7 +791,7 @@ namespace FlaxEditor.Surface.Archetypes Title = "Transform Node (local space)", Description = "Transforms the skeleton node", Flags = NodeFlags.AnimGraph, - Size = new Vector2(270, 130), + Size = new Vector2(280, 130), DefaultValues = new object[] { string.Empty, @@ -816,7 +816,7 @@ namespace FlaxEditor.Surface.Archetypes Title = "Transform Node (model space)", Description = "Transforms the skeleton node", Flags = NodeFlags.AnimGraph, - Size = new Vector2(270, 130), + Size = new Vector2(280, 130), DefaultValues = new object[] { string.Empty, @@ -872,7 +872,7 @@ namespace FlaxEditor.Surface.Archetypes Title = "Get Node Transform (model space)", Description = "Samples the skeleton node transformation (in model space)", Flags = NodeFlags.AnimGraph, - Size = new Vector2(250, 40), + Size = new Vector2(324, 40), DefaultValues = new object[] { string.Empty, @@ -880,7 +880,7 @@ namespace FlaxEditor.Surface.Archetypes Elements = new[] { NodeElementArchetype.Factory.Input(0, string.Empty, true, typeof(void), 0), - NodeElementArchetype.Factory.SkeletonNodeNameSelect(40, Surface.Constants.LayoutOffsetY * 1, 120, 0), + NodeElementArchetype.Factory.SkeletonNodeNameSelect(40, Surface.Constants.LayoutOffsetY * 1, 160, 0), NodeElementArchetype.Factory.Text(0, Surface.Constants.LayoutOffsetY * 1, "Node:"), NodeElementArchetype.Factory.Output(0, "Transform", typeof(Transform), 1), } @@ -903,7 +903,7 @@ namespace FlaxEditor.Surface.Archetypes NodeElementArchetype.Factory.Input(0, string.Empty, true, typeof(void), 1), NodeElementArchetype.Factory.Input(1, "Target", true, typeof(Vector3), 2), NodeElementArchetype.Factory.Input(2, "Weight", true, typeof(float), 3, 1), - NodeElementArchetype.Factory.SkeletonNodeNameSelect(40, Surface.Constants.LayoutOffsetY * 3, 120, 0), + NodeElementArchetype.Factory.SkeletonNodeNameSelect(40, Surface.Constants.LayoutOffsetY * 3, 160, 0), NodeElementArchetype.Factory.Text(0, Surface.Constants.LayoutOffsetY * 3, "Node:"), } }, @@ -913,7 +913,7 @@ namespace FlaxEditor.Surface.Archetypes Title = "Get Node Transform (local space)", Description = "Samples the skeleton node transformation (in local space)", Flags = NodeFlags.AnimGraph, - Size = new Vector2(250, 40), + Size = new Vector2(316, 40), DefaultValues = new object[] { string.Empty, diff --git a/Source/Editor/Surface/Archetypes/Constants.cs b/Source/Editor/Surface/Archetypes/Constants.cs index 63cec366b..3ae9d6a8a 100644 --- a/Source/Editor/Surface/Archetypes/Constants.cs +++ b/Source/Editor/Surface/Archetypes/Constants.cs @@ -379,7 +379,7 @@ namespace FlaxEditor.Surface.Archetypes Elements = new[] { NodeElementArchetype.Factory.Output(0, "Value", typeof(uint), 0), - NodeElementArchetype.Factory.Integer(0, 0, 0) + NodeElementArchetype.Factory.UnsignedInteger(0, 0, 0, -1, 0, int.MaxValue) } }, }; diff --git a/Source/Editor/Surface/Archetypes/Function.cs b/Source/Editor/Surface/Archetypes/Function.cs index 7c4963072..9f7e3757a 100644 --- a/Source/Editor/Surface/Archetypes/Function.cs +++ b/Source/Editor/Surface/Archetypes/Function.cs @@ -867,8 +867,15 @@ namespace FlaxEditor.Surface.Archetypes for (int i = 0; i < signature.Params.Length; i++) { ref var param = ref signature.Params[i]; - if (param.Type != memberParameters[i].Type || param.IsOut != memberParameters[i].IsOut) + ref var paramMember = ref memberParameters[i]; + if (param.Type != paramMember.Type || param.IsOut != paramMember.IsOut) { + // Special case: param.Type is serialized as just a type while paramMember.Type might be a reference for output parameters (eg. `out Int32` vs `out Int32&`) + var paramMemberTypeName = paramMember.Type.TypeName; + if (param.IsOut && param.IsOut == paramMember.IsOut && paramMember.Type.IsReference && !param.Type.IsReference && + paramMemberTypeName.Substring(0, paramMemberTypeName.Length - 1) == param.Type.TypeName) + continue; + isInvalid = true; break; } @@ -1176,7 +1183,7 @@ namespace FlaxEditor.Surface.Archetypes [EditorOrder(0), Tooltip("The name of the parameter."), ExpandGroups] public string Name; - [EditorOrder(1), Tooltip("The type fo the parameter value.")] + [EditorOrder(1), Tooltip("The type for the parameter value.")] [TypeReference(typeof(object), nameof(IsTypeValid))] public ScriptType Type; @@ -1547,7 +1554,7 @@ namespace FlaxEditor.Surface.Archetypes // Check if return type has been changed if (_signature.ReturnType != prevReturnType) { - // Update all return nodes used by this function to match teh new type + // Update all return nodes used by this function to match the new type var usedNodes = DepthFirstTraversal(false); var hasAnyReturnNode = false; foreach (var node in usedNodes) diff --git a/Source/Editor/Surface/Archetypes/Material.cs b/Source/Editor/Surface/Archetypes/Material.cs index a007db7d7..ceb9921cb 100644 --- a/Source/Editor/Surface/Archetypes/Material.cs +++ b/Source/Editor/Surface/Archetypes/Material.cs @@ -642,6 +642,179 @@ namespace FlaxEditor.Surface.Archetypes NodeElementArchetype.Factory.Output(0, "XYZ", typeof(Vector3), 0), } }, + new NodeArchetype + { + TypeID = 26, + Title = "Blend Normals", + Description = "Blend two normal maps to create a single normal map", + Flags = NodeFlags.MaterialGraph, + Size = new Vector2(170, 40), + ConnectionsHints = ConnectionsHint.Vector, + Elements = new[] + { + NodeElementArchetype.Factory.Input(0, "Base Normal", true, typeof(Vector3), 0), + NodeElementArchetype.Factory.Input(1, "Additional Normal", true, typeof(Vector3), 1), + NodeElementArchetype.Factory.Output(0, "Result", typeof(Vector3), 2) + } + }, + new NodeArchetype + { + TypeID = 27, + Title = "Rotator", + Description = "Rotates UV coordinates according to a scalar angle (0-1)", + Flags = NodeFlags.MaterialGraph, + Size = new Vector2(150, 55), + Elements = new[] + { + NodeElementArchetype.Factory.Input(0, "UV", true, typeof(Vector2), 0), + NodeElementArchetype.Factory.Input(1, "Center", true, typeof(Vector2), 1), + NodeElementArchetype.Factory.Input(2, "Rotation Angle", true, typeof(float), 2), + NodeElementArchetype.Factory.Output(0, string.Empty, typeof(Vector2), 3), + } + }, + new NodeArchetype + { + TypeID = 28, + Title = "Sphere Mask", + Description = "Creates a sphere mask", + Flags = NodeFlags.MaterialGraph, + Size = new Vector2(150, 100), + ConnectionsHints = ConnectionsHint.Vector, + IndependentBoxes = new[] + { + 0, + 1 + }, + DefaultValues = new object[] + { + 0.3f, + 0.5f, + false + }, + Elements = new[] + { + NodeElementArchetype.Factory.Input(0, "A", true, null, 0), + NodeElementArchetype.Factory.Input(1, "B", true, null, 1), + NodeElementArchetype.Factory.Input(2, "Radius", true, typeof(float), 2, 0), + NodeElementArchetype.Factory.Input(3, "Hardness", true, typeof(float), 3, 1), + NodeElementArchetype.Factory.Input(4, "Invert", true, typeof(bool), 4, 2), + NodeElementArchetype.Factory.Output(0, string.Empty, typeof(float), 5), + } + }, + new NodeArchetype + { + TypeID = 29, + Title = "UV Tiling & Offset", + Description = "Takes UVs and applies tiling and offset", + Flags = NodeFlags.MaterialGraph, + Size = new Vector2(175, 60), + DefaultValues = new object[] + { + Vector2.One, + Vector2.Zero + }, + Elements = new[] + { + NodeElementArchetype.Factory.Input(0, "UV", true, typeof(Vector2), 0), + NodeElementArchetype.Factory.Input(1, "Tiling", true, typeof(Vector2), 1, 0), + NodeElementArchetype.Factory.Input(2, "Offset", true, typeof(Vector2), 2, 1), + NodeElementArchetype.Factory.Output(0, string.Empty, typeof(Vector2), 3), + } + }, + new NodeArchetype + { + TypeID = 30, + Title = "DDX", + Description = "Returns the partial derivative of the specified value with respect to the screen-space x-coordinate", + Flags = NodeFlags.MaterialGraph, + Size = new Vector2(90, 25), + ConnectionsHints = ConnectionsHint.Numeric, + IndependentBoxes = new[] { 0 }, + DependentBoxes = new[] { 1 }, + Elements = new[] + { + NodeElementArchetype.Factory.Input(0, "Value", true, null, 0), + NodeElementArchetype.Factory.Output(0, string.Empty, null, 1), + } + }, + new NodeArchetype + { + TypeID = 31, + Title = "DDY", + Description = "Returns the partial derivative of the specified value with respect to the screen-space y-coordinate", + Flags = NodeFlags.MaterialGraph, + Size = new Vector2(90, 25), + ConnectionsHints = ConnectionsHint.Numeric, + IndependentBoxes = new[] { 0 }, + DependentBoxes = new[] { 1 }, + Elements = new[] + { + NodeElementArchetype.Factory.Input(0, "Value", true, null, 0), + NodeElementArchetype.Factory.Output(0, string.Empty, null, 1), + } + }, + new NodeArchetype + { + TypeID = 32, + Title = "Sign", + Description = "Returns -1 if value is less than zero; 0 if value equals zero; and 1 if value is greater than zero", + Flags = NodeFlags.MaterialGraph, + Size = new Vector2(90, 25), + ConnectionsHints = ConnectionsHint.Numeric, + IndependentBoxes = new[] { 0 }, + Elements = new[] + { + NodeElementArchetype.Factory.Input(0, "Value", true, null, 0), + NodeElementArchetype.Factory.Output(0, string.Empty, typeof(float), 1), + } + }, + new NodeArchetype + { + TypeID = 33, + Title = "Any", + Description = "True if any components of value are non-zero; otherwise, false", + Flags = NodeFlags.MaterialGraph, + Size = new Vector2(90, 25), + ConnectionsHints = ConnectionsHint.Numeric, + IndependentBoxes = new[] { 0 }, + Elements = new[] + { + NodeElementArchetype.Factory.Input(0, "Value", true, null, 0), + NodeElementArchetype.Factory.Output(0, string.Empty, typeof(bool), 1), + } + }, + new NodeArchetype + { + TypeID = 34, + Title = "All", + Description = "Determines if all components of the specified value are non-zero", + Flags = NodeFlags.MaterialGraph, + Size = new Vector2(90, 25), + ConnectionsHints = ConnectionsHint.Numeric, + IndependentBoxes = new[] { 0 }, + Elements = new[] + { + NodeElementArchetype.Factory.Input(0, "Value", true, null, 0), + NodeElementArchetype.Factory.Output(0, string.Empty, typeof(bool), 1), + } + }, + new NodeArchetype + { + TypeID = 35, + Title = "Black Body", + Description = "Simulates black body radiation via a given temperature in kelvin", + Flags = NodeFlags.MaterialGraph, + Size = new Vector2(120, 25), + DefaultValues = new object[] + { + 0.0f, + }, + Elements = new[] + { + NodeElementArchetype.Factory.Input(0, "Temp", true, typeof(float), 0, 0), + NodeElementArchetype.Factory.Output(0, string.Empty, typeof(Vector3), 1), + } + }, }; } } diff --git a/Source/Editor/Surface/Archetypes/Math.cs b/Source/Editor/Surface/Archetypes/Math.cs index 92ae69a12..7416ea498 100644 --- a/Source/Editor/Surface/Archetypes/Math.cs +++ b/Source/Editor/Surface/Archetypes/Math.cs @@ -404,6 +404,29 @@ namespace FlaxEditor.Surface.Archetypes NodeElementArchetype.Factory.Output(0, "A | B", null, 2), } }, + new NodeArchetype + { + TypeID = 48, + Title = "Remap", + Description = "Remaps a value from one range to another, so for example having 25 in a range of 0 to 100 being remapped to 0 to 1 would return 0.25", + Flags = NodeFlags.AllGraphs, + Size = new Vector2(175, 75), + DefaultValues = new object[] + { + 25.0f, + new Vector2(0.0f, 100.0f), + new Vector2(0.0f, 1.0f), + false + }, + Elements = new[] + { + NodeElementArchetype.Factory.Input(0, "Value", true, typeof(float), 0, 0), + NodeElementArchetype.Factory.Input(1, "In Range", true, typeof(Vector2), 1, 1), + NodeElementArchetype.Factory.Input(2, "Out Range", true, typeof(Vector2), 2, 2), + NodeElementArchetype.Factory.Input(3, "Clamp", true, typeof(bool), 3, 3), + NodeElementArchetype.Factory.Output(0, string.Empty, typeof(float), 4), + } + }, }; } } diff --git a/Source/Editor/Surface/Archetypes/Packing.cs b/Source/Editor/Surface/Archetypes/Packing.cs index 04f3819ea..f5b466ab3 100644 --- a/Source/Editor/Surface/Archetypes/Packing.cs +++ b/Source/Editor/Surface/Archetypes/Packing.cs @@ -486,7 +486,7 @@ namespace FlaxEditor.Surface.Archetypes Description = "Unpack X component from Vector", Flags = NodeFlags.AllGraphs, ConnectionsHints = ConnectionsHint.Vector, - Size = new Vector2(160, 30), + Size = new Vector2(110, 30), Elements = new[] { NodeElementArchetype.Factory.Input(0, "Value", true, null, 0), @@ -500,7 +500,7 @@ namespace FlaxEditor.Surface.Archetypes Description = "Unpack Y component from Vector", Flags = NodeFlags.AllGraphs, ConnectionsHints = ConnectionsHint.Vector, - Size = new Vector2(160, 30), + Size = new Vector2(110, 30), Elements = new[] { NodeElementArchetype.Factory.Input(0, "Value", true, null, 0), @@ -514,7 +514,7 @@ namespace FlaxEditor.Surface.Archetypes Description = "Unpack Z component from Vector", Flags = NodeFlags.AllGraphs, ConnectionsHints = ConnectionsHint.Vector, - Size = new Vector2(160, 30), + Size = new Vector2(110, 30), Elements = new[] { NodeElementArchetype.Factory.Input(0, "Value", true, null, 0), @@ -528,7 +528,7 @@ namespace FlaxEditor.Surface.Archetypes Description = "Unpack W component from Vector", Flags = NodeFlags.AllGraphs, ConnectionsHints = ConnectionsHint.Vector, - Size = new Vector2(160, 30), + Size = new Vector2(110, 30), Elements = new[] { NodeElementArchetype.Factory.Input(0, "Value", true, null, 0), @@ -544,7 +544,7 @@ namespace FlaxEditor.Surface.Archetypes Description = "Unpack XY components from Vector", Flags = NodeFlags.AllGraphs, ConnectionsHints = ConnectionsHint.Vector, - Size = new Vector2(160, 30), + Size = new Vector2(110, 30), Elements = new[] { NodeElementArchetype.Factory.Input(0, "Value", true, null, 0), @@ -558,7 +558,7 @@ namespace FlaxEditor.Surface.Archetypes Description = "Unpack XZ components from Vector", Flags = NodeFlags.AllGraphs, ConnectionsHints = ConnectionsHint.Vector, - Size = new Vector2(160, 30), + Size = new Vector2(110, 30), Elements = new[] { NodeElementArchetype.Factory.Input(0, "Value", true, null, 0), @@ -572,7 +572,7 @@ namespace FlaxEditor.Surface.Archetypes Description = "Unpack YZ components from Vector", Flags = NodeFlags.AllGraphs, ConnectionsHints = ConnectionsHint.Vector, - Size = new Vector2(160, 30), + Size = new Vector2(110, 30), Elements = new[] { NodeElementArchetype.Factory.Input(0, "Value", true, null, 0), @@ -588,7 +588,7 @@ namespace FlaxEditor.Surface.Archetypes Description = "Unpack XYZ components from Vector", Flags = NodeFlags.AllGraphs, ConnectionsHints = ConnectionsHint.Vector, - Size = new Vector2(160, 30), + Size = new Vector2(110, 30), Elements = new[] { NodeElementArchetype.Factory.Input(0, "Value", true, null, 0), diff --git a/Source/Editor/Surface/Archetypes/Parameters.cs b/Source/Editor/Surface/Archetypes/Parameters.cs index 43048d19d..5332b3475 100644 --- a/Source/Editor/Surface/Archetypes/Parameters.cs +++ b/Source/Editor/Surface/Archetypes/Parameters.cs @@ -32,7 +32,7 @@ namespace FlaxEditor.Surface.Archetypes public Dictionary Prototypes = DefaultPrototypes; /// - /// The default prototypes for thr node elements to use for the given parameter type. + /// The default prototypes for the node elements to use for the given parameter type. /// public static readonly Dictionary DefaultPrototypes = new Dictionary { diff --git a/Source/Editor/Surface/Archetypes/Particles.cs b/Source/Editor/Surface/Archetypes/Particles.cs index 4d72bfa7b..1f3ee8ce6 100644 --- a/Source/Editor/Surface/Archetypes/Particles.cs +++ b/Source/Editor/Surface/Archetypes/Particles.cs @@ -338,7 +338,7 @@ namespace FlaxEditor.Surface.Archetypes Size = new Vector2(300, 600), DefaultValues = new object[] { - 500, // Capacity + 1000, // Capacity (int)ParticlesSimulationMode.Default, // Simulation Mode (int)ParticlesSimulationSpace.Local, // Simulation Space true, // Enable Pooling diff --git a/Source/Editor/Surface/Elements/Box.cs b/Source/Editor/Surface/Elements/Box.cs index 886ab1e37..b8d88741f 100644 --- a/Source/Editor/Surface/Elements/Box.cs +++ b/Source/Editor/Surface/Elements/Box.cs @@ -97,7 +97,7 @@ namespace FlaxEditor.Surface.Elements var connections = Connections.ToArray(); for (int i = 0; i < connections.Length; i++) { - var targetBox = Connections[i]; + var targetBox = connections[i]; // Break connection Connections.Remove(targetBox); @@ -565,7 +565,28 @@ namespace FlaxEditor.Surface.Elements { _isMouseDown = false; if (Surface.CanEdit) - Surface.ConnectingStart(this); + { + if (!IsOutput && HasSingleConnection) + { + var connectedBox = Connections[0]; + if (Surface.Undo != null) + { + var action = new ConnectBoxesAction((InputBox)this, (OutputBox)connectedBox, false); + BreakConnection(connectedBox); + action.End(); + Surface.Undo.AddAction(action); + } + else + { + BreakConnection(connectedBox); + } + Surface.ConnectingStart(connectedBox); + } + else + { + Surface.ConnectingStart(this); + } + } } base.OnMouseLeave(); } diff --git a/Source/Editor/Surface/Elements/SkeletonNodeNameSelectElement.cs b/Source/Editor/Surface/Elements/SkeletonNodeNameSelectElement.cs index 0d8d676c7..4321eabd0 100644 --- a/Source/Editor/Surface/Elements/SkeletonNodeNameSelectElement.cs +++ b/Source/Editor/Surface/Elements/SkeletonNodeNameSelectElement.cs @@ -32,8 +32,8 @@ namespace FlaxEditor.Surface.Elements } set { - if (!string.IsNullOrEmpty(value)) - SelectedIndex = _nodeNameToIndex[value]; + if (!string.IsNullOrEmpty(value) && _nodeNameToIndex.TryGetValue(value, out var index)) + SelectedIndex = index; else SelectedIndex = -1; } @@ -60,8 +60,8 @@ namespace FlaxEditor.Surface.Elements { _selectedIndices.Clear(); var selectedNode = (string)ParentNode.Values[Archetype.ValueIndex]; - if (!string.IsNullOrEmpty(selectedNode)) - _selectedIndices.Add(_nodeNameToIndex[selectedNode]); + if (!string.IsNullOrEmpty(selectedNode) && _nodeNameToIndex.TryGetValue(selectedNode, out var index)) + _selectedIndices.Add(index); OnSelectedIndexChanged(); } @@ -92,7 +92,7 @@ namespace FlaxEditor.Surface.Elements { sb.Clear(); var node = nodes[nodeIndex]; - _nodeNameToIndex.Add(node.Name, nodeIndex); + _nodeNameToIndex[node.Name] = nodeIndex; int parent = node.ParentIndex; while (parent != -1) { diff --git a/Source/Editor/Surface/Elements/UnsignedIntegerValue.cs b/Source/Editor/Surface/Elements/UnsignedIntegerValue.cs index 9668105f1..a1822a268 100644 --- a/Source/Editor/Surface/Elements/UnsignedIntegerValue.cs +++ b/Source/Editor/Surface/Elements/UnsignedIntegerValue.cs @@ -85,7 +85,7 @@ namespace FlaxEditor.Surface.Elements else if (value is Vector4 valueVec4) result = (uint)(arch.BoxID == 0 ? valueVec4.X : arch.BoxID == 1 ? valueVec4.Y : arch.BoxID == 2 ? valueVec4.Z : valueVec4.W); else - result = 0; + result = 0u; return result; } diff --git a/Source/Editor/Surface/NodeElementArchetype.cs b/Source/Editor/Surface/NodeElementArchetype.cs index 07ea35875..cc2f41106 100644 --- a/Source/Editor/Surface/NodeElementArchetype.cs +++ b/Source/Editor/Surface/NodeElementArchetype.cs @@ -239,6 +239,32 @@ namespace FlaxEditor.Surface }; } + /// + /// Creates new Unsigned Integer value element description. + /// + /// The x location (in node area space). + /// The y location (in node area space). + /// The index of the node variable linked as the input. Useful to make a physical connection between input box and default value for it. + /// The index of the component to edit. For vectors this can be set to modify only single component of it. Eg. for vec2 value component set to 1 will edit only Y component. Default value -1 will be used to edit whole value. + /// The minimum value range. + /// The maximum value range. + /// The archetype. + public static NodeElementArchetype UnsignedInteger(float x, float y, int valueIndex = -1, int component = -1, uint valueMin = 0, uint valueMax = 1000000) + { + return new NodeElementArchetype + { + Type = NodeElementType.UnsignedIntegerValue, + Position = new Vector2(Constants.NodeMarginX + x, Constants.NodeMarginY + Constants.NodeHeaderSize + y), + Text = null, + Single = false, + ValueIndex = valueIndex, + ValueMin = valueMin, + ValueMax = valueMax, + BoxID = -1, + ConnectionsType = ScriptType.Null + }; + } + /// /// Creates new Float value element description. /// diff --git a/Source/Editor/Surface/NodeElementType.cs b/Source/Editor/Surface/NodeElementType.cs index 48b2356dd..f104b234c 100644 --- a/Source/Editor/Surface/NodeElementType.cs +++ b/Source/Editor/Surface/NodeElementType.cs @@ -94,5 +94,10 @@ namespace FlaxEditor.Surface /// The actor picker. /// Actor = 19, + + /// + /// The unsigned integer value. + /// + UnsignedIntegerValue = 20, } } diff --git a/Source/Editor/Surface/SurfaceNode.cs b/Source/Editor/Surface/SurfaceNode.cs index cf39384ce..abbd6a6a0 100644 --- a/Source/Editor/Surface/SurfaceNode.cs +++ b/Source/Editor/Surface/SurfaceNode.cs @@ -185,9 +185,9 @@ namespace FlaxEditor.Surface var rightWidth = 40.0f; var boxLabelFont = Style.Current.FontSmall; var titleLabelFont = Style.Current.FontLarge; - for (int i = 0; i < Elements.Count; i++) + for (int i = 0; i < Children.Count; i++) { - if (Elements[i] is InputBox inputBox) + if (Children[i] is InputBox inputBox) { var boxWidth = boxLabelFont.MeasureText(inputBox.Text).X + 20; if (inputBox.DefaultValueEditor != null) @@ -195,15 +195,23 @@ namespace FlaxEditor.Surface leftWidth = Mathf.Max(leftWidth, boxWidth); leftHeight = Mathf.Max(leftHeight, inputBox.Archetype.Position.Y - Constants.NodeMarginY - Constants.NodeHeaderSize + 20.0f); } - else if (Elements[i] is OutputBox outputBox) + else if (Children[i] is OutputBox outputBox) { rightWidth = Mathf.Max(rightWidth, boxLabelFont.MeasureText(outputBox.Text).X + 20); rightHeight = Mathf.Max(rightHeight, outputBox.Archetype.Position.Y - Constants.NodeMarginY - Constants.NodeHeaderSize + 20.0f); } - else if (Elements[i] is Control control) + else if (Children[i] is Control control) { - width = Mathf.Max(width, control.Width + 10); - height = Mathf.Max(height, control.Height + 10); + if (control.AnchorPreset == AnchorPresets.TopLeft) + { + width = Mathf.Max(width, control.Right + 4 - Constants.NodeMarginX); + height = Mathf.Max(height, control.Bottom + 4 - Constants.NodeMarginY - Constants.NodeHeaderSize); + } + else + { + width = Mathf.Max(width, control.Width + 4); + height = Mathf.Max(height, control.Height + 4); + } } } width = Mathf.Max(width, leftWidth + rightWidth + 10); @@ -267,6 +275,9 @@ namespace FlaxEditor.Surface case NodeElementType.Actor: element = new ActorSelect(this, arch); break; + case NodeElementType.UnsignedIntegerValue: + element = new UnsignedIntegerValue(this, arch); + break; //default: throw new NotImplementedException("Unknown node element type: " + arch.Type); } if (element != null) @@ -303,12 +314,16 @@ namespace FlaxEditor.Surface { if (type == ScriptType.Null) type = new ScriptType(typeof(object)); + + // Try to reuse box var box = GetBox(id); if ((isOut && box is InputBox) || (!isOut && box is OutputBox)) { box.Dispose(); box = null; } + + // Create new if missing if (box == null) { if (isOut) @@ -319,10 +334,15 @@ namespace FlaxEditor.Surface } else { + // Sync properties for exiting box box.Text = text; box.CurrentType = type; box.Y = Constants.NodeMarginY + Constants.NodeHeaderSize + yLevel * Constants.LayoutOffsetY; } + + // Update box + box.OnConnectionsChanged(); + return box; } diff --git a/Source/Editor/Surface/VisjectSurface.ContextMenu.cs b/Source/Editor/Surface/VisjectSurface.ContextMenu.cs index 6b38ffdfc..c2b0d1887 100644 --- a/Source/Editor/Surface/VisjectSurface.ContextMenu.cs +++ b/Source/Editor/Surface/VisjectSurface.ContextMenu.cs @@ -15,6 +15,7 @@ namespace FlaxEditor.Surface { private ContextMenuButton _cmCopyButton; private ContextMenuButton _cmDuplicateButton; + private ContextMenuButton _cmFormatNodesConnectionButton; private ContextMenuButton _cmRemoveNodeConnectionsButton; private ContextMenuButton _cmRemoveBoxConnectionsButton; private readonly Vector2 ContextMenuOffset = new Vector2(5); @@ -216,6 +217,13 @@ namespace FlaxEditor.Surface } }).Enabled = Nodes.Any(x => x.Breakpoint.Set && x.Breakpoint.Enabled); } + menu.AddSeparator(); + + _cmFormatNodesConnectionButton = menu.AddButton("Format node(s)", () => + { + FormatGraph(SelectedNodes); + }); + _cmFormatNodesConnectionButton.Enabled = HasNodesSelection; menu.AddSeparator(); _cmRemoveNodeConnectionsButton = menu.AddButton("Remove all connections to that node(s)", () => diff --git a/Source/Editor/Surface/VisjectSurface.DragDrop.cs b/Source/Editor/Surface/VisjectSurface.DragDrop.cs index 323ef8510..422198f5b 100644 --- a/Source/Editor/Surface/VisjectSurface.DragDrop.cs +++ b/Source/Editor/Surface/VisjectSurface.DragDrop.cs @@ -107,7 +107,7 @@ namespace FlaxEditor.Surface /// Validates the parameter drag operation. /// /// Name of the parameter. - /// Tre if can drag that parameter, otherwise false. + /// True if can drag that parameter, otherwise false. protected virtual bool ValidateDragParameter(string parameterName) { return GetParameter(parameterName) != null; diff --git a/Source/Editor/Surface/VisjectSurface.Formatting.cs b/Source/Editor/Surface/VisjectSurface.Formatting.cs new file mode 100644 index 000000000..848f79157 --- /dev/null +++ b/Source/Editor/Surface/VisjectSurface.Formatting.cs @@ -0,0 +1,287 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using FlaxEngine; +using FlaxEditor.Surface.Elements; +using FlaxEditor.Surface.Undo; + +namespace FlaxEditor.Surface +{ + public partial class VisjectSurface + { + // Reference https://github.com/stefnotch/xnode-graph-formatter/blob/812e08e71c7b9b7eb0810dbdfb0a9a1034da6941/Assets/Examples/MathGraph/Editor/MathGraphEditor.cs + + private class NodeFormattingData + { + /// + /// Starting from 0 at the main nodes + /// + public int Layer; + + /// + /// Position in the layer + /// + public int Offset; + + /// + /// How far the subtree needs to be moved additionally + /// + public int SubtreeOffset; + } + + /// + /// Formats a graph where the nodes can be disjointed. + /// Uses the Sugiyama method + /// + /// List of nodes + protected void FormatGraph(List nodes) + { + if (nodes.Count <= 1 || !CanEdit) return; + + var nodesToVisit = new HashSet(nodes); + + // While we haven't formatted every node + while (nodesToVisit.Count > 0) + { + // Run a search in both directions + var connectedNodes = new List(); + var queue = new Queue(); + + var startNode = nodesToVisit.First(); + nodesToVisit.Remove(startNode); + queue.Enqueue(startNode); + + while (queue.Count > 0) + { + var node = queue.Dequeue(); + connectedNodes.Add(node); + + for (int i = 0; i < node.Elements.Count; i++) + { + if (node.Elements[i] is Box box) + { + for (int j = 0; j < box.Connections.Count; j++) + { + if (nodesToVisit.Contains(box.Connections[j].ParentNode)) + { + nodesToVisit.Remove(box.Connections[j].ParentNode); + queue.Enqueue(box.Connections[j].ParentNode); + } + } + + } + } + } + + FormatConnectedGraph(connectedNodes); + } + } + + /// + /// Formats a graph where all nodes are connected + /// + /// List of connected nodes + protected void FormatConnectedGraph(List nodes) + { + if (nodes.Count <= 1 || !CanEdit) return; + + var boundingBox = GetNodesBounds(nodes); + + var nodeData = nodes.ToDictionary(n => n, n => new NodeFormattingData { }); + + // Rightmost nodes with none of our nodes to the right of them + var endNodes = nodes + .Where(n => !n.GetBoxes().Any(b => b.IsOutput && b.Connections.Any(c => nodeData.ContainsKey(c.ParentNode)))) + .OrderBy(n => n.Top) // Keep their relative order + .ToList(); + + // Longest path layering + int maxLayer = SetLayers(nodeData, endNodes); + + // Set the vertical offsets + int maxOffset = SetOffsets(nodeData, endNodes, maxLayer); + + // Layout the nodes + + // Get the largest nodes in the Y and X direction + float[] widths = new float[maxLayer + 1]; + float[] heights = new float[maxOffset + 1]; + for (int i = 0; i < nodes.Count; i++) + { + if (nodeData.TryGetValue(nodes[i], out var data)) + { + if (nodes[i].Width > widths[data.Layer]) + { + widths[data.Layer] = nodes[i].Width; + } + if (nodes[i].Height > heights[data.Offset]) + { + heights[data.Offset] = nodes[i].Height; + } + } + } + + Vector2 minDistanceBetweenNodes = new Vector2(30, 30); + + // Figure out the node positions (aligned to a grid) + float[] nodeXPositions = new float[widths.Length]; + for (int i = 1; i < widths.Length; i++) + { + // Go from right to left (backwards) through the nodes + nodeXPositions[i] = nodeXPositions[i - 1] + minDistanceBetweenNodes.X + widths[i]; + } + + float[] nodeYPositions = new float[heights.Length]; + for (int i = 1; i < heights.Length; i++) + { + // Go from top to bottom through the nodes + nodeYPositions[i] = nodeYPositions[i - 1] + heights[i - 1] + minDistanceBetweenNodes.Y; + } + + // Set the node positions + var undoActions = new List(); + var topRightPosition = endNodes[0].Location; + for (int i = 0; i < nodes.Count; i++) + { + if (nodeData.TryGetValue(nodes[i], out var data)) + { + Vector2 newLocation = new Vector2(-nodeXPositions[data.Layer], nodeYPositions[data.Offset]) + topRightPosition; + Vector2 locationDelta = newLocation - nodes[i].Location; + nodes[i].Location = newLocation; + + if (Undo != null) + undoActions.Add(new MoveNodesAction(Context, new[] { nodes[i].ID }, locationDelta)); + } + } + + MarkAsEdited(false); + Undo?.AddAction(new MultiUndoAction(undoActions, "Format nodes")); + } + + /// + /// Assigns a layer to every node + /// + /// The exta node data + /// The end nodes + /// The number of the maximum layer + private int SetLayers(Dictionary nodeData, List endNodes) + { + // Longest path layering + int maxLayer = 0; + var stack = new Stack(endNodes); + + while (stack.Count > 0) + { + var node = stack.Pop(); + int layer = nodeData[node].Layer; + + for (int i = 0; i < node.Elements.Count; i++) + { + if (node.Elements[i] is InputBox box && box.HasAnyConnection) + { + var childNode = box.Connections[0].ParentNode; + + if (nodeData.TryGetValue(childNode, out var data)) + { + int nodeLayer = Math.Max(data.Layer, layer + 1); + data.Layer = nodeLayer; + if (nodeLayer > maxLayer) + { + maxLayer = nodeLayer; + } + + stack.Push(childNode); + } + } + } + } + return maxLayer; + } + + + /// + /// Sets the node offsets + /// + /// The exta node data + /// The end nodes + /// The number of the maximum layer + /// The number of the maximum offset + private int SetOffsets(Dictionary nodeData, List endNodes, int maxLayer) + { + int maxOffset = 0; + + // Keeps track of the largest offset (Y axis) for every layer + int[] offsets = new int[maxLayer + 1]; + + var visitedNodes = new HashSet(); + + void SetOffsets(SurfaceNode node, NodeFormattingData straightParentData) + { + if (!nodeData.TryGetValue(node, out var data)) return; + + // If we realize that the current node would collide with an already existing node in this layer + if (data.Layer >= 0 && offsets[data.Layer] > data.Offset) + { + // Move the entire sub-tree down + straightParentData.SubtreeOffset = Math.Max(straightParentData.SubtreeOffset, offsets[data.Layer] - data.Offset); + } + + // Keeps track of the offset of the last direct child we visited + int childOffset = data.Offset; + bool straightChild = true; + + // Run the algorithm for every child + for (int i = 0; i < node.Elements.Count; i++) + { + if (node.Elements[i] is InputBox box && box.HasAnyConnection) + { + var childNode = box.Connections[0].ParentNode; + if (!visitedNodes.Contains(childNode) && nodeData.TryGetValue(childNode, out var childData)) + { + visitedNodes.Add(childNode); + childData.Offset = childOffset; + SetOffsets(childNode, straightChild ? straightParentData : childData); + childOffset = childData.Offset + 1; + straightChild = false; + } + } + } + + if (data.Layer >= 0) + { + // When coming out of the recursion, apply the extra subtree offsets + data.Offset += straightParentData.SubtreeOffset; + if (data.Offset > maxOffset) + { + maxOffset = data.Offset; + } + offsets[data.Layer] = data.Offset + 1; + } + } + + { + // An imaginary final node + var endNodeData = new NodeFormattingData { Layer = -1 }; + int childOffset = 0; + bool straightChild = true; + + for (int i = 0; i < endNodes.Count; i++) + { + if (nodeData.TryGetValue(endNodes[i], out var childData)) + { + visitedNodes.Add(endNodes[i]); + childData.Offset = childOffset; + SetOffsets(endNodes[i], straightChild ? endNodeData : childData); + childOffset = childData.Offset + 1; + straightChild = false; + } + } + } + + return maxOffset; + } + + } +} diff --git a/Source/Editor/Surface/VisjectSurface.Input.cs b/Source/Editor/Surface/VisjectSurface.Input.cs index 68966370c..dec89e143 100644 --- a/Source/Editor/Surface/VisjectSurface.Input.cs +++ b/Source/Editor/Surface/VisjectSurface.Input.cs @@ -24,7 +24,9 @@ namespace FlaxEditor.Surface private class InputBracket { + private readonly float DefaultWidth = 120f; private readonly Margin _padding = new Margin(10f); + public Box Box { get; } public Vector2 EndBracketPosition { get; } public List Nodes { get; } = new List(); @@ -33,7 +35,7 @@ namespace FlaxEditor.Surface public InputBracket(Box box, Vector2 nodePosition) { Box = box; - EndBracketPosition = nodePosition; + EndBracketPosition = nodePosition + new Vector2(DefaultWidth, 0); Update(); } @@ -47,11 +49,10 @@ namespace FlaxEditor.Surface } else { - area = new Rectangle(EndBracketPosition, new Vector2(120f, 80f)); + area = new Rectangle(EndBracketPosition, new Vector2(DefaultWidth, 80f)); } _padding.ExpandRectangle(ref area); - Vector2 endPoint = area.Location + new Vector2(area.Width, area.Height / 2f); - Vector2 offset = EndBracketPosition - endPoint; + Vector2 offset = EndBracketPosition - area.UpperRight; area.Location += offset; Area = area; if (!offset.IsZero) @@ -99,18 +100,6 @@ namespace FlaxEditor.Surface /// public event Window.MouseWheelDelegate CustomMouseWheel; - /// - /// Gets the node under the mouse location. - /// - /// The node or null if no intersection. - public SurfaceNode GetNodeUnderMouse() - { - var pos = _rootControl.PointFromParent(ref _mousePos); - if (_rootControl.GetChildAt(pos) is SurfaceNode node) - return node; - return null; - } - /// /// Gets the control under the mouse location. /// @@ -118,9 +107,7 @@ namespace FlaxEditor.Surface public SurfaceControl GetControlUnderMouse() { var pos = _rootControl.PointFromParent(ref _mousePos); - if (_rootControl.GetChildAtRecursive(pos) is SurfaceControl control) - return control; - return null; + return _rootControl.GetChildAt(pos) as SurfaceControl; } private void UpdateSelectionRectangle() @@ -464,15 +451,7 @@ namespace FlaxEditor.Surface { // Check if any control is under the mouse _cmStartPos = location; - if (controlUnderMouse != null) - { - if (!HasNodesSelection) - Select(controlUnderMouse); - - // Show secondary context menu - ShowSecondaryCM(_cmStartPos, controlUnderMouse); - } - else + if (controlUnderMouse == null) { // Show primary context menu ShowPrimaryMenu(_cmStartPos); @@ -708,31 +687,38 @@ namespace FlaxEditor.Surface private Vector2 FindEmptySpace(Box box) { - int boxIndex = 0; + Vector2 distanceBetweenNodes = new Vector2(30, 30); var node = box.ParentNode; + + // Same height as node + float yLocation = node.Top; + for (int i = 0; i < node.Elements.Count; i++) { - // Box on the same side above the current box if (node.Elements[i] is Box nodeBox && nodeBox.IsOutput == box.IsOutput && nodeBox.Y < box.Y) { - boxIndex++; + // Below connected node + yLocation = Mathf.Max(yLocation, nodeBox.ParentNode.Bottom + distanceBetweenNodes.Y); } } + // TODO: Dodge the other nodes - Vector2 distanceBetweenNodes = new Vector2(40, 20); - const float NodeHeight = 120; + float xLocation = node.Location.X; + if (box.IsOutput) + { + xLocation += node.Width + distanceBetweenNodes.X; + } + else + { + xLocation += -120 - distanceBetweenNodes.X; + } - float direction = box.IsOutput ? 1 : -1; - - Vector2 newNodeLocation = node.Location + - new Vector2( - (node.Width + distanceBetweenNodes.X) * direction, - boxIndex * (NodeHeight + distanceBetweenNodes.Y) - ); - - return newNodeLocation; + return new Vector2( + xLocation, + yLocation + ); } } } diff --git a/Source/Editor/Surface/VisualScriptSurface.cs b/Source/Editor/Surface/VisualScriptSurface.cs index ccabc8f2f..5a5a2e250 100644 --- a/Source/Editor/Surface/VisualScriptSurface.cs +++ b/Source/Editor/Surface/VisualScriptSurface.cs @@ -71,7 +71,7 @@ namespace FlaxEditor.Surface // Check if has cached groups if (_cache.Count != 0) { - // Check if context menu doesn;t have the recent cached groups + // Check if context menu doesn't have the recent cached groups if (!contextMenu.Groups.Any(g => g.Archetype.Tag is int asInt && asInt == _version)) { var groups = contextMenu.Groups.Where(g => g.Archetype.Tag is int).ToArray(); diff --git a/Source/Editor/Tools/Foliage/FoliageTab.cs b/Source/Editor/Tools/Foliage/FoliageTab.cs index 9dbb17e88..fe4120035 100644 --- a/Source/Editor/Tools/Foliage/FoliageTab.cs +++ b/Source/Editor/Tools/Foliage/FoliageTab.cs @@ -21,6 +21,7 @@ namespace FlaxEditor.Tools.Foliage private readonly Tabs _modes; private readonly ContainerControl _noFoliagePanel; private int _selectedFoliageTypeIndex = -1; + private Button _createNewFoliage; /// /// The editor instance. @@ -99,6 +100,7 @@ namespace FlaxEditor.Tools.Foliage public FoliageTab(SpriteHandle icon, Editor editor) : base(string.Empty, icon) { + Level.SceneLoaded += OnSceneLoaded; Editor = editor; Editor.SceneEditing.SelectionChanged += OnSelectionChanged; @@ -135,14 +137,31 @@ namespace FlaxEditor.Tools.Foliage Offsets = Margin.Zero, Parent = _noFoliagePanel }; - var noFoliageButton = new Button + _createNewFoliage = new Button { Text = "Create new foliage", AnchorPreset = AnchorPresets.MiddleCenter, Offsets = new Margin(-60, 120, -12, 24), Parent = _noFoliagePanel, + Enabled = false }; - noFoliageButton.Clicked += OnCreateNewFoliageClicked; + _createNewFoliage.Clicked += OnCreateNewFoliageClicked; + } + + private void OnSceneLoaded(Scene arg1, Guid arg2) + { + _createNewFoliage.Enabled = true; + + Level.SceneUnloaded += OnSceneUnloaded; + Level.SceneLoaded -= OnSceneLoaded; + } + + private void OnSceneUnloaded(Scene arg1, Guid arg2) + { + _createNewFoliage.Enabled = false; + + Level.SceneLoaded += OnSceneLoaded; + Level.SceneUnloaded -= OnSceneUnloaded; } private void OnSelected(Tab tab) @@ -248,5 +267,16 @@ namespace FlaxEditor.Tools.Foliage { SelectedFoliageTypesChanged?.Invoke(); } + + /// + public override void OnDestroy() + { + if (_createNewFoliage.Enabled) + Level.SceneUnloaded -= OnSceneUnloaded; + else + Level.SceneLoaded -= OnSceneLoaded; + + base.OnDestroy(); + } } } diff --git a/Source/Editor/Tools/Foliage/FoliageTools.cpp b/Source/Editor/Tools/Foliage/FoliageTools.cpp index a0cbf8e14..3354be0b3 100644 --- a/Source/Editor/Tools/Foliage/FoliageTools.cpp +++ b/Source/Editor/Tools/Foliage/FoliageTools.cpp @@ -67,7 +67,7 @@ struct GeometryLookup static bool Search(Actor* actor, GeometryLookup& lookup) { // Early out if object is not intersecting with the foliage brush bounds - if (!actor->GetBox().Intersects(lookup.Brush)) + if (!actor->GetIsActive() || !actor->GetBox().Intersects(lookup.Brush)) return true; const auto brush = lookup.Brush; diff --git a/Source/Editor/Tools/Terrain/CarveTab.cs b/Source/Editor/Tools/Terrain/CarveTab.cs index a3a65c52a..e7b880414 100644 --- a/Source/Editor/Tools/Terrain/CarveTab.cs +++ b/Source/Editor/Tools/Terrain/CarveTab.cs @@ -18,6 +18,7 @@ namespace FlaxEditor.Tools.Terrain { private readonly Tabs _modes; private readonly ContainerControl _noTerrainPanel; + private readonly Button _createTerrainButton; /// /// The editor instance. @@ -57,6 +58,7 @@ namespace FlaxEditor.Tools.Terrain public CarveTab(SpriteHandle icon, Editor editor) : base(string.Empty, icon) { + Level.SceneLoaded += OnSceneLoaded; Editor = editor; Editor.SceneEditing.SelectionChanged += OnSelectionChanged; @@ -93,14 +95,31 @@ namespace FlaxEditor.Tools.Terrain Offsets = Margin.Zero, Parent = _noTerrainPanel }; - var noTerrainButton = new Button + _createTerrainButton = new Button { Text = "Create new terrain", AnchorPreset = AnchorPresets.MiddleCenter, Offsets = new Margin(-60, 120, -12, 24), - Parent = _noTerrainPanel + Parent = _noTerrainPanel, + Enabled = false }; - noTerrainButton.Clicked += OnCreateNewTerrainClicked; + _createTerrainButton.Clicked += OnCreateNewTerrainClicked; + } + + private void OnSceneLoaded(Scene arg1, Guid arg2) + { + _createTerrainButton.Enabled = true; + + Level.SceneUnloaded += OnSceneUnloaded; + Level.SceneLoaded -= OnSceneLoaded; + } + + private void OnSceneUnloaded(Scene arg1, Guid arg2) + { + _createTerrainButton.Enabled = false; + + Level.SceneLoaded += OnSceneLoaded; + Level.SceneUnloaded -= OnSceneUnloaded; } private void OnSelected(Tab tab) @@ -183,5 +202,16 @@ namespace FlaxEditor.Tools.Terrain default: throw new IndexOutOfRangeException("Invalid carve tab mode."); } } + + /// + public override void OnDestroy() + { + if (_createTerrainButton.Enabled) + Level.SceneUnloaded -= OnSceneUnloaded; + else + Level.SceneLoaded -= OnSceneLoaded; + + base.OnDestroy(); + } } } diff --git a/Source/Editor/Tools/Terrain/Paint/Mode.cs b/Source/Editor/Tools/Terrain/Paint/Mode.cs index 1a1373d05..e53071aa3 100644 --- a/Source/Editor/Tools/Terrain/Paint/Mode.cs +++ b/Source/Editor/Tools/Terrain/Paint/Mode.cs @@ -33,7 +33,7 @@ namespace FlaxEditor.Tools.Terrain.Paint } /// - /// The tool strength (normalized to range 0-1). Defines the intensity of the paint operation to make it stronger or mre subtle. + /// The tool strength (normalized to range 0-1). Defines the intensity of the paint operation to make it stronger or more subtle. /// [EditorOrder(0), Limit(0, 10, 0.01f), Tooltip("The tool strength (normalized to range 0-1). Defines the intensity of the paint operation to make it stronger or more subtle.")] public float Strength = 1.0f; diff --git a/Source/Editor/Tools/Terrain/Sculpt/Mode.cs b/Source/Editor/Tools/Terrain/Sculpt/Mode.cs index 14fbe0175..eb32b3934 100644 --- a/Source/Editor/Tools/Terrain/Sculpt/Mode.cs +++ b/Source/Editor/Tools/Terrain/Sculpt/Mode.cs @@ -33,7 +33,7 @@ namespace FlaxEditor.Tools.Terrain.Sculpt } /// - /// The tool strength (normalized to range 0-1). Defines the intensity of the sculpt operation to make it stronger or mre subtle. + /// The tool strength (normalized to range 0-1). Defines the intensity of the sculpt operation to make it stronger or more subtle. /// [EditorOrder(0), Limit(0, 6, 0.01f), Tooltip("The tool strength (normalized to range 0-1). Defines the intensity of the sculpt operation to make it stronger or more subtle.")] public float Strength = 1.2f; diff --git a/Source/Editor/Tools/Terrain/TerrainTools.cpp b/Source/Editor/Tools/Terrain/TerrainTools.cpp index 6bf4f8fd4..e89b5e3e9 100644 --- a/Source/Editor/Tools/Terrain/TerrainTools.cpp +++ b/Source/Editor/Tools/Terrain/TerrainTools.cpp @@ -2,7 +2,7 @@ #include "TerrainTools.h" #include "Engine/Core/Cache.h" -#include "Engine/Core/Math/VectorInt.h" +#include "Engine/Core/Math/Int2.h" #include "Engine/Core/Math/Color32.h" #include "Engine/Core/Collections/CollectionPoolCache.h" #include "Engine/Terrain/TerrainPatch.h" diff --git a/Source/Editor/Tools/VertexPainting.cs b/Source/Editor/Tools/VertexPainting.cs index c130370e7..29d9daedc 100644 --- a/Source/Editor/Tools/VertexPainting.cs +++ b/Source/Editor/Tools/VertexPainting.cs @@ -49,7 +49,7 @@ namespace FlaxEditor.Tools set => Tab._gizmoMode.BrushStrength = value; } - [EditorOrder(20), EditorDisplay("Brush"), Limit(0.0f, 1.0f, 0.01f), Tooltip("The falloff parameter fo the brush. Adjusts the paint strength for the vertices that are far from the brush center. Use lower values to make painting smoother and softer.")] + [EditorOrder(20), EditorDisplay("Brush"), Limit(0.0f, 1.0f, 0.01f), Tooltip("The falloff parameter for the brush. Adjusts the paint strength for the vertices that are far from the brush center. Use lower values to make painting smoother and softer.")] public float BrushFalloff { get => Tab._gizmoMode.BrushFalloff; diff --git a/Source/Editor/Utilities/EditorUtilities.cpp b/Source/Editor/Utilities/EditorUtilities.cpp index 1571c39d1..538b78173 100644 --- a/Source/Editor/Utilities/EditorUtilities.cpp +++ b/Source/Editor/Utilities/EditorUtilities.cpp @@ -6,10 +6,8 @@ #include "Engine/Core/Log.h" #include "Engine/Graphics/Textures/TextureData.h" #include "Engine/Graphics/PixelFormatExtensions.h" -#include "Engine/Serialization/FileReadStream.h" #include "Engine/Tools/TextureTool/TextureTool.h" #include "Engine/Core/Math/Color32.h" -#include "Engine/Core/Math/VectorInt.h" #include "Engine/Core/Config/GameSettings.h" #include "Engine/Content/Content.h" #include "Engine/Content/AssetReference.h" @@ -247,7 +245,7 @@ void UpdateIconData(uint8* iconData, const TextureData* icon) iconTexSize = Math::RoundUpToPowerOf2(width); } - // Try to pick a proper mip (requrie the same size) + // Try to pick a proper mip (require the same size) const TextureMipData* srcPixels = nullptr; int32 mipLevels = icon->GetMipLevels(); for (int32 mipIndex = 0; mipIndex < mipLevels; mipIndex++) @@ -743,6 +741,59 @@ bool EditorUtilities::GenerateCertificate(const String& name, const String& outp return false; } +bool EditorUtilities::IsInvalidPathChar(Char c) +{ + char illegalChars[] = + { + '?', + '\\', + '/', + '\"', + '<', + '>', + '|', + ':', + '*', + '\u0001', + '\u0002', + '\u0003', + '\u0004', + '\u0005', + '\u0006', + '\a', + '\b', + '\t', + '\n', + '\v', + '\f', + '\r', + '\u000E', + '\u000F', + '\u0010', + '\u0011', + '\u0012', + '\u0013', + '\u0014', + '\u0015', + '\u0016', + '\u0017', + '\u0018', + '\u0019', + '\u001A', + '\u001B', + '\u001C', + '\u001D', + '\u001E', + '\u001F' + }; + for (auto i : illegalChars) + { + if (c == i) + return true; + } + return false; +} + bool EditorUtilities::ReplaceInFiles(const String& folderPath, const Char* searchPattern, DirectorySearchOption searchOption, const String& findWhat, const String& replaceWith) { Array files; diff --git a/Source/Editor/Utilities/EditorUtilities.h b/Source/Editor/Utilities/EditorUtilities.h index 236d10e6f..a5f129c1b 100644 --- a/Source/Editor/Utilities/EditorUtilities.h +++ b/Source/Editor/Utilities/EditorUtilities.h @@ -42,6 +42,13 @@ public: public: + /// + /// Determines whether the specified path character is invalid. + /// + /// The path character. + /// true if the given character cannot be used as a path because it is illegal character; otherwise, false. + static bool IsInvalidPathChar(Char c); + /// /// Replaces the given text with other one in the files. /// diff --git a/Source/Editor/Utilities/ObjectSnapshot.cs b/Source/Editor/Utilities/ObjectSnapshot.cs index a64ddf306..8f29d1145 100644 --- a/Source/Editor/Utilities/ObjectSnapshot.cs +++ b/Source/Editor/Utilities/ObjectSnapshot.cs @@ -251,7 +251,7 @@ namespace FlaxEditor.Utilities var list = new List(); #if DEBUG_OBJECT_SNAPSHOT_COMPARISION - Debug.Logger.LogHandler.LogWrite(LogType.Warning, "-------------- Comparision --------------"); + Debug.Logger.LogHandler.LogWrite(LogType.Warning, "-------------- Comparison --------------"); #endif for (int i = _members.Count - 1; i >= 0; i--) { diff --git a/Source/Editor/Utilities/ShuntingYardParser.cs b/Source/Editor/Utilities/ShuntingYardParser.cs index dfd83b1f8..a07026d1c 100644 --- a/Source/Editor/Utilities/ShuntingYardParser.cs +++ b/Source/Editor/Utilities/ShuntingYardParser.cs @@ -126,7 +126,7 @@ namespace FlaxEditor.Utilities /// /// The first operator. /// The second operator. - /// The comparision result. + /// The comparison result. private static bool CompareOperators(string oper1, string oper2) { var op1 = Operators[oper1]; @@ -193,6 +193,7 @@ namespace FlaxEditor.Utilities { case 'x': case 'X': + { // Hexadecimal value i++; token.Clear(); @@ -200,22 +201,35 @@ namespace FlaxEditor.Utilities throw new ParsingException("invalid hexadecimal number"); while (i + 1 < text.Length && StringUtils.IsHexDigit(text[i + 1])) { - i++; - token.Append(text[i]); + token.Append(text[++i]); } var value = ulong.Parse(token.ToString(), NumberStyles.HexNumber); token.Clear(); token.Append(value.ToString()); break; + } default: + { // Decimal value while (i + 1 < text.Length && DetermineType(text[i + 1]) == TokenType.Number) { - i++; - token.Append(text[i]); + token.Append(text[++i]); + } + + // Exponential notation + if (i + 2 < text.Length && (text[i + 1] == 'e' || text[i + 1] == 'E')) + { + token.Append(text[++i]); + if (text[i + 1] == '-' || text[i + 1] == '+') + token.Append(text[++i]); + while (i + 1 < text.Length && DetermineType(text[i + 1]) == TokenType.Number) + { + token.Append(text[++i]); + } } break; } + } } // Discard solo '-' diff --git a/Source/Editor/Utilities/Utils.cs b/Source/Editor/Utilities/Utils.cs index 5f1636239..26c5ec92b 100644 --- a/Source/Editor/Utilities/Utils.cs +++ b/Source/Editor/Utilities/Utils.cs @@ -532,6 +532,8 @@ namespace FlaxEditor.Utilities break; case VariantType.Enum: case VariantType.Structure: + case VariantType.ManagedObject: + case VariantType.Typename: stream.Write(int.MaxValue); stream.WriteStrAnsi(type.FullName, 77); break; @@ -742,6 +744,7 @@ namespace FlaxEditor.Utilities case VariantType.Array: return new ScriptType(typeof(object[])); case VariantType.Dictionary: return new ScriptType(typeof(Dictionary)); case VariantType.ManagedObject: return new ScriptType(typeof(object)); + case VariantType.Blob: return new ScriptType(typeof(byte[])); default: throw new ArgumentOutOfRangeException($"Unknown Variant Type {variantType} without typename."); } } @@ -760,7 +763,7 @@ namespace FlaxEditor.Utilities data[i] = (byte)(c ^ 77); } var typeName = System.Text.Encoding.ASCII.GetString(data); - return TypeUtils.GetType(typeName).Type; + return TypeUtils.GetManagedType(typeName); } if (typeNameLength > 0) { @@ -772,7 +775,7 @@ namespace FlaxEditor.Utilities data[i] = (char)(c ^ 77); } var typeName = new string(data); - return TypeUtils.GetType(typeName).Type; + return TypeUtils.GetManagedType(typeName); } switch (variantType) { @@ -805,6 +808,7 @@ namespace FlaxEditor.Utilities case VariantType.Array: return typeof(object[]); case VariantType.Dictionary: return typeof(Dictionary); case VariantType.ManagedObject: return typeof(object); + case VariantType.Blob: return typeof(byte[]); default: throw new ArgumentOutOfRangeException($"Unknown Variant Type {variantType} without typename."); } } @@ -824,7 +828,7 @@ namespace FlaxEditor.Utilities data[i] = (byte)(c ^ 77); } var typeName = System.Text.Encoding.ASCII.GetString(data); - type = TypeUtils.GetType(typeName).Type; + type = TypeUtils.GetManagedType(typeName); } else if (typeNameLength > 0) { @@ -836,7 +840,7 @@ namespace FlaxEditor.Utilities data[i] = (char)(c ^ 77); } var typeName = new string(data); - type = TypeUtils.GetType(typeName).Type; + type = TypeUtils.GetManagedType(typeName); } switch (variantType) { diff --git a/Source/Editor/Viewport/Cameras/FPSCamera.cs b/Source/Editor/Viewport/Cameras/FPSCamera.cs index c9e65d6e4..b5098696d 100644 --- a/Source/Editor/Viewport/Cameras/FPSCamera.cs +++ b/Source/Editor/Viewport/Cameras/FPSCamera.cs @@ -24,7 +24,7 @@ namespace FlaxEditor.Viewport.Cameras public bool IsAnimatingMove => _moveStartTime > Mathf.Epsilon; /// - /// The target point location. It's used to orbit around it whe user clicks Alt+LMB. + /// The target point location. It's used to orbit around it when user clicks Alt+LMB. /// public Vector3 TargetPoint = new Vector3(-200); @@ -188,8 +188,16 @@ namespace FlaxEditor.Viewport.Cameras if (input.IsPanning) { var panningSpeed = 0.8f; - position -= right * (mouseDelta.X * panningSpeed); - position -= up * (mouseDelta.Y * panningSpeed); + if (Viewport.InvertPanning) + { + position += up * (mouseDelta.Y * panningSpeed); + position += right * (mouseDelta.X * panningSpeed); + } + else + { + position -= right * (mouseDelta.X * panningSpeed); + position -= up * (mouseDelta.Y * panningSpeed); + } } // Move diff --git a/Source/Editor/Viewport/Cameras/ViewportCamera.cs b/Source/Editor/Viewport/Cameras/ViewportCamera.cs index a01000035..85b7a917c 100644 --- a/Source/Editor/Viewport/Cameras/ViewportCamera.cs +++ b/Source/Editor/Viewport/Cameras/ViewportCamera.cs @@ -32,9 +32,9 @@ namespace FlaxEditor.Viewport.Cameras /// /// The target object bounds. /// The margin distance scale of the orbit radius. - public void SerArcBallView(BoundingBox objectBounds, float marginDistanceScale = 2.0f) + public void SetArcBallView(BoundingBox objectBounds, float marginDistanceScale = 2.0f) { - SerArcBallView(BoundingSphere.FromBox(objectBounds), marginDistanceScale); + SetArcBallView(BoundingSphere.FromBox(objectBounds), marginDistanceScale); } /// @@ -42,18 +42,18 @@ namespace FlaxEditor.Viewport.Cameras /// /// The target object bounds. /// The margin distance scale of the orbit radius. - public void SerArcBallView(BoundingSphere objectBounds, float marginDistanceScale = 2.0f) + public void SetArcBallView(BoundingSphere objectBounds, float marginDistanceScale = 2.0f) { - SerArcBallView(new Quaternion(-0.08f, -0.92f, 0.31f, -0.23f), objectBounds.Center, objectBounds.Radius * marginDistanceScale); + SetArcBallView(new Quaternion(-0.08f, -0.92f, 0.31f, -0.23f), objectBounds.Center, objectBounds.Radius * marginDistanceScale); } /// /// Sets view orientation and position to match the arc ball camera style view for the given orbit radius. /// /// The orbit radius. - public void SerArcBallView(float orbitRadius) + public void SetArcBallView(float orbitRadius) { - SerArcBallView(new Quaternion(-0.08f, -0.92f, 0.31f, -0.23f), Vector3.Zero, orbitRadius); + SetArcBallView(new Quaternion(-0.08f, -0.92f, 0.31f, -0.23f), Vector3.Zero, orbitRadius); } /// @@ -62,7 +62,7 @@ namespace FlaxEditor.Viewport.Cameras /// The view rotation. /// The orbit center location. /// The orbit radius. - public void SerArcBallView(Quaternion orientation, Vector3 orbitCenter, float orbitRadius) + public void SetArcBallView(Quaternion orientation, Vector3 orbitCenter, float orbitRadius) { // Rotate Viewport.ViewOrientation = orientation; diff --git a/Source/Editor/Viewport/EditorViewport.cs b/Source/Editor/Viewport/EditorViewport.cs index 02696f743..a19589c05 100644 --- a/Source/Editor/Viewport/EditorViewport.cs +++ b/Source/Editor/Viewport/EditorViewport.cs @@ -138,9 +138,7 @@ namespace FlaxEditor.Viewport private bool _isControllingMouse; private int _deltaFilteringStep; - private Vector2 _startPosMiddle; - private Vector2 _startPosRight; - private Vector2 _startPosLeft; + private Vector2 _startPos; private Vector2 _mouseDeltaRightLast; private Vector2[] _deltaFilteringBuffer = new Vector2[FpsCameraFilteringFrames]; @@ -180,6 +178,7 @@ namespace FlaxEditor.Viewport private float _orthoSize = 1.0f; private bool _isOrtho = false; private float _wheelMovementChangeDeltaSum = 0; + private bool _invertPanning; /// /// Speed of the mouse. @@ -403,6 +402,15 @@ namespace FlaxEditor.Viewport set => _isOrtho = value; } + /// + /// Gets or sets if the panning direction is inverted. + /// + public bool InvertPanning + { + get => _invertPanning; + set => _invertPanning = value; + } + /// /// The input actions collection to processed during user input. /// @@ -434,6 +442,7 @@ namespace FlaxEditor.Viewport _nearPlane = options.Viewport.DefaultNearPlane; _farPlane = options.Viewport.DefaultFarPlane; _fieldOfView = options.Viewport.DefaultFieldOfView; + _invertPanning = options.Viewport.DefaultInvertPanning; Editor.Instance.Options.OptionsChanged += OnEditorOptionsChanged; OnEditorOptionsChanged(options); @@ -454,7 +463,7 @@ namespace FlaxEditor.Viewport var button = camSpeedCM.AddButton(v.ToString()); button.Tag = v; } - camSpeedCM.ButtonClicked += (button) => MovementSpeed = (float)button.Tag; + camSpeedCM.ButtonClicked += button => MovementSpeed = (float)button.Tag; camSpeedCM.VisibleChanged += WidgetCamSpeedShowHide; camSpeedButton.Parent = camSpeed; camSpeed.Parent = this; @@ -515,9 +524,9 @@ namespace FlaxEditor.Viewport // Orthographic { var ortho = ViewWidgetButtonMenu.AddButton("Orthographic"); - var orthoValue = new CheckBox(75, 2, _isOrtho); + var orthoValue = new CheckBox(90, 2, _isOrtho); orthoValue.Parent = ortho; - orthoValue.StateChanged += (checkBox) => + orthoValue.StateChanged += checkBox => { if (checkBox.Checked != _isOrtho) { @@ -528,13 +537,25 @@ namespace FlaxEditor.Viewport ViewWidgetButtonMenu.VisibleChanged += control => orthoValue.Checked = _isOrtho; } + // Cara Orientation + { + var cameraView = ViewWidgetButtonMenu.AddChildMenu("Orientation").ContextMenu; + for (int i = 0; i < EditorViewportCameraOrientationValues.Length; i++) + { + var co = EditorViewportCameraOrientationValues[i]; + var button = cameraView.AddButton(co.Name); + button.Tag = co.Orientation; + } + cameraView.ButtonClicked += button => ViewOrientation = Quaternion.Euler((Vector3)button.Tag); + } + // Field of View { var fov = ViewWidgetButtonMenu.AddButton("Field Of View"); - var fovValue = new FloatValueBox(1, 75, 2, 50.0f, 35.0f, 160.0f, 0.1f); + var fovValue = new FloatValueBox(1, 90, 2, 70.0f, 35.0f, 160.0f, 0.1f); fovValue.Parent = fov; fovValue.ValueChanged += () => _fieldOfView = fovValue.Value; - ViewWidgetButtonMenu.VisibleChanged += (control) => + ViewWidgetButtonMenu.VisibleChanged += control => { fov.Visible = !_isOrtho; fovValue.Value = _fieldOfView; @@ -544,10 +565,10 @@ namespace FlaxEditor.Viewport // Ortho Scale { var orthoSize = ViewWidgetButtonMenu.AddButton("Ortho Scale"); - var orthoSizeValue = new FloatValueBox(_orthoSize, 75, 2, 50.0f, 0.001f, 100000.0f, 0.01f); + var orthoSizeValue = new FloatValueBox(_orthoSize, 90, 2, 70.0f, 0.001f, 100000.0f, 0.01f); orthoSizeValue.Parent = orthoSize; orthoSizeValue.ValueChanged += () => _orthoSize = orthoSizeValue.Value; - ViewWidgetButtonMenu.VisibleChanged += (control) => + ViewWidgetButtonMenu.VisibleChanged += control => { orthoSize.Visible = _isOrtho; orthoSizeValue.Value = _orthoSize; @@ -557,7 +578,7 @@ namespace FlaxEditor.Viewport // Near Plane { var nearPlane = ViewWidgetButtonMenu.AddButton("Near Plane"); - var nearPlaneValue = new FloatValueBox(2.0f, 75, 2, 50.0f, 0.001f, 1000.0f); + var nearPlaneValue = new FloatValueBox(2.0f, 90, 2, 70.0f, 0.001f, 1000.0f); nearPlaneValue.Parent = nearPlane; nearPlaneValue.ValueChanged += () => _nearPlane = nearPlaneValue.Value; ViewWidgetButtonMenu.VisibleChanged += control => nearPlaneValue.Value = _nearPlane; @@ -566,7 +587,7 @@ namespace FlaxEditor.Viewport // Far Plane { var farPlane = ViewWidgetButtonMenu.AddButton("Far Plane"); - var farPlaneValue = new FloatValueBox(1000, 75, 2, 50.0f, 10.0f); + var farPlaneValue = new FloatValueBox(1000, 90, 2, 70.0f, 10.0f); farPlaneValue.Parent = farPlane; farPlaneValue.ValueChanged += () => _farPlane = farPlaneValue.Value; ViewWidgetButtonMenu.VisibleChanged += control => farPlaneValue.Value = _farPlane; @@ -575,7 +596,7 @@ namespace FlaxEditor.Viewport // Brightness { var brightness = ViewWidgetButtonMenu.AddButton("Brightness"); - var brightnessValue = new FloatValueBox(1.0f, 75, 2, 50.0f, 0.001f, 10.0f, 0.001f); + var brightnessValue = new FloatValueBox(1.0f, 90, 2, 70.0f, 0.001f, 10.0f, 0.001f); brightnessValue.Parent = brightness; brightnessValue.ValueChanged += () => Brightness = brightnessValue.Value; ViewWidgetButtonMenu.VisibleChanged += control => brightnessValue.Value = Brightness; @@ -584,11 +605,26 @@ namespace FlaxEditor.Viewport // Resolution { var resolution = ViewWidgetButtonMenu.AddButton("Resolution"); - var resolutionValue = new FloatValueBox(1.0f, 75, 2, 50.0f, 0.1f, 4.0f, 0.001f); + var resolutionValue = new FloatValueBox(1.0f, 90, 2, 70.0f, 0.1f, 4.0f, 0.001f); resolutionValue.Parent = resolution; resolutionValue.ValueChanged += () => ResolutionScale = resolutionValue.Value; ViewWidgetButtonMenu.VisibleChanged += control => resolutionValue.Value = ResolutionScale; } + + // Invert Panning + { + var invert = ViewWidgetButtonMenu.AddButton("Invert Panning"); + var invertValue = new CheckBox(90, 2, _invertPanning); + invertValue.Parent = invert; + invertValue.StateChanged += checkBox => + { + if (checkBox.Checked != _invertPanning) + { + _invertPanning = checkBox.Checked; + } + }; + ViewWidgetButtonMenu.VisibleChanged += control => invertValue.Checked = _invertPanning; + } } // Link for task event @@ -812,7 +848,7 @@ namespace FlaxEditor.Viewport /// protected virtual void OnLeftMouseButtonDown() { - _startPosLeft = _viewMousePos; + _startPos = _viewMousePos; } /// @@ -827,7 +863,7 @@ namespace FlaxEditor.Viewport /// protected virtual void OnRightMouseButtonDown() { - _startPosRight = _viewMousePos; + _startPos = _viewMousePos; } /// @@ -842,7 +878,7 @@ namespace FlaxEditor.Viewport /// protected virtual void OnMiddleMouseButtonDown() { - _startPosMiddle = _viewMousePos; + _startPos = _viewMousePos; } /// @@ -1013,7 +1049,13 @@ namespace FlaxEditor.Viewport moveDelta *= 0.3f; // Calculate smooth mouse delta not dependant on viewport size - Vector2 offset = _viewMousePos - (_input.IsMouseMiddleDown ? _startPosMiddle : _startPosRight); + + Vector2 offset = _viewMousePos - _startPos; + if (_input.IsZooming && !_input.IsMouseRightDown && !_input.IsMouseLeftDown && !_input.IsMouseMiddleDown) + { + offset = Vector2.Zero; + } + offset.X = offset.X > 0 ? Mathf.Floor(offset.X) : Mathf.Ceil(offset.X); offset.Y = offset.Y > 0 ? Mathf.Floor(offset.Y) : Mathf.Ceil(offset.Y); _mouseDeltaRight = offset / size; @@ -1055,7 +1097,7 @@ namespace FlaxEditor.Viewport // Move mouse back to the root position if (centerMouse && (_input.IsMouseRightDown || _input.IsMouseLeftDown || _input.IsMouseMiddleDown)) { - Vector2 center = PointToWindow(_input.IsMouseMiddleDown ? _startPosMiddle : _startPosRight); + Vector2 center = PointToWindow(_startPos); win.MousePosition = center; } } @@ -1101,11 +1143,11 @@ namespace FlaxEditor.Viewport if (_input.IsMouseLeftDown) { // Calculate smooth mouse delta not dependant on viewport size - Vector2 offset = _viewMousePos - _startPosLeft; + Vector2 offset = _viewMousePos - _startPos; offset.X = offset.X > 0 ? Mathf.Floor(offset.X) : Mathf.Ceil(offset.X); offset.Y = offset.Y > 0 ? Mathf.Floor(offset.Y) : Mathf.Ceil(offset.Y); _mouseDeltaLeft = offset / size; - _startPosLeft = _viewMousePos; + _startPos = _viewMousePos; } else { @@ -1193,6 +1235,28 @@ namespace FlaxEditor.Viewport base.OnDestroy(); } + private struct CameraOrientation + { + public readonly string Name; + public readonly Vector3 Orientation; + + public CameraOrientation(string name, Vector3 orientation) + { + Name = name; + Orientation = orientation; + } + } + + private readonly CameraOrientation[] EditorViewportCameraOrientationValues = + { + new CameraOrientation("Front", new Vector3(0, 0, 0)), + new CameraOrientation("Back", new Vector3(0, 180, 0)), + new CameraOrientation("Left", new Vector3(0, 90, 0)), + new CameraOrientation("Right", new Vector3(0, -90, 0)), + new CameraOrientation("Top", new Vector3(-90, 0, 0)), + new CameraOrientation("Bottom", new Vector3(90, 0, 0)) + }; + private readonly float[] EditorViewportCameraSpeedValues = { 0.1f, diff --git a/Source/Editor/Viewport/PrefabWindowViewport.cs b/Source/Editor/Viewport/PrefabWindowViewport.cs index 94b994ac8..dd6f8fce3 100644 --- a/Source/Editor/Viewport/PrefabWindowViewport.cs +++ b/Source/Editor/Viewport/PrefabWindowViewport.cs @@ -485,6 +485,23 @@ namespace FlaxEditor.Viewport } } + /// + public override void Draw() + { + base.Draw(); + + // Selected UI controls outline + for (var i = 0; i < _window.Selection.Count; i++) + { + if (_window.Selection[i].EditableObject is UIControl controlActor && controlActor.Control != null) + { + var control = controlActor.Control; + var bounds = Rectangle.FromPoints(control.PointToParent(this, Vector2.Zero), control.PointToParent(this, control.Size)); + Render2D.DrawRectangle(bounds, Editor.Instance.Options.Options.Visual.SelectionOutlineColor0, Editor.Instance.Options.Options.Visual.UISelectionOutlineSize); + } + } + } + /// protected override void OnLeftMouseButtonUp() { diff --git a/Source/Editor/Viewport/Previews/AnimatedModelPreview.cs b/Source/Editor/Viewport/Previews/AnimatedModelPreview.cs index 24ae53261..b85895528 100644 --- a/Source/Editor/Viewport/Previews/AnimatedModelPreview.cs +++ b/Source/Editor/Viewport/Previews/AnimatedModelPreview.cs @@ -84,7 +84,7 @@ namespace FlaxEditor.Viewport.Previews // Preview LOD { var previewLOD = ViewWidgetButtonMenu.AddButton("Preview LOD"); - var previewLODValue = new IntValueBox(-1, 75, 2, 50.0f, -1, 10, 0.02f); + var previewLODValue = new IntValueBox(-1, 90, 2, 70.0f, -1, 10, 0.02f); previewLODValue.Parent = previewLOD; previewLODValue.ValueChanged += () => _previewModel.ForcedLOD = previewLODValue.Value; ViewWidgetButtonMenu.VisibleChanged += control => previewLODValue.Value = _previewModel.ForcedLOD; diff --git a/Source/Editor/Viewport/Previews/AssetPreview.cs b/Source/Editor/Viewport/Previews/AssetPreview.cs index 012837f87..9bf3573a1 100644 --- a/Source/Editor/Viewport/Previews/AssetPreview.cs +++ b/Source/Editor/Viewport/Previews/AssetPreview.cs @@ -85,7 +85,7 @@ namespace FlaxEditor.Viewport.Previews var orbitRadius = 200.0f; if (camera is ArcBallCamera arcBallCamera) orbitRadius = arcBallCamera.OrbitRadius; - camera.SerArcBallView(new Quaternion(-0.08f, -0.92f, 0.31f, -0.23f), Vector3.Zero, orbitRadius); + camera.SetArcBallView(new Quaternion(-0.08f, -0.92f, 0.31f, -0.23f), Vector3.Zero, orbitRadius); if (useWidgets) { diff --git a/Source/Editor/Viewport/Previews/MaterialPreview.cs b/Source/Editor/Viewport/Previews/MaterialPreview.cs index a03598193..6ff695b69 100644 --- a/Source/Editor/Viewport/Previews/MaterialPreview.cs +++ b/Source/Editor/Viewport/Previews/MaterialPreview.cs @@ -23,6 +23,15 @@ namespace FlaxEditor.Viewport.Previews "Cone" }; + private static readonly Transform[] Transforms = + { + new Transform(Vector3.Zero, Quaternion.RotationY(Mathf.Pi), new Vector3(0.45f)), + new Transform(Vector3.Zero, Quaternion.RotationY(Mathf.Pi), new Vector3(0.45f)), + new Transform(Vector3.Zero, Quaternion.Identity, new Vector3(0.45f)), + new Transform(Vector3.Zero, Quaternion.RotationY(Mathf.Pi), new Vector3(0.45f)), + new Transform(Vector3.Zero, Quaternion.RotationY(Mathf.Pi), new Vector3(0.45f)), + }; + private StaticModel _previewModel; private Decal _decal; private Terrain _terrain; @@ -65,6 +74,7 @@ namespace FlaxEditor.Viewport.Previews _selectedModelIndex = value; _previewModel.Model = FlaxEngine.Content.LoadAsyncInternal("Editor/Primitives/" + Models[value]); + _previewModel.Transform = Transforms[value]; } } @@ -77,7 +87,6 @@ namespace FlaxEditor.Viewport.Previews { // Setup preview scene _previewModel = new StaticModel(); - _previewModel.Transform = new Transform(Vector3.Zero, Quaternion.RotationY(Mathf.Pi), new Vector3(0.45f)); SelectedModelIndex = 0; // Link actors for rendering diff --git a/Source/Editor/Viewport/Previews/ModelPreview.cs b/Source/Editor/Viewport/Previews/ModelPreview.cs index e4789bdb8..aa03d0ee2 100644 --- a/Source/Editor/Viewport/Previews/ModelPreview.cs +++ b/Source/Editor/Viewport/Previews/ModelPreview.cs @@ -53,7 +53,7 @@ namespace FlaxEditor.Viewport.Previews // Preview LOD { var previewLOD = ViewWidgetButtonMenu.AddButton("Preview LOD"); - var previewLODValue = new IntValueBox(-1, 75, 2, 50.0f, -1, 10, 0.02f); + var previewLODValue = new IntValueBox(-1, 90, 2, 70.0f, -1, 10, 0.02f); previewLODValue.Parent = previewLOD; previewLODValue.ValueChanged += () => _previewModel.ForcedLOD = previewLODValue.Value; ViewWidgetButtonMenu.VisibleChanged += control => previewLODValue.Value = _previewModel.ForcedLOD; diff --git a/Source/Editor/Viewport/Previews/ParticleEmitterPreview.cs b/Source/Editor/Viewport/Previews/ParticleEmitterPreview.cs index e7021050b..638c8324c 100644 --- a/Source/Editor/Viewport/Previews/ParticleEmitterPreview.cs +++ b/Source/Editor/Viewport/Previews/ParticleEmitterPreview.cs @@ -67,7 +67,7 @@ namespace FlaxEditor.Viewport.Previews if (useWidgets) { var playbackDuration = ViewWidgetButtonMenu.AddButton("Duration"); - var playbackDurationValue = new FloatValueBox(_playbackDuration, 75, 2, 50.0f, 0.1f, 1000000.0f, 0.1f); + var playbackDurationValue = new FloatValueBox(_playbackDuration, 90, 2, 70.0f, 0.1f, 1000000.0f, 0.1f); playbackDurationValue.Parent = playbackDuration; playbackDurationValue.ValueChanged += () => PlaybackDuration = playbackDurationValue.Value; ViewWidgetButtonMenu.VisibleChanged += control => playbackDurationValue.Value = PlaybackDuration; diff --git a/Source/Editor/Viewport/Previews/PrefabPreview.cs b/Source/Editor/Viewport/Previews/PrefabPreview.cs index d0ed8a0c2..066e98456 100644 --- a/Source/Editor/Viewport/Previews/PrefabPreview.cs +++ b/Source/Editor/Viewport/Previews/PrefabPreview.cs @@ -1,7 +1,6 @@ // Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. using FlaxEngine; -using FlaxEngine.GUI; using Object = FlaxEngine.Object; namespace FlaxEditor.Viewport.Previews @@ -19,7 +18,7 @@ namespace FlaxEditor.Viewport.Previews private Prefab _prefab; private Actor _instance; - internal Control customControlLinked; + internal UIControl customControlLinked; /// /// Gets or sets the prefab asset to preview. @@ -29,39 +28,37 @@ namespace FlaxEditor.Viewport.Previews get => _prefab; set { - if (_prefab != value) + if (_prefab == value) + return; + + // Unset and cleanup spawned instance + if (_instance) { - if (_instance) + var instance = _instance; + Instance = null; + Object.Destroy(instance); + } + + _prefab = value; + + if (_prefab) + { + // Load prefab + _prefab.WaitForLoaded(); + + // Spawn prefab + var prevPreview = LoadingPreview; + LoadingPreview = this; + var instance = PrefabManager.SpawnPrefab(_prefab, null); + LoadingPreview = prevPreview; + if (instance == null) { - if (customControlLinked != null) - { - customControlLinked.Parent = null; - customControlLinked = null; - } - Task.RemoveCustomActor(_instance); - Object.Destroy(_instance); + _prefab = null; + throw new FlaxException("Failed to spawn a prefab for the preview."); } - _prefab = value; - - if (_prefab) - { - _prefab.WaitForLoaded(); // TODO: use lazy prefab spawning to reduce stalls - - var prevPreview = LoadingPreview; - LoadingPreview = this; - - _instance = PrefabManager.SpawnPrefab(_prefab, null); - - LoadingPreview = prevPreview; - - if (_instance == null) - { - _prefab = null; - throw new FlaxException("Failed to spawn a prefab for the preview."); - } - Task.AddCustomActor(_instance); - } + // Set instance + Instance = instance; } } } @@ -72,7 +69,47 @@ namespace FlaxEditor.Viewport.Previews public Actor Instance { get => _instance; - internal set => _instance = value; + internal set + { + if (_instance == value) + return; + + if (_instance) + { + // Unlink UI control + if (customControlLinked) + { + if (customControlLinked.Control?.Parent == this) + customControlLinked.Control.Parent = null; + customControlLinked = null; + } + + // Remove for the preview + Task.RemoveCustomActor(_instance); + } + + _instance = value; + + if (_instance) + { + // Add to the preview + Task.AddCustomActor(_instance); + + // Link UI canvases to the preview + LinkCanvas(_instance); + } + } + } + + private void LinkCanvas(Actor actor) + { + if (actor is UICanvas uiCanvas) + uiCanvas.EditorOverride(Task, this); + var children = actor.ChildrenCount; + for (int i = 0; i < children; i++) + { + LinkCanvas(actor.GetChild(i)); + } } /// @@ -87,7 +124,6 @@ namespace FlaxEditor.Viewport.Previews /// public override void OnDestroy() { - // Cleanup Prefab = null; base.OnDestroy(); diff --git a/Source/Editor/Viewport/Previews/TexturePreview.cs b/Source/Editor/Viewport/Previews/TexturePreview.cs index d1904027a..5e1024609 100644 --- a/Source/Editor/Viewport/Previews/TexturePreview.cs +++ b/Source/Editor/Viewport/Previews/TexturePreview.cs @@ -159,13 +159,14 @@ namespace FlaxEditor.Viewport.Previews float prevScale = _viewScale; _viewScale = Mathf.Clamp(_viewScale + delta * 0.24f, 0.001f, 20.0f); - // Move view to make use of the control much more soother - //float coeff = (prevScale + (_viewScale - prevScale)) / prevScale; - //_viewPos += (location * coeff - location) / _viewScale; - //_viewPos += location / _viewScale; - Vector2 sizeDelta = (_viewScale - prevScale) * _textureRect.Size; + // Compensate for the Rectangle.MakeScaled + Vector2 sizeDelta = (_viewScale - prevScale) * _textureRect.Size * 0.5f; _viewPos += sizeDelta * 0.5f; + // Move to zoom position + Vector2 locationOnTexture = (location - _textureRect.Location) / _textureRect.Size; + _viewPos -= sizeDelta * locationOnTexture; + return true; } diff --git a/Source/Editor/Windows/AboutDialog.cs b/Source/Editor/Windows/AboutDialog.cs index 03e60f43c..bb320fb87 100644 --- a/Source/Editor/Windows/AboutDialog.cs +++ b/Source/Editor/Windows/AboutDialog.cs @@ -115,7 +115,7 @@ namespace FlaxEditor.Windows { var thirdPartyPanel = new Panel(ScrollBars.Vertical) { - Bounds = new Rectangle(0, topParentControl.Bottom + 4, Width, Height - topParentControl.Bottom - 24), + Bounds = new Rectangle(4, topParentControl.Bottom + 4, Width - 8, Height - topParentControl.Bottom - 24), Parent = this }; var thirdPartyEntries = new[] diff --git a/Source/Editor/Windows/Assets/AssetEditorWindow.cs b/Source/Editor/Windows/Assets/AssetEditorWindow.cs index 756adbe05..8ad655d01 100644 --- a/Source/Editor/Windows/Assets/AssetEditorWindow.cs +++ b/Source/Editor/Windows/Assets/AssetEditorWindow.cs @@ -288,7 +288,7 @@ namespace FlaxEditor.Windows.Assets public abstract class AssetEditorWindowBase : AssetEditorWindow where T : Asset { /// - /// Flag set to true if window is is waiting for asset to be loaded (to send or events). + /// Flag set to true if window is waiting for asset to be loaded (to send or events). /// protected bool _isWaitingForLoaded; @@ -430,7 +430,7 @@ namespace FlaxEditor.Windows.Assets /// /// Gets the original asset. Note: is the cloned asset for local editing. Use to apply changes to the original asset. /// - public T OriginalAsset => (T)FlaxEngine.Content.GetAsset(_item.ID); + public T OriginalAsset => (T)FlaxEngine.Content.Load(_item.ID); /// protected ClonedAssetEditorWindowBase(Editor editor, AssetItem item) diff --git a/Source/Editor/Windows/Assets/JsonAssetWindow.cs b/Source/Editor/Windows/Assets/JsonAssetWindow.cs index 670414241..da463c501 100644 --- a/Source/Editor/Windows/Assets/JsonAssetWindow.cs +++ b/Source/Editor/Windows/Assets/JsonAssetWindow.cs @@ -40,6 +40,12 @@ namespace FlaxEditor.Windows.Assets _presenter.Modified += MarkAsEdited; } + private void OnScriptsReloadBegin() + { + Close(); + ScriptsBuilder.ScriptsReloadBegin -= OnScriptsReloadBegin; + } + /// public override void Save() { @@ -76,6 +82,10 @@ namespace FlaxEditor.Windows.Assets _presenter.Select(_object); ClearEditedFlag(); + // Auto-close on scripting reload if json asset is from game scripts (it might be reloaded) + if (_object != null && FlaxEngine.Scripting.IsTypeFromGameScripts(_object.GetType())) + ScriptsBuilder.ScriptsReloadBegin += OnScriptsReloadBegin; + base.OnAssetLoaded(); } diff --git a/Source/Editor/Windows/Assets/ModelWindow.cs b/Source/Editor/Windows/Assets/ModelWindow.cs index 9910c6e7a..533c9694c 100644 --- a/Source/Editor/Windows/Assets/ModelWindow.cs +++ b/Source/Editor/Windows/Assets/ModelWindow.cs @@ -1004,7 +1004,7 @@ namespace FlaxEditor.Windows.Assets protected override void OnAssetLoaded() { _refreshOnLODsLoaded = true; - _preview.ViewportCamera.SerArcBallView(Asset.GetBox()); + _preview.ViewportCamera.SetArcBallView(Asset.GetBox()); UpdateEffectsOnAsset(); // TODO: disable streaming for this model diff --git a/Source/Editor/Windows/Assets/PrefabWindow.Hierarchy.cs b/Source/Editor/Windows/Assets/PrefabWindow.Hierarchy.cs index 08621f2ec..bded7e147 100644 --- a/Source/Editor/Windows/Assets/PrefabWindow.Hierarchy.cs +++ b/Source/Editor/Windows/Assets/PrefabWindow.Hierarchy.cs @@ -69,6 +69,7 @@ namespace FlaxEditor.Windows.Assets bool hasSthSelected = Selection.Count > 0; bool isSingleActorSelected = Selection.Count == 1 && Selection[0] is ActorNode; bool isRootSelected = isSingleActorSelected && Selection[0] == Graph.Main; + bool hasPrefabLink = isSingleActorSelected && (Selection[0] as ActorNode).HasPrefabLink; // Create popup @@ -97,7 +98,7 @@ namespace FlaxEditor.Windows.Assets b.Enabled = hasSthSelected && !isRootSelected; b = contextMenu.AddButton("Set Root", SetRoot); - b.Enabled = isSingleActorSelected && !isRootSelected; + b.Enabled = isSingleActorSelected && !isRootSelected && hasPrefabLink; // Prefab options @@ -108,8 +109,6 @@ namespace FlaxEditor.Windows.Assets (Selection[0] as ActorNode).CanCreatePrefab && Editor.Windows.ContentWin.CurrentViewFolder.CanHaveAssets; - bool hasPrefabLink = isSingleActorSelected && (Selection[0] as ActorNode).HasPrefabLink; - b = contextMenu.AddButton("Select Prefab", Editor.Prefabs.SelectPrefab); b.Enabled = hasPrefabLink; diff --git a/Source/Editor/Windows/Assets/PrefabWindow.cs b/Source/Editor/Windows/Assets/PrefabWindow.cs index a8b3e24a4..4cc7898ee 100644 --- a/Source/Editor/Windows/Assets/PrefabWindow.cs +++ b/Source/Editor/Windows/Assets/PrefabWindow.cs @@ -284,6 +284,9 @@ namespace FlaxEditor.Windows.Assets { // Simply update changes Editor.Prefabs.ApplyAll(_viewport.Instance); + + // Refresh properties panel to sync new prefab default values + _propertiesEditor.BuildLayout(); } catch (Exception) { diff --git a/Source/Editor/Windows/Assets/SceneAnimationWindow.cs b/Source/Editor/Windows/Assets/SceneAnimationWindow.cs index cbed724bc..c18edc716 100644 --- a/Source/Editor/Windows/Assets/SceneAnimationWindow.cs +++ b/Source/Editor/Windows/Assets/SceneAnimationWindow.cs @@ -277,6 +277,7 @@ namespace FlaxEditor.Windows.Assets private readonly StagingTexture[] _stagingTextures = new StagingTexture[FrameLatency + 1]; private RenderProgress _progress; private RenderEditorState _editorState; + private GameWindow _gameWindow; public RenderOptions Options => _options; @@ -373,6 +374,9 @@ namespace FlaxEditor.Windows.Assets gameWin.Viewport.Task.PostRender += OnPostRender; if (!gameWin.Visible) gameWin.Show(); + else if (!gameWin.IsFocused) + gameWin.Focus(); + _gameWindow = gameWin; _warmUpTimeLeft = _options.WarmUpTime; _animationFrame = 0; var stagingTextureDesc = GPUTextureDescription.New2D(resolution.X, resolution.Y, gameWin.Viewport.Task.Output.Format, GPUTextureFlags.None); @@ -385,6 +389,12 @@ namespace FlaxEditor.Windows.Assets _stagingTextures[i].TaskFrame = -1; } _player.Play(); + if (!_player.IsPlaying) + { + Editor.LogError("Scene Animation Player failed to start playing."); + CancelRendering(); + return; + } if (_warmUpTimeLeft > 0.0f) { // Start warmup time @@ -438,8 +448,11 @@ namespace FlaxEditor.Windows.Assets _stagingTextures[i].Texture.ReleaseGPU(); Object.Destroy(ref _stagingTextures[i].Texture); } - _progress.End(); - editor.ProgressReporting.UnregisterHandler(_progress); + if (_progress != null) + { + _progress.End(); + editor.ProgressReporting.UnregisterHandler(_progress); + } if (_editorState != null) { editor.StateMachine.GoToState(editor.StateMachine.EditingSceneState); @@ -452,6 +465,7 @@ namespace FlaxEditor.Windows.Assets gameWin.Viewport.BackgroundColor = Color.Transparent; gameWin.Viewport.KeepAspectRatio = false; gameWin.Viewport.Task.PostRender -= OnPostRender; + _gameWindow = null; _isRendering = false; _presenter.Panel.Enabled = true; _presenter.BuildLayoutOnUpdate(); @@ -467,6 +481,11 @@ namespace FlaxEditor.Windows.Assets { // Render first frame _player.Play(); + if (!_player.IsPlaying) + { + Editor.LogError("Scene Animation Player failed to start playing."); + CancelRendering(); + } _warmUpTimeLeft = -1; _state = States.Render; } @@ -490,7 +509,7 @@ namespace FlaxEditor.Windows.Assets } if (_player.IsStopped || _player.Time >= _options.EndTime) { - // End rendering but perform reaming copies of the staging textures so all data is captured (from GPU to CPU) + // End rendering but perform remaining copies of the staging textures so all data is captured (from GPU to CPU) _state = States.Staging; break; } @@ -503,8 +522,7 @@ namespace FlaxEditor.Windows.Assets private void OnPostRender(GPUContext context, RenderContext renderContext) { - var gameWin = Editor.Instance.Windows.GameWin; - var task = gameWin.Viewport.Task; + var task = _gameWindow.Viewport.Task; var taskFrame = task.FrameCount; // Check all staging textures for finished GPU to CPU transfers @@ -540,7 +558,7 @@ namespace FlaxEditor.Windows.Assets ref var stagingTexture = ref _stagingTextures[textureIdx]; stagingTexture.AnimationFrame = _animationFrame; stagingTexture.TaskFrame = taskFrame; - _options.VideoOutputFormat.RenderFrame(context, ref stagingTexture, _options, gameWin.Viewport.Task.Output); + _options.VideoOutputFormat.RenderFrame(context, ref stagingTexture, _options, _gameWindow.Viewport.Task.Output); // Now wait for the next animation frame to be updated _state = States.Update; diff --git a/Source/Editor/Windows/Assets/SkinnedModelWindow.cs b/Source/Editor/Windows/Assets/SkinnedModelWindow.cs index 2f6c952ba..c599e7e2a 100644 --- a/Source/Editor/Windows/Assets/SkinnedModelWindow.cs +++ b/Source/Editor/Windows/Assets/SkinnedModelWindow.cs @@ -1097,7 +1097,7 @@ namespace FlaxEditor.Windows.Assets protected override void OnAssetLoaded() { _refreshOnLODsLoaded = true; - _preview.ViewportCamera.SerArcBallView(Asset.GetBox()); + _preview.ViewportCamera.SetArcBallView(Asset.GetBox()); UpdateEffectsOnAsset(); // TODO: disable streaming for this model diff --git a/Source/Editor/Windows/ContentWindow.ContextMenu.cs b/Source/Editor/Windows/ContentWindow.ContextMenu.cs index 1b4d2be31..f81220ac4 100644 --- a/Source/Editor/Windows/ContentWindow.ContextMenu.cs +++ b/Source/Editor/Windows/ContentWindow.ContextMenu.cs @@ -224,7 +224,7 @@ namespace FlaxEditor.Windows /// private void ExportSelection() { - if(FileSystem.ShowBrowseFolderDialog(Editor.Windows.MainWindow, null, "Select the output folder", out var outputFolder)) + if (FileSystem.ShowBrowseFolderDialog(Editor.Windows.MainWindow, null, "Select the output folder", out var outputFolder)) return; var selection = _view.Selection; diff --git a/Source/Editor/Windows/ContentWindow.cs b/Source/Editor/Windows/ContentWindow.cs index 66ed50daf..145c0d972 100644 --- a/Source/Editor/Windows/ContentWindow.cs +++ b/Source/Editor/Windows/ContentWindow.cs @@ -396,7 +396,7 @@ namespace FlaxEditor.Windows /// The item to delete. public void Delete(ContentItem item) { - Delete(new List { item }); + Delete(new List(1) { item }); } /// @@ -405,36 +405,18 @@ namespace FlaxEditor.Windows /// The items to delete. public void Delete(List items) { + if (items.Count == 0) return; + // TODO: remove items that depend on different items in the list: use wants to remove `folderA` and `folderA/asset.x`, we should just remove `folderA` var toDelete = new List(items); + string msg = toDelete.Count == 1 ? + string.Format("Are you sure to delete \'{0}\'?\nThis action cannot be undone. Files will be deleted permanently.", items[0].Path) + : string.Format("Are you sure to delete {0} selected items?\nThis action cannot be undone. Files will be deleted permanently.", items.Count); + // Ask user - if (toDelete.Count == 1) - { - // Single item - if (MessageBox.Show(string.Format("Are you sure to delete \'{0}\'?\nThis action cannot be undone. Files will be deleted permanently.", items[0].Path), - "Delete asset(s)", - MessageBoxButtons.OKCancel, - MessageBoxIcon.Question) - != DialogResult.OK) - { - // Break - return; - } - } - else - { - // Many items - if (MessageBox.Show(string.Format("Are you sure to delete {0} selected items?\nThis action cannot be undone. Files will be deleted permanently.", items.Count), - "Delete asset(s)", - MessageBoxButtons.OKCancel, - MessageBoxIcon.Question) - != DialogResult.OK) - { - // Break - return; - } - } + if (MessageBox.Show(msg, "Delete asset(s)", MessageBoxButtons.OKCancel, MessageBoxIcon.Question) != DialogResult.OK) + return; // Clear navigation // TODO: just remove invalid locations from the history (those are removed) diff --git a/Source/Editor/Windows/GameWindow.cs b/Source/Editor/Windows/GameWindow.cs index a29869825..858f0e0ba 100644 --- a/Source/Editor/Windows/GameWindow.cs +++ b/Source/Editor/Windows/GameWindow.cs @@ -222,7 +222,7 @@ namespace FlaxEditor.Windows private void PlayingStateOnSceneDuplicating() { - // Remove reaming GUI controls so loaded scene can add own GUI + // Remove remaining GUI controls so loaded scene can add own GUI //_guiRoot.DisposeChildren(); // Show GUI @@ -231,7 +231,7 @@ namespace FlaxEditor.Windows private void PlayingStateOnSceneRestored() { - // Remove reaming GUI controls so loaded scene can add own GUI + // Remove remaining GUI controls so loaded scene can add own GUI //_guiRoot.DisposeChildren(); // Hide GUI @@ -294,7 +294,9 @@ namespace FlaxEditor.Windows { if (Editor.Instance.SceneEditing.Selection[i].EditableObject is UIControl controlActor && controlActor.Control != null) { - Render2D.DrawRectangle(controlActor.Control.Bounds, Editor.Instance.Options.Options.Visual.SelectionOutlineColor0, Editor.Instance.Options.Options.Visual.UISelectionOutlineSize); + var control = controlActor.Control; + var bounds = Rectangle.FromPoints(control.PointToParent(_viewport, Vector2.Zero), control.PointToParent(_viewport, control.Size)); + Render2D.DrawRectangle(bounds, Editor.Instance.Options.Options.Visual.SelectionOutlineColor0, Editor.Instance.Options.Options.Visual.UISelectionOutlineSize); } } @@ -387,6 +389,12 @@ namespace FlaxEditor.Windows } } + // Prevent closing the game window tab during a play session + if (Editor.StateMachine.IsPlayMode && Editor.Options.Options.Input.CloseTab.Process(this, key)) + { + return true; + } + return base.OnKeyDown(key); } diff --git a/Source/Editor/Windows/SplashScreen.cpp b/Source/Editor/Windows/SplashScreen.cpp index 98578950f..bc92a8d2a 100644 --- a/Source/Editor/Windows/SplashScreen.cpp +++ b/Source/Editor/Windows/SplashScreen.cpp @@ -110,6 +110,12 @@ const Char* SplashScreenQuotes[] = TEXT("You have my bow.\nAnd my axe!"), TEXT("To the bridge of Khazad-dum."), TEXT("One ring to rule them all.\nOne ring to find them."), + TEXT("Ladies and gentelman, we got him"), + TEXT("Cyberpunk of game engines"), + TEXT("That's what she said"), + TEXT("Compiling Shaders (93,788)"), + TEXT("Hi There"), + TEXT("BAGUETTE"), }; SplashScreen::~SplashScreen() diff --git a/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp b/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp index 8f43506d2..1a26318a1 100644 --- a/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp +++ b/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp @@ -1489,11 +1489,11 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu case 2: value = transitionsData.Position / transitionsData.Length; break; - // Reaming Time + // Remaining Time case 3: value = transitionsData.Length - transitionsData.Position; break; - // Reaming Normalized Time + // Remaining Normalized Time case 4: value = 1.0f - (transitionsData.Position / transitionsData.Length); break; diff --git a/Source/Engine/Audio/AudioSource.h b/Source/Engine/Audio/AudioSource.h index ac15b5a4b..2b5b7e9d8 100644 --- a/Source/Engine/Audio/AudioSource.h +++ b/Source/Engine/Audio/AudioSource.h @@ -199,7 +199,7 @@ public: /// Gets the current state of the audio playback (playing/paused/stopped). /// /// The value. - API_PROPERTY() FORCE_INLINE States GetState() const + API_PROPERTY() FORCE_INLINE AudioSource::States GetState() const { return _state; } diff --git a/Source/Engine/CSG/CSGMesh.Triangulate.cpp b/Source/Engine/CSG/CSGMesh.Triangulate.cpp index bb814653b..8a64f4042 100644 --- a/Source/Engine/CSG/CSGMesh.Triangulate.cpp +++ b/Source/Engine/CSG/CSGMesh.Triangulate.cpp @@ -22,7 +22,7 @@ bool CSG::Mesh::Triangulate(RawData& data, Array& cacheVB) const Array surfaceCacheVB(32); // Cache submeshes by material to lay them down - // key- brush index, value- direcotry for surafecs (key: surface index, value: list with start vertex per triangle) + // key- brush index, value- direcotry for surfaces (key: surface index, value: list with start vertex per triangle) Dictionary>> polygonsPerBrush(64); // Build index buffer @@ -115,8 +115,8 @@ bool CSG::Mesh::Triangulate(RawData& data, Array& cacheVB) const tangent.Normalize(); // Gram-Schmidt orthogonalize - Vector3 newTangentUnormalized = tangent - normal * Vector3::Dot(normal, tangent); - const float length = newTangentUnormalized.Length(); + Vector3 newTangentUnnormalized = tangent - normal * Vector3::Dot(normal, tangent); + const float length = newTangentUnnormalized.Length(); // Workaround to handle degenerated case if (Math::IsZero(length)) @@ -129,7 +129,7 @@ bool CSG::Mesh::Triangulate(RawData& data, Array& cacheVB) const } else { - tangent = newTangentUnormalized / length; + tangent = newTangentUnnormalized / length; bitangent.Normalize(); } @@ -217,12 +217,12 @@ bool CSG::Mesh::Triangulate(RawData& data, Array& cacheVB) const auto& vertex = cacheVB[triangleStartVertex + k]; Vector3 projected = Vector3::Project(vertex.Position, 0, 0, 1000, 1000, 0, 1, vp); - Vector2 projectecXY = Vector2(projected); + Vector2 projectedXY = Vector2(projected); - min = Vector2::Min(projectecXY, min); - max = Vector2::Max(projectecXY, max); + min = Vector2::Min(projectedXY, min); + max = Vector2::Max(projectedXY, max); - pointsCache.Add(projectecXY); + pointsCache.Add(projectedXY); } } diff --git a/Source/Engine/CSG/CSGMesh.cpp b/Source/Engine/CSG/CSGMesh.cpp index cc4897d3a..9a885a35d 100644 --- a/Source/Engine/CSG/CSGMesh.cpp +++ b/Source/Engine/CSG/CSGMesh.cpp @@ -38,7 +38,7 @@ void CSG::Mesh::PerformOperation(Mesh* other) { case Mode::Additive: { - // Check if both meshes do not intesect + // Check if both meshes do not intersect if (AABB::IsOutside(_bounds, other->GetBounds())) // TODO: test every sub bounds not whole _bounds { // Add vertices to the mesh without any additional calculations @@ -57,7 +57,7 @@ void CSG::Mesh::PerformOperation(Mesh* other) case Mode::Subtractive: { - // Check if both meshes do not intesect + // Check if both meshes do not intersect if (AABB::IsOutside(_bounds, other->GetBounds())) // TODO: test every sub bounds not whole _bounds { // Do nothing @@ -141,7 +141,7 @@ void CSG::Mesh::intersect(const Mesh* other, PolygonOperation insideOp, PolygonO // insideOp - operation for polygons being inside the other brush // outsideOp - operation for polygons being outside the other brush - // Prevent from redudant action + // Prevent from redundant action if (insideOp == Keep && outsideOp == Keep) return; @@ -180,7 +180,7 @@ void CSG::Mesh::intersectSubMesh(const Mesh* other, int32 subMeshIndex, PolygonO int32 startBrushSurface = brushMeta.StartSurfaceIndex; int32 endBrushSurface = startBrushSurface + brushMeta.SurfacesCount; - // Check every polygon (itereate fron end since we can ass new polygons and we don't want to process them) + // Check every polygon (iterate from end since we can ass new polygons and we don't want to process them) for (int32 i = _polygons.Count() - 1; i >= 0; i--) { auto& polygon = _polygons[i]; diff --git a/Source/Engine/Content/Assets/ModelBase.h b/Source/Engine/Content/Assets/ModelBase.h index ebf05570d..89805f3c0 100644 --- a/Source/Engine/Content/Assets/ModelBase.h +++ b/Source/Engine/Content/Assets/ModelBase.h @@ -53,7 +53,7 @@ public: ScopeLock lock(Locker); const int32 prevCount = MaterialSlots.Count(); - MaterialSlots.Resize(slotsCount); + MaterialSlots.Resize(slotsCount, false); // Initialize slot names for (int32 i = prevCount; i < slotsCount; i++) diff --git a/Source/Engine/Content/JsonAsset.h b/Source/Engine/Content/JsonAsset.h index ce617938d..c74c4a045 100644 --- a/Source/Engine/Content/JsonAsset.h +++ b/Source/Engine/Content/JsonAsset.h @@ -75,7 +75,7 @@ protected: /// Generic type of Json-format asset. It provides the managed representation of this resource data so it can be accessed via C# API. /// /// -API_CLASS(NoSpawn) class JsonAsset : public JsonAssetBase +API_CLASS(NoSpawn) class FLAXENGINE_API JsonAsset : public JsonAssetBase { DECLARE_ASSET_HEADER(JsonAsset); diff --git a/Source/Engine/Content/Loading/ContentLoadingManager.cpp b/Source/Engine/Content/Loading/ContentLoadingManager.cpp index ac2dd8257..6e6ff7a65 100644 --- a/Source/Engine/Content/Loading/ContentLoadingManager.cpp +++ b/Source/Engine/Content/Loading/ContentLoadingManager.cpp @@ -208,7 +208,7 @@ void ContentLoadingManagerService::Dispose() MainThread = nullptr; ThisThread = nullptr; - // Cancel all reaming tasks (no chance to execute them) + // Cancel all remaining tasks (no chance to execute them) Tasks.CancelAll(); } diff --git a/Source/Engine/Content/Utilities/IESLoader.cpp b/Source/Engine/Content/Utilities/IESLoader.cpp index 0ef7c320a..271ab3d5e 100644 --- a/Source/Engine/Content/Utilities/IESLoader.cpp +++ b/Source/Engine/Content/Utilities/IESLoader.cpp @@ -240,7 +240,7 @@ float IESLoader::ExtractInR16(Array& output) float result = 0.0f; for (uint32 i = 0; i < hAnglesCount; i++) result += InterpolateBilinear(static_cast(i), v); - *out++ = ConvertFloatToHalf(invMaxValue * result / (float)hAnglesCount); + *out++ = Float16Compressor::Compress(invMaxValue * result / (float)hAnglesCount); } } diff --git a/Source/Engine/ContentExporters/ExportModel.cpp b/Source/Engine/ContentExporters/ExportModel.cpp index efc13419b..7fdd011f9 100644 --- a/Source/Engine/ContentExporters/ExportModel.cpp +++ b/Source/Engine/ContentExporters/ExportModel.cpp @@ -76,7 +76,7 @@ ExportAssetResult AssetExporters::ExportModel(ExportAssetContext& context) for (uint32 i = 0; i < vertices; i++) { auto v = vb1[i].TexCoord; - output->WriteTextFormatted("vt {0} {1}\n", ConvertHalfToFloat(v.X), ConvertHalfToFloat(v.Y)); + output->WriteTextFormatted("vt {0} {1}\n", Float16Compressor::Decompress(v.X), Float16Compressor::Decompress(v.Y)); } output->WriteChar('\n'); @@ -181,7 +181,7 @@ ExportAssetResult AssetExporters::ExportSkinnedModel(ExportAssetContext& context for (uint32 i = 0; i < vertices; i++) { auto v = vb0[i].TexCoord; - output->WriteTextFormatted("vt {0} {1}\n", ConvertHalfToFloat(v.X), ConvertHalfToFloat(v.Y)); + output->WriteTextFormatted("vt {0} {1}\n", Float16Compressor::Decompress(v.X), Float16Compressor::Decompress(v.Y)); } output->WriteChar('\n'); diff --git a/Source/Engine/ContentImporters/AssetsImportingManager.cpp b/Source/Engine/ContentImporters/AssetsImportingManager.cpp index 3a39bd64b..f1d048ca5 100644 --- a/Source/Engine/ContentImporters/AssetsImportingManager.cpp +++ b/Source/Engine/ContentImporters/AssetsImportingManager.cpp @@ -178,7 +178,7 @@ void CreateAssetContext::ApplyChanges() // Move file if (FileSystem::MoveFile(TargetAssetPath, OutputPath, true)) { - LOG(Warning, "Cannot move imported file to the destination path."); + LOG(Warning, "Cannot move imported file {0} to the destination path {1}.", OutputPath, TargetAssetPath); _applyChangesResult = CreateAssetResult::CannotSaveFile; return; } diff --git a/Source/Engine/Core/Config/LayersTagsSettings.h b/Source/Engine/Core/Config/LayersTagsSettings.h index 4ed9394fb..c01f915aa 100644 --- a/Source/Engine/Core/Config/LayersTagsSettings.h +++ b/Source/Engine/Core/Config/LayersTagsSettings.h @@ -3,6 +3,7 @@ #pragma once #include "Engine/Core/Config/Settings.h" +#include "Engine/Serialization/Json.h" /// /// Layers and objects tags settings. diff --git a/Source/Engine/Core/Config/Settings.h b/Source/Engine/Core/Config/Settings.h index 0380c96b3..e21e558fc 100644 --- a/Source/Engine/Core/Config/Settings.h +++ b/Source/Engine/Core/Config/Settings.h @@ -9,7 +9,7 @@ /// /// Base class for all global settings containers for the engine. Helps to apply, store and expose properties to c#. /// -class SettingsBase +class FLAXENGINE_API SettingsBase { public: diff --git a/Source/Engine/Core/Log.cpp b/Source/Engine/Core/Log.cpp index 2213672c9..cb73a79e1 100644 --- a/Source/Engine/Core/Log.cpp +++ b/Source/Engine/Core/Log.cpp @@ -51,18 +51,18 @@ bool Log::Logger::Init() { // Check if there are any files to delete const int32 maxLogFiles = 20; - int32 reaming = oldLogs.Count() - maxLogFiles + 1; - if (reaming > 0) + int32 remaining = oldLogs.Count() - maxLogFiles + 1; + if (remaining > 0) { Sorting::QuickSort(oldLogs.Get(), oldLogs.Count()); // Delete the oldest logs int32 i = 0; - while (reaming > 0) + while (remaining > 0) { FileSystem::DeleteFile(oldLogs[i++]); filesDeleted++; - reaming--; + remaining--; } } } diff --git a/Source/Engine/Core/Math/AABB.h b/Source/Engine/Core/Math/AABB.h index 39e8a5bf6..5853d6597 100644 --- a/Source/Engine/Core/Math/AABB.h +++ b/Source/Engine/Core/Math/AABB.h @@ -8,7 +8,7 @@ /// /// Integer axis aligned bounding box /// -struct AABB +struct FLAXENGINE_API AABB { public: diff --git a/Source/Engine/Core/Math/BoundingFrustum.h b/Source/Engine/Core/Math/BoundingFrustum.h index b8b16c412..9052521fc 100644 --- a/Source/Engine/Core/Math/BoundingFrustum.h +++ b/Source/Engine/Core/Math/BoundingFrustum.h @@ -10,7 +10,7 @@ /// /// Defines a frustum which can be used in frustum culling, zoom to Extents (zoom to fit) operations, (matrix, frustum, camera) interchange, and many kind of intersection testing. /// -API_STRUCT(InBuild) struct BoundingFrustum +API_STRUCT(InBuild) struct FLAXENGINE_API BoundingFrustum { private: diff --git a/Source/Engine/Core/Math/CollisionsHelper.cpp b/Source/Engine/Core/Math/CollisionsHelper.cpp index 48e7472d1..d485e5fba 100644 --- a/Source/Engine/Core/Math/CollisionsHelper.cpp +++ b/Source/Engine/Core/Math/CollisionsHelper.cpp @@ -15,7 +15,7 @@ void CollisionsHelper::ClosestPointPointLine(const Vector2& point, const Vector2 Vector2 n = p1 - p0; const float length = n.Length(); - if (length < 1e-10) + if (length < 1e-10f) { // Both points are the same, just give any result = p0; @@ -24,7 +24,7 @@ void CollisionsHelper::ClosestPointPointLine(const Vector2& point, const Vector2 n /= length; const float dot = Vector2::Dot(n, p); - if (dot <= 0.0) + if (dot <= 0.0f) { // Before first point result = p0; @@ -41,6 +41,45 @@ void CollisionsHelper::ClosestPointPointLine(const Vector2& point, const Vector2 } } +Vector2 CollisionsHelper::ClosestPointPointLine(const Vector2& point, const Vector2& p0, const Vector2& p1) +{ + Vector2 result; + ClosestPointPointLine(point, p0, p1, result); + return result; +} + +void CollisionsHelper::ClosestPointPointLine(const Vector3& point, const Vector3& p0, const Vector3& p1, Vector3& result) +{ + const Vector3 p = point - p0; + Vector3 n = p1 - p0; + const float length = n.Length(); + if (length < 1e-10f) + { + result = p0; + return; + } + n /= length; + const float dot = Vector3::Dot(n, p); + if (dot <= 0.0f) + { + result = p0; + return; + } + if (dot >= length) + { + result = p1; + return; + } + result = p0 + n * dot; +} + +Vector3 CollisionsHelper::ClosestPointPointLine(const Vector3& point, const Vector3& p0, const Vector3& p1) +{ + Vector3 result; + ClosestPointPointLine(point, p0, p1, result); + return result; +} + void CollisionsHelper::ClosestPointPointTriangle(const Vector3& point, const Vector3& vertex1, const Vector3& vertex2, const Vector3& vertex3, Vector3& result) { // Source: Real-Time Collision Detection by Christer Ericson @@ -101,6 +140,13 @@ void CollisionsHelper::ClosestPointPointTriangle(const Vector3& point, const Vec result = vertex1 + ab * v2 + ac * w2; //= u*vertex1 + v*vertex2 + w*vertex3, u = va * denom = 1.0f - v - w } +Vector3 CollisionsHelper::ClosestPointPointTriangle(const Vector3& point, const Vector3& vertex1, const Vector3& vertex2, const Vector3& vertex3) +{ + Vector3 result; + ClosestPointPointTriangle(point, vertex1, vertex2, vertex3, result); + return result; +} + void CollisionsHelper::ClosestPointPlanePoint(const Plane& plane, const Vector3& point, Vector3& result) { // Source: Real-Time Collision Detection by Christer Ericson @@ -112,6 +158,13 @@ void CollisionsHelper::ClosestPointPlanePoint(const Plane& plane, const Vector3& result = point - t * plane.Normal; } +Vector3 CollisionsHelper::ClosestPointPlanePoint(const Plane& plane, const Vector3& point) +{ + Vector3 result; + ClosestPointPlanePoint(plane, point, result); + return result; +} + void CollisionsHelper::ClosestPointBoxPoint(const BoundingBox& box, const Vector3& point, Vector3& result) { // Source: Real-Time Collision Detection by Christer Ericson @@ -122,6 +175,13 @@ void CollisionsHelper::ClosestPointBoxPoint(const BoundingBox& box, const Vector Vector3::Min(temp, box.Maximum, result); } +Vector3 CollisionsHelper::ClosestPointBoxPoint(const BoundingBox& box, const Vector3& point) +{ + Vector3 result; + ClosestPointBoxPoint(box, point, result); + return result; +} + void CollisionsHelper::ClosestPointRectanglePoint(const Rectangle& rect, const Vector2& point, Vector2& result) { Vector2 temp, end; @@ -130,6 +190,13 @@ void CollisionsHelper::ClosestPointRectanglePoint(const Rectangle& rect, const V Vector2::Min(temp, end, result); } +Vector2 CollisionsHelper::ClosestPointRectanglePoint(const Rectangle& rect, const Vector2& point) +{ + Vector2 result; + ClosestPointRectanglePoint(rect, point, result); + return result; +} + void CollisionsHelper::ClosestPointSpherePoint(const BoundingSphere& sphere, const Vector3& point, Vector3& result) { // Source: Jorgy343 @@ -147,6 +214,13 @@ void CollisionsHelper::ClosestPointSpherePoint(const BoundingSphere& sphere, con result += sphere.Center; } +Vector3 CollisionsHelper::ClosestPointSpherePoint(const BoundingSphere& sphere, const Vector3& point) +{ + Vector3 result; + ClosestPointSpherePoint(sphere, point, result); + return result; +} + void CollisionsHelper::ClosestPointSphereSphere(const BoundingSphere& sphere1, const BoundingSphere& sphere2, Vector3& result) { // Source: Jorgy343 @@ -164,6 +238,13 @@ void CollisionsHelper::ClosestPointSphereSphere(const BoundingSphere& sphere1, c result += sphere1.Center; } +Vector3 CollisionsHelper::ClosestPointSphereSphere(const BoundingSphere& sphere1, const BoundingSphere& sphere2) +{ + Vector3 result; + ClosestPointSphereSphere(sphere1, sphere2, result); + return result; +} + float CollisionsHelper::DistancePlanePoint(const Plane& plane, const Vector3& point) { // Source: Real-Time Collision Detection by Christer Ericson @@ -821,7 +902,6 @@ bool CollisionsHelper::RayIntersectsBox(const Ray& ray, const BoundingBox& box, d = Math::Abs(size.Z - Math::Abs(localPoint.Z)); if (d < dMin) { - dMin = d; normal = Vector3(0, 0, Math::Sign(localPoint.Z)); } @@ -990,15 +1070,11 @@ PlaneIntersectionType CollisionsHelper::PlaneIntersectsBox(const Plane& plane, c min.Z = plane.Normal.Z >= 0.0f ? box.Maximum.Z : box.Minimum.Z; float distance = Vector3::Dot(plane.Normal, max); - if (distance + plane.D > Plane::DistanceEpsilon) return PlaneIntersectionType::Front; - distance = Vector3::Dot(plane.Normal, min); - if (distance + plane.D < Plane::DistanceEpsilon) return PlaneIntersectionType::Back; - return PlaneIntersectionType::Intersecting; } @@ -1012,10 +1088,8 @@ PlaneIntersectionType CollisionsHelper::PlaneIntersectsSphere(const Plane& plane if (distance > sphere.Radius) return PlaneIntersectionType::Front; - if (distance < -sphere.Radius) return PlaneIntersectionType::Back; - return PlaneIntersectionType::Intersecting; } @@ -1023,13 +1097,10 @@ bool CollisionsHelper::BoxIntersectsBox(const BoundingBox& box1, const BoundingB { if (box1.Minimum.X > box2.Maximum.X || box2.Minimum.X > box1.Maximum.X) return false; - if (box1.Minimum.Y > box2.Maximum.Y || box2.Minimum.Y > box1.Maximum.Y) return false; - if (box1.Minimum.Z > box2.Maximum.Z || box2.Minimum.Z > box1.Maximum.Z) return false; - return true; } diff --git a/Source/Engine/Core/Math/CollisionsHelper.h b/Source/Engine/Core/Math/CollisionsHelper.h index 33631a682..4bebf2492 100644 --- a/Source/Engine/Core/Math/CollisionsHelper.h +++ b/Source/Engine/Core/Math/CollisionsHelper.h @@ -72,6 +72,33 @@ public: /// When the method completes, contains the closest point between the two objects. static void ClosestPointPointLine(const Vector2& point, const Vector2& p0, const Vector2& p1, Vector2& result); + /// + /// Determines the closest point between a point and a line. + /// + /// The point to test. + /// The line first point. + /// The line second point. + /// The closest point between the two objects. + static Vector2 ClosestPointPointLine(const Vector2& point, const Vector2& p0, const Vector2& p1); + + /// + /// Determines the closest point between a point and a line. + /// + /// The point to test. + /// The line first point. + /// The line second point. + /// When the method completes, contains the closest point between the two objects. + static void ClosestPointPointLine(const Vector3& point, const Vector3& p0, const Vector3& p1, Vector3& result); + + /// + /// Determines the closest point between a point and a line. + /// + /// The point to test. + /// The line first point. + /// The line second point. + /// The closest point between the two objects. + static Vector3 ClosestPointPointLine(const Vector3& point, const Vector3& p0, const Vector3& p1); + /// /// Determines the closest point between a point and a triangle. /// @@ -82,6 +109,16 @@ public: /// When the method completes, contains the closest point between the two objects. static void ClosestPointPointTriangle(const Vector3& point, const Vector3& vertex1, const Vector3& vertex2, const Vector3& vertex3, Vector3& result); + /// + /// Determines the closest point between a point and a triangle. + /// + /// The point to test. + /// The first vertex to test. + /// The second vertex to test. + /// The third vertex to test. + /// The closest point between the two objects. + static Vector3 ClosestPointPointTriangle(const Vector3& point, const Vector3& vertex1, const Vector3& vertex2, const Vector3& vertex3); + /// /// Determines the closest point between a and a point. /// @@ -90,6 +127,14 @@ public: /// When the method completes, contains the closest point between the two objects. static void ClosestPointPlanePoint(const Plane& plane, const Vector3& point, Vector3& result); + /// + /// Determines the closest point between a and a point. + /// + /// The plane to test. + /// The point to test. + /// The closest point between the two objects. + static Vector3 ClosestPointPlanePoint(const Plane& plane, const Vector3& point); + /// /// Determines the closest point between a and a point. /// @@ -98,6 +143,14 @@ public: /// When the method completes, contains the closest point between the two objects. static void ClosestPointBoxPoint(const BoundingBox& box, const Vector3& point, Vector3& result); + /// + /// Determines the closest point between a and a point. + /// + /// The box to test. + /// The point to test. + /// The closest point between the two objects. + static Vector3 ClosestPointBoxPoint(const BoundingBox& box, const Vector3& point); + /// /// Determines the closest point between a and a point. /// @@ -106,6 +159,14 @@ public: /// When the method completes, contains the closest point between the two objects. static void ClosestPointRectanglePoint(const Rectangle& rect, const Vector2& point, Vector2& result); + /// + /// Determines the closest point between a and a point. + /// + /// The rectangle to test. + /// The point to test. + /// The closest point between the two objects. + static Vector2 ClosestPointRectanglePoint(const Rectangle& rect, const Vector2& point); + /// /// Determines the closest point between a and a point. /// @@ -114,6 +175,14 @@ public: /// When the method completes, contains the closest point between the two objects; or, if the point is directly in the center of the sphere, contains . static void ClosestPointSpherePoint(const BoundingSphere& sphere, const Vector3& point, Vector3& result); + /// + /// Determines the closest point between a and a point. + /// + /// The sphere to test. + /// The point to test. + /// The closest point between the two objects; or, if the point is directly in the center of the sphere, contains . + static Vector3 ClosestPointSpherePoint(const BoundingSphere& sphere, const Vector3& point); + /// /// Determines the closest point between a and a . /// @@ -127,6 +196,19 @@ public: /// static void ClosestPointSphereSphere(const BoundingSphere& sphere1, const BoundingSphere& sphere2, Vector3& result); + /// + /// Determines the closest point between a and a . + /// + /// The first sphere to test. + /// The second sphere to test. + /// The closest point between the two objects; or, if the point is directly in the center of the sphere, contains . + /// + /// If the two spheres are overlapping, but not directly on top of each other, the closest point + /// is the 'closest' point of intersection. This can also be considered is the deepest point of + /// intersection. + /// + static Vector3 ClosestPointSphereSphere(const BoundingSphere& sphere1, const BoundingSphere& sphere2); + /// /// Determines the distance between a and a point. /// diff --git a/Source/Engine/Core/Math/Color.cs b/Source/Engine/Core/Math/Color.cs index 5aee4c428..a865bde15 100644 --- a/Source/Engine/Core/Math/Color.cs +++ b/Source/Engine/Core/Math/Color.cs @@ -280,7 +280,7 @@ namespace FlaxEngine /// /// The hexadecimal string. /// Output value. - /// True if value has benn parsed, otherwise false. + /// True if value has been parsed, otherwise false. public static bool TryParseHex(string hexString, out Color value) { value = Black; diff --git a/Source/Engine/Core/Math/Half.cpp b/Source/Engine/Core/Math/Half.cpp index 47bf66802..0de29c030 100644 --- a/Source/Engine/Core/Math/Half.cpp +++ b/Source/Engine/Core/Math/Half.cpp @@ -18,81 +18,81 @@ Half4 Half4::Zero(0, 0, 0, 0); Half2::Half2(const Vector2& v) { - X = ConvertFloatToHalf(v.X); - Y = ConvertFloatToHalf(v.Y); + X = Float16Compressor::Compress(v.X); + Y = Float16Compressor::Compress(v.Y); } Vector2 Half2::ToVector2() const { return Vector2( - ConvertHalfToFloat(X), - ConvertHalfToFloat(Y) + Float16Compressor::Decompress(X), + Float16Compressor::Decompress(Y) ); } Half3::Half3(const Vector3& v) { - X = ConvertFloatToHalf(v.X); - Y = ConvertFloatToHalf(v.Y); - Z = ConvertFloatToHalf(v.Z); + X = Float16Compressor::Compress(v.X); + Y = Float16Compressor::Compress(v.Y); + Z = Float16Compressor::Compress(v.Z); } Vector3 Half3::ToVector3() const { return Vector3( - ConvertHalfToFloat(X), - ConvertHalfToFloat(Y), - ConvertHalfToFloat(Z) + Float16Compressor::Decompress(X), + Float16Compressor::Decompress(Y), + Float16Compressor::Decompress(Z) ); } Half4::Half4(const Vector4& v) { - X = ConvertFloatToHalf(v.X); - Y = ConvertFloatToHalf(v.Y); - Z = ConvertFloatToHalf(v.Z); - W = ConvertFloatToHalf(v.W); + X = Float16Compressor::Compress(v.X); + Y = Float16Compressor::Compress(v.Y); + Z = Float16Compressor::Compress(v.Z); + W = Float16Compressor::Compress(v.W); } Half4::Half4(const Color& c) { - X = ConvertFloatToHalf(c.R); - Y = ConvertFloatToHalf(c.G); - Z = ConvertFloatToHalf(c.B); - W = ConvertFloatToHalf(c.A); + X = Float16Compressor::Compress(c.R); + Y = Float16Compressor::Compress(c.G); + Z = Float16Compressor::Compress(c.B); + W = Float16Compressor::Compress(c.A); } Half4::Half4(const Rectangle& rect) { - X = ConvertFloatToHalf(rect.Location.X); - Y = ConvertFloatToHalf(rect.Location.Y); - Z = ConvertFloatToHalf(rect.Size.X); - W = ConvertFloatToHalf(rect.Size.Y); + X = Float16Compressor::Compress(rect.Location.X); + Y = Float16Compressor::Compress(rect.Location.Y); + Z = Float16Compressor::Compress(rect.Size.X); + W = Float16Compressor::Compress(rect.Size.Y); } Vector2 Half4::ToVector2() const { return Vector2( - ConvertHalfToFloat(X), - ConvertHalfToFloat(Y) + Float16Compressor::Decompress(X), + Float16Compressor::Decompress(Y) ); } Vector3 Half4::ToVector3() const { return Vector3( - ConvertHalfToFloat(X), - ConvertHalfToFloat(Y), - ConvertHalfToFloat(Z) + Float16Compressor::Decompress(X), + Float16Compressor::Decompress(Y), + Float16Compressor::Decompress(Z) ); } Vector4 Half4::ToVector4() const { return Vector4( - ConvertHalfToFloat(X), - ConvertHalfToFloat(Y), - ConvertHalfToFloat(Z), - ConvertHalfToFloat(W) + Float16Compressor::Decompress(X), + Float16Compressor::Decompress(Y), + Float16Compressor::Decompress(Z), + Float16Compressor::Decompress(W) ); } diff --git a/Source/Engine/Core/Math/Half.h b/Source/Engine/Core/Math/Half.h index 19059ad0c..e0773b1d4 100644 --- a/Source/Engine/Core/Math/Half.h +++ b/Source/Engine/Core/Math/Half.h @@ -11,8 +11,14 @@ typedef uint16 Half; #define USE_SSE_HALF_CONVERSION 0 -class Float16Compressor +/// +/// Utility for packing/unpacking floating point value from single precision (32 bit) to half precision (16 bit). +/// +class FLAXENGINE_API Float16Compressor { + // Reference: + // http://www.cs.cmu.edu/~jinlianw/third_party/float16_compressor.hpp + union Bits { float f; @@ -22,24 +28,19 @@ class Float16Compressor static const int shift = 13; static const int shiftSign = 16; - static const int32 infN = 0x7F800000; // flt32 infinity static const int32 maxN = 0x477FE000; // max flt16 normal as a flt32 static const int32 minN = 0x38800000; // min flt16 normal as a flt32 static const int32 signN = 0x80000000; // flt32 sign bit - static const int32 infC = infN >> shift; static const int32 nanN = (infC + 1) << shift; // minimum flt16 nan as a flt32 static const int32 maxC = maxN >> shift; static const int32 minC = minN >> shift; static const int32 signC = signN >> shiftSign; // flt16 sign bit - static const int32 mulN = 0x52000000; // (1 << 23) / minN static const int32 mulC = 0x33800000; // minN / (1 << (23 - shift)) - static const int32 subC = 0x003FF; // max flt32 subnormal down shifted static const int32 norC = 0x00400; // min flt32 normal down shifted - static const int32 maxD = infC - maxC - 1; static const int32 minD = minC - subC - 1; @@ -48,9 +49,9 @@ public: static Half Compress(const float value) { #if USE_SSE_HALF_CONVERSION - __m128 V1 = _mm_set_ss(value); - __m128i V2 = _mm_cvtps_ph(V1, 0); - return static_cast(_mm_cvtsi128_si32(V2)); + __m128 value1 = _mm_set_ss(value); + __m128i value2 = _mm_cvtps_ph(value1, 0); + return static_cast(_mm_cvtsi128_si32(value2)); #else Bits v, s; v.f = value; @@ -72,9 +73,9 @@ public: static float Decompress(const Half value) { #if USE_SSE_HALF_CONVERSION - __m128i V1 = _mm_cvtsi32_si128(static_cast(value)); - __m128 V2 = _mm_cvtph_ps(V1); - return _mm_cvtss_f32(V2); + __m128i value1 = _mm_cvtsi32_si128(static_cast(value)); + __m128 value2 = _mm_cvtph_ps(value1); + return _mm_cvtss_f32(value2); #else Bits v; v.ui = value; @@ -95,20 +96,10 @@ public: } }; -inline float ConvertHalfToFloat(const Half value) -{ - return Float16Compressor::Decompress(value); -} - -inline Half ConvertFloatToHalf(const float value) -{ - return Float16Compressor::Compress(value); -} - /// /// Defines a two component vector, using half precision floating point coordinates. /// -struct Half2 +struct FLAXENGINE_API Half2 { public: @@ -145,8 +136,8 @@ public: /// Y component Half2(float x, float y) { - X = ConvertFloatToHalf(x); - Y = ConvertFloatToHalf(y); + X = Float16Compressor::Compress(x); + Y = Float16Compressor::Compress(y); } /// @@ -167,7 +158,7 @@ public: /// /// Defines a three component vector, using half precision floating point coordinates. /// -struct Half3 +struct FLAXENGINE_API Half3 { public: @@ -201,9 +192,9 @@ public: Half3(const float x, const float y, const float z) { - X = ConvertFloatToHalf(x); - Y = ConvertFloatToHalf(y); - Z = ConvertFloatToHalf(z); + X = Float16Compressor::Compress(x); + Y = Float16Compressor::Compress(y); + Z = Float16Compressor::Compress(z); } Half3(const Vector3& v); @@ -216,7 +207,7 @@ public: /// /// Defines a four component vector, using half precision floating point coordinates. /// -struct Half4 +struct FLAXENGINE_API Half4 { public: @@ -255,31 +246,27 @@ public: Half4(const float x, const float y, const float z) { - X = ConvertFloatToHalf(x); - Y = ConvertFloatToHalf(y); - Z = ConvertFloatToHalf(z); + X = Float16Compressor::Compress(x); + Y = Float16Compressor::Compress(y); + Z = Float16Compressor::Compress(z); W = 0; } Half4(const float x, const float y, const float z, const float w) { - X = ConvertFloatToHalf(x); - Y = ConvertFloatToHalf(y); - Z = ConvertFloatToHalf(z); - W = ConvertFloatToHalf(w); + X = Float16Compressor::Compress(x); + Y = Float16Compressor::Compress(y); + Z = Float16Compressor::Compress(z); + W = Float16Compressor::Compress(w); } explicit Half4(const Vector4& v); - explicit Half4(const Color& c); - explicit Half4(const Rectangle& rect); public: Vector2 ToVector2() const; - Vector3 ToVector3() const; - Vector4 ToVector4() const; }; diff --git a/Source/Engine/Core/Math/Int2.h b/Source/Engine/Core/Math/Int2.h new file mode 100644 index 000000000..a6038d1b0 --- /dev/null +++ b/Source/Engine/Core/Math/Int2.h @@ -0,0 +1,279 @@ +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. + +#pragma once + +#include "Math.h" +#include "Engine/Core/Formatting.h" +#include "Engine/Core/Templates.h" + +struct Vector2; +struct Vector3; +struct Vector4; + +/// +/// Two-components vector (32 bit integer type). +/// +API_STRUCT(InBuild) struct FLAXENGINE_API Int2 +{ +public: + + union + { + struct + { + // X component + int32 X; + + // Y component + int32 Y; + }; + + // Raw values + int32 Raw[2]; + }; + +public: + + // Vector with all components equal 0 + static const Int2 Zero; + + // Vector with all components equal 1 + static const Int2 One; + +public: + + /// + /// Empty constructor. + /// + Int2() + { + } + + // Init + // @param xy Value to assign to the all components + Int2(int32 xy) + : X(xy) + , Y(xy) + { + } + + // Init + // @param x X component value + // @param y Y component value + Int2(int32 x, int32 y) + : X(x) + , Y(y) + { + } + + // Init + // @param v Vector to use X and Y components + explicit Int2(const Vector2& v); + +public: + + String ToString() const; + +public: + + // Arithmetic operators with Int2 + + Int2 operator+(const Int2& b) const + { + return Add(*this, b); + } + + Int2 operator-(const Int2& b) const + { + return Subtract(*this, b); + } + + Int2 operator*(const Int2& b) const + { + return Multiply(*this, b); + } + + Int2 operator/(const Int2& b) const + { + return Divide(*this, b); + } + + Int2 operator-() const + { + return Int2(-X, -Y); + } + + // op= operators with Int2 + + Int2& operator+=(const Int2& b) + { + *this = Add(*this, b); + return *this; + } + + Int2& operator-=(const Int2& b) + { + *this = Subtract(*this, b); + return *this; + } + + Int2& operator*=(const Int2& b) + { + *this = Multiply(*this, b); + return *this; + } + + Int2& operator/=(const Int2& b) + { + *this = Divide(*this, b); + return *this; + } + + // Arithmetic operators with int32 + + Int2 operator+(int32 b) const + { + return Add(*this, b); + } + + Int2 operator-(int32 b) const + { + return Subtract(*this, b); + } + + Int2 operator*(int32 b) const + { + return Multiply(*this, b); + } + + Int2 operator/(int32 b) const + { + return Divide(*this, b); + } + + // op= operators with int32 + + Int2& operator+=(int32 b) + { + *this = Add(*this, b); + return *this; + } + + Int2& operator-=(int32 b) + { + *this = Subtract(*this, b); + return *this; + } + + Int2& operator*=(int32 b) + { + *this = Multiply(*this, b); + return *this; + } + + Int2& operator/=(int32 b) + { + *this = Divide(*this, b); + return *this; + } + + // Comparison operators + + bool operator==(const Int2& b) const + { + return X == b.X && Y == b.Y; + } + + bool operator!=(const Int2& b) const + { + return X != b.X || Y != b.Y; + } + + bool operator>(const Int2& b) const + { + return X > b.X && Y > b.Y; + } + + bool operator>=(const Int2& b) const + { + return X >= b.X && Y >= b.Y; + } + + bool operator<(const Int2& b) const + { + return X < b.X && Y < b.Y; + } + + bool operator<=(const Int2& b) const + { + return X <= b.X && Y <= b.Y; + } + +public: + + static void Add(const Int2& a, const Int2& b, Int2* result) + { + result->X = a.X + b.X; + result->Y = a.Y + b.Y; + } + + static Int2 Add(const Int2& a, const Int2& b) + { + Int2 result; + Add(a, b, &result); + return result; + } + + static void Subtract(const Int2& a, const Int2& b, Int2* result) + { + result->X = a.X - b.X; + result->Y = a.Y - b.Y; + } + + static Int2 Subtract(const Int2& a, const Int2& b) + { + Int2 result; + Subtract(a, b, &result); + return result; + } + + static Int2 Multiply(const Int2& a, const Int2& b) + { + return Int2(a.X * b.X, a.Y * b.Y); + } + + static Int2 Multiply(const Int2& a, int32 b) + { + return Int2(a.X * b, a.Y * b); + } + + static Int2 Divide(const Int2& a, const Int2& b) + { + return Int2(a.X / b.X, a.Y / b.Y); + } + + static Int2 Divide(const Int2& a, int32 b) + { + return Int2(a.X / b, a.Y / b); + } + + // Creates vector from minimum components of two vectors + static Int2 Min(const Int2& a, const Int2& b) + { + return Int2(a.X < b.X ? a.X : b.X, a.Y < b.Y ? a.Y : b.Y); + } + + // Creates vector from maximum components of two vectors + static Int2 Max(const Int2& a, const Int2& b) + { + return Int2(a.X > b.X ? a.X : b.X, a.Y > b.Y ? a.Y : b.Y); + } +}; + +template<> +struct TIsPODType +{ + enum { Value = true }; +}; + +DEFINE_DEFAULT_FORMATTING(Int2, "X:{0} Y:{1}", v.X, v.Y); diff --git a/Source/Engine/Core/Math/Int3.h b/Source/Engine/Core/Math/Int3.h new file mode 100644 index 000000000..6886acc6b --- /dev/null +++ b/Source/Engine/Core/Math/Int3.h @@ -0,0 +1,128 @@ +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. + +#pragma once + +#include "Math.h" +#include "Engine/Core/Formatting.h" +#include "Engine/Core/Templates.h" + +struct Vector2; +struct Vector3; +struct Vector4; + +/// +/// Three-components vector (32 bit integer type). +/// +API_STRUCT(InBuild) struct FLAXENGINE_API Int3 +{ +public: + + union + { + struct + { + // X component + int32 X; + + // Y component + int32 Y; + + // Y component + int32 Z; + }; + + // Raw values + int32 Raw[3]; + }; + +public: + + // Vector with all components equal 0 + static const Int3 Zero; + + // Vector with all components equal 1 + static const Int3 One; + +public: + + /// + /// Empty constructor. + /// + Int3() + { + } + + // Init + // @param xy Value to assign to the all components + Int3(int32 xyz) + : X(xyz) + , Y(xyz) + , Z(xyz) + { + } + + // Init + // @param x X component value + // @param y Y component value + // @param z Z component value + Int3(int32 x, int32 y, int32 z) + : X(x) + , Y(y) + , Z(z) + { + } + + // Init + // @param v Vector to use X, Y and Z components + explicit Int3(const Vector3& v); + +public: + + String ToString() const; + +public: + + // Returns a vector containing the largest components of the specified vectors + // @param a The first source vector + // @param b The second source vector + // @param result When the method completes, contains an new vector composed of the largest components of the source vectors + static Int3 Max(const Int3& a, const Int3& b) + { + return Int3(a.X > b.X ? a.X : b.X, a.Y > b.Y ? a.Y : b.Y, a.Z > b.Z ? a.Z : b.Z); + } + + // Returns a vector containing the smallest components of the specified vectors + // @param a The first source vector + // @param b The second source vector + // @param result When the method completes, contains an new vector composed of the smallest components of the source vectors + static Int3 Min(const Int3& a, const Int3& b) + { + return Int3(a.X < b.X ? a.X : b.X, a.Y < b.Y ? a.Y : b.Y, a.Z < b.Z ? a.Z : b.Z); + } + + // Returns a vector containing the largest components of the specified vectors + // @param a The first source vector + // @param b The second source vector + // @param result When the method completes, contains an new vector composed of the largest components of the source vectors + static void Max(const Int3& a, const Int3& b, Int3* result) + { + *result = Int3(a.X > b.X ? a.X : b.X, a.Y > b.Y ? a.Y : b.Y, a.Z > b.Z ? a.Z : b.Z); + } + + // Returns a vector containing the smallest components of the specified vectors + // @param a The first source vector + // @param b The second source vector + // @param result When the method completes, contains an new vector composed of the smallest components of the source vectors + static void Min(const Int3& a, const Int3& b, Int3* result) + { + *result = Int3(a.X < b.X ? a.X : b.X, a.Y < b.Y ? a.Y : b.Y, a.Z < b.Z ? a.Z : b.Z); + } +}; + +template<> +struct TIsPODType +{ + enum { Value = true }; +}; + +DEFINE_DEFAULT_FORMATTING(Int3, "X:{0} Y:{1} Z:{2}", v.X, v.Y, v.Z); diff --git a/Source/Engine/Core/Math/Int4.h b/Source/Engine/Core/Math/Int4.h new file mode 100644 index 000000000..c8e9eebd8 --- /dev/null +++ b/Source/Engine/Core/Math/Int4.h @@ -0,0 +1,134 @@ +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. + +#pragma once + +#include "Math.h" +#include "Engine/Core/Formatting.h" +#include "Engine/Core/Templates.h" + +struct Vector2; +struct Vector3; +struct Vector4; + +/// +/// Four-components vector (32 bit integer type). +/// +API_STRUCT(InBuild) struct FLAXENGINE_API Int4 +{ +public: + + union + { + struct + { + // X component + int32 X; + + // Y component + int32 Y; + + // Z component + int32 Z; + + // W component + int32 W; + }; + + // Raw values + int32 Raw[4]; + }; + +public: + + // Vector with all components equal 0 + static const Int4 Zero; + + // Vector with all components equal 1 + static const Int4 One; + +public: + + /// + /// Empty constructor. + /// + Int4() + { + } + + // Init + // @param xy Value to assign to the all components + Int4(int32 xyzw) + : X(xyzw) + , Y(xyzw) + , Z(xyzw) + , W(xyzw) + { + } + + // Init + // @param x X component value + // @param y Y component value + // @param z Z component value + // @param w W component value + Int4(int32 x, int32 y, int32 z, int32 w) + : X(x) + , Y(y) + , Z(z) + , W(w) + { + } + + // Init + // @param v Vector to use X, Y, Z and W components + explicit Int4(const Vector4& v); + +public: + + String ToString() const; + +public: + + /// + /// Returns average arithmetic of all the components + /// + /// Average arithmetic of all the components + float AverageArithmetic() const + { + return (X + Y + Z + W) * 0.25f; + } + + /// + /// Gets sum of all vector components values + /// + /// Sum of X, Y, Z and W + int32 SumValues() const + { + return X + Y + Z + W; + } + + /// + /// Returns minimum value of all the components + /// + /// Minimum value + int32 MinValue() const + { + return Math::Min(X, Y, Z, W); + } + + /// + /// Returns maximum value of all the components + /// + /// Maximum value + int32 MaxValue() const + { + return Math::Max(X, Y, Z, W); + } +}; + +template<> +struct TIsPODType +{ + enum { Value = true }; +}; + +DEFINE_DEFAULT_FORMATTING(Int4, "X:{0} Y:{1} Z:{2} W:{3}", v.X, v.Y, v.Z, v.W); diff --git a/Source/Engine/Core/Math/Mathf.cs b/Source/Engine/Core/Math/Mathf.cs index 7286573bf..529bd6ab0 100644 --- a/Source/Engine/Core/Math/Mathf.cs +++ b/Source/Engine/Core/Math/Mathf.cs @@ -36,6 +36,11 @@ namespace FlaxEngine /// public const float PiOverFour = (float)(Math.PI / 4); + /// + /// A value specifying the golden mean + /// + public const float GoldenRatio = 1.6180339887f; + /// /// Returns the absolute value of f. /// diff --git a/Source/Engine/Core/Math/Matrix3x3.h b/Source/Engine/Core/Math/Matrix3x3.h index 3cf690259..b28be5437 100644 --- a/Source/Engine/Core/Math/Matrix3x3.h +++ b/Source/Engine/Core/Math/Matrix3x3.h @@ -9,7 +9,7 @@ /// /// Represents a 3x3 mathematical matrix. /// -API_STRUCT(InBuild) struct Matrix3x3 +API_STRUCT(InBuild) struct FLAXENGINE_API Matrix3x3 { public: diff --git a/Source/Engine/Core/Math/OrientedBoundingBox.h b/Source/Engine/Core/Math/OrientedBoundingBox.h index 03256633d..d19108f84 100644 --- a/Source/Engine/Core/Math/OrientedBoundingBox.h +++ b/Source/Engine/Core/Math/OrientedBoundingBox.h @@ -8,7 +8,7 @@ #include "CollisionsHelper.h" // Oriented Bounding Box (OBB) is a rectangular block, much like an AABB (Bounding Box) but with an arbitrary orientation in 3D space. -API_STRUCT(InBuild) struct OrientedBoundingBox +API_STRUCT(InBuild) struct FLAXENGINE_API OrientedBoundingBox { public: diff --git a/Source/Engine/Core/Math/Packed.h b/Source/Engine/Core/Math/Packed.h index ec666bf61..4f41dac9c 100644 --- a/Source/Engine/Core/Math/Packed.h +++ b/Source/Engine/Core/Math/Packed.h @@ -13,7 +13,7 @@ typedef Half Float16; /// /// Packed vector, layout: R:10 bytes, G:10 bytes, B:10 bytes, A:2 bytes, all values are stored as floats in range [0;1]. /// -struct Float1010102 +struct FLAXENGINE_API Float1010102 { union { @@ -64,7 +64,7 @@ public: }; // The 3D vector is packed into 32 bits with 11/11/10 bits per floating-point component. -struct FloatR11G11B10 +struct FLAXENGINE_API FloatR11G11B10 { union { @@ -118,7 +118,7 @@ public: Vector3 ToVector3() const; }; -struct RG16UNorm +struct FLAXENGINE_API RG16UNorm { uint16 X, Y; @@ -131,7 +131,7 @@ struct RG16UNorm Vector2 ToVector2() const; }; -struct RGBA16UNorm +struct FLAXENGINE_API RGBA16UNorm { uint16 X, Y, Z, W; diff --git a/Source/Engine/Core/Math/Plane.h b/Source/Engine/Core/Math/Plane.h index 2ca34258f..2fbe7438c 100644 --- a/Source/Engine/Core/Math/Plane.h +++ b/Source/Engine/Core/Math/Plane.h @@ -8,7 +8,7 @@ /// /// Represents a plane in three dimensional space. /// -API_STRUCT() struct Plane +API_STRUCT() struct FLAXENGINE_API Plane { DECLARE_SCRIPTING_TYPE_MINIMAL(Plane); public: diff --git a/Source/Engine/Core/Math/Ray.h b/Source/Engine/Core/Math/Ray.h index 56d06a21e..f125cba97 100644 --- a/Source/Engine/Core/Math/Ray.h +++ b/Source/Engine/Core/Math/Ray.h @@ -11,7 +11,7 @@ struct Viewport; /// /// Represents a three dimensional line based on a point in space and a direction. /// -API_STRUCT() struct Ray +API_STRUCT() struct FLAXENGINE_API Ray { DECLARE_SCRIPTING_TYPE_MINIMAL(Ray); public: diff --git a/Source/Engine/Core/Math/Rectangle.h b/Source/Engine/Core/Math/Rectangle.h index 305ae70e1..f2dcbd20a 100644 --- a/Source/Engine/Core/Math/Rectangle.h +++ b/Source/Engine/Core/Math/Rectangle.h @@ -10,7 +10,6 @@ API_STRUCT() struct FLAXENGINE_API Rectangle { DECLARE_SCRIPTING_TYPE_MINIMAL(Rectangle); -public: /// /// The empty rectangle. diff --git a/Source/Engine/Core/Math/Transform.h b/Source/Engine/Core/Math/Transform.h index 3b8cf71b7..ed19efeae 100644 --- a/Source/Engine/Core/Math/Transform.h +++ b/Source/Engine/Core/Math/Transform.h @@ -11,10 +11,9 @@ struct Matrix; /// /// Describes transformation in a 3D space. /// -API_STRUCT() struct Transform +API_STRUCT() struct FLAXENGINE_API Transform { DECLARE_SCRIPTING_TYPE_MINIMAL(Transform); -public: /// /// The translation vector of the transform. diff --git a/Source/Engine/Core/Math/Triangle.h b/Source/Engine/Core/Math/Triangle.h index 03f91ece3..b0f7ea09d 100644 --- a/Source/Engine/Core/Math/Triangle.h +++ b/Source/Engine/Core/Math/Triangle.h @@ -8,7 +8,7 @@ /// /// Represents a three dimensional triangle. /// -struct Triangle +struct FLAXENGINE_API Triangle { public: diff --git a/Source/Engine/Core/Math/Vector2.cpp b/Source/Engine/Core/Math/Vector2.cpp index 6a0468033..c23e79d19 100644 --- a/Source/Engine/Core/Math/Vector2.cpp +++ b/Source/Engine/Core/Math/Vector2.cpp @@ -4,8 +4,8 @@ #include "Vector3.h" #include "Vector4.h" #include "Color.h" +#include "Int2.h" #include "../Types/String.h" -#include "VectorInt.h" static_assert(sizeof(Vector2) == 8, "Invalid Vector2 type size."); diff --git a/Source/Engine/Core/Math/Vector3.cpp b/Source/Engine/Core/Math/Vector3.cpp index e3ff349ba..f66bbcbbd 100644 --- a/Source/Engine/Core/Math/Vector3.cpp +++ b/Source/Engine/Core/Math/Vector3.cpp @@ -6,7 +6,7 @@ #include "Color.h" #include "Quaternion.h" #include "Matrix.h" -#include "VectorInt.h" +#include "Int3.h" #include "../Types/String.h" static_assert(sizeof(Vector3) == 12, "Invalid Vector3 type size."); diff --git a/Source/Engine/Core/Math/Vector3.cs b/Source/Engine/Core/Math/Vector3.cs index db27cf8ed..a57fcb224 100644 --- a/Source/Engine/Core/Math/Vector3.cs +++ b/Source/Engine/Core/Math/Vector3.cs @@ -1221,7 +1221,7 @@ namespace FlaxEngine float dot = Mathf.Clamp(Dot(from.Normalized, to.Normalized), -1F, 1F); if (Mathf.Abs(dot) > (1F - Mathf.Epsilon)) return dot > 0F ? 0F : 180F; - return Mathf.Acos(dot) * Mathf.DegreesToRadians; + return Mathf.Acos(dot) * Mathf.RadiansToDegrees; } /// diff --git a/Source/Engine/Core/Math/Vector4.cpp b/Source/Engine/Core/Math/Vector4.cpp index 3caaab985..34fa27aac 100644 --- a/Source/Engine/Core/Math/Vector4.cpp +++ b/Source/Engine/Core/Math/Vector4.cpp @@ -6,7 +6,7 @@ #include "Color.h" #include "Matrix.h" #include "Rectangle.h" -#include "VectorInt.h" +#include "Int4.h" #include "../Types/String.h" static_assert(sizeof(Vector4) == 16, "Invalid Vector4 type size."); diff --git a/Source/Engine/Core/Math/VectorInt.h b/Source/Engine/Core/Math/VectorInt.h index 80804c39a..d9611d615 100644 --- a/Source/Engine/Core/Math/VectorInt.h +++ b/Source/Engine/Core/Math/VectorInt.h @@ -2,518 +2,6 @@ #pragma once -#include "Math.h" -#include "Engine/Core/Formatting.h" -#include "Engine/Core/Templates.h" - -struct Vector2; -struct Vector3; -struct Vector4; - -/// -/// Two-components vector (32 bit integer type). -/// -API_STRUCT(InBuild) struct Int2 -{ -public: - - union - { - struct - { - // X component - int32 X; - - // Y component - int32 Y; - }; - - // Raw values - int32 Raw[2]; - }; - -public: - - // Vector with all components equal 0 - static const Int2 Zero; - - // Vector with all components equal 1 - static const Int2 One; - -public: - - /// - /// Empty constructor. - /// - Int2() - { - } - - // Init - // @param xy Value to assign to the all components - Int2(int32 xy) - : X(xy) - , Y(xy) - { - } - - // Init - // @param x X component value - // @param y Y component value - Int2(int32 x, int32 y) - : X(x) - , Y(y) - { - } - - // Init - // @param v Vector to use X and Y components - explicit Int2(const Vector2& v); - -public: - - String ToString() const; - -public: - - // Arithmetic operators with Int2 - - Int2 operator+(const Int2& b) const - { - return Add(*this, b); - } - - Int2 operator-(const Int2& b) const - { - return Subtract(*this, b); - } - - Int2 operator*(const Int2& b) const - { - return Multiply(*this, b); - } - - Int2 operator/(const Int2& b) const - { - return Divide(*this, b); - } - - Int2 operator-() const - { - return Int2(-X, -Y); - } - - // op= operators with Int2 - - Int2& operator+=(const Int2& b) - { - *this = Add(*this, b); - return *this; - } - - Int2& operator-=(const Int2& b) - { - *this = Subtract(*this, b); - return *this; - } - - Int2& operator*=(const Int2& b) - { - *this = Multiply(*this, b); - return *this; - } - - Int2& operator/=(const Int2& b) - { - *this = Divide(*this, b); - return *this; - } - - // Arithmetic operators with int32 - - Int2 operator+(int32 b) const - { - return Add(*this, b); - } - - Int2 operator-(int32 b) const - { - return Subtract(*this, b); - } - - Int2 operator*(int32 b) const - { - return Multiply(*this, b); - } - - Int2 operator/(int32 b) const - { - return Divide(*this, b); - } - - // op= operators with int32 - - Int2& operator+=(int32 b) - { - *this = Add(*this, b); - return *this; - } - - Int2& operator-=(int32 b) - { - *this = Subtract(*this, b); - return *this; - } - - Int2& operator*=(int32 b) - { - *this = Multiply(*this, b); - return *this; - } - - Int2& operator/=(int32 b) - { - *this = Divide(*this, b); - return *this; - } - - // Comparison operators - - bool operator==(const Int2& b) const - { - return X == b.X && Y == b.Y; - } - - bool operator!=(const Int2& b) const - { - return X != b.X || Y != b.Y; - } - - bool operator>(const Int2& b) const - { - return X > b.X && Y > b.Y; - } - - bool operator>=(const Int2& b) const - { - return X >= b.X && Y >= b.Y; - } - - bool operator<(const Int2& b) const - { - return X < b.X && Y < b.Y; - } - - bool operator<=(const Int2& b) const - { - return X <= b.X && Y <= b.Y; - } - -public: - - static void Add(const Int2& a, const Int2& b, Int2* result) - { - result->X = a.X + b.X; - result->Y = a.Y + b.Y; - } - - static Int2 Add(const Int2& a, const Int2& b) - { - Int2 result; - Add(a, b, &result); - return result; - } - - static void Subtract(const Int2& a, const Int2& b, Int2* result) - { - result->X = a.X - b.X; - result->Y = a.Y - b.Y; - } - - static Int2 Subtract(const Int2& a, const Int2& b) - { - Int2 result; - Subtract(a, b, &result); - return result; - } - - static Int2 Multiply(const Int2& a, const Int2& b) - { - return Int2(a.X * b.X, a.Y * b.Y); - } - - static Int2 Multiply(const Int2& a, int32 b) - { - return Int2(a.X * b, a.Y * b); - } - - static Int2 Divide(const Int2& a, const Int2& b) - { - return Int2(a.X / b.X, a.Y / b.Y); - } - - static Int2 Divide(const Int2& a, int32 b) - { - return Int2(a.X / b, a.Y / b); - } - - // Creates vector from minimum components of two vectors - static Int2 Min(const Int2& a, const Int2& b) - { - return Int2(a.X < b.X ? a.X : b.X, a.Y < b.Y ? a.Y : b.Y); - } - - // Creates vector from maximum components of two vectors - static Int2 Max(const Int2& a, const Int2& b) - { - return Int2(a.X > b.X ? a.X : b.X, a.Y > b.Y ? a.Y : b.Y); - } -}; - -/// -/// Three-components vector (32 bit integer type). -/// -API_STRUCT(InBuild) struct Int3 -{ -public: - - union - { - struct - { - // X component - int32 X; - - // Y component - int32 Y; - - // Y component - int32 Z; - }; - - // Raw values - int32 Raw[3]; - }; - -public: - - // Vector with all components equal 0 - static const Int3 Zero; - - // Vector with all components equal 1 - static const Int3 One; - -public: - - /// - /// Empty constructor. - /// - Int3() - { - } - - // Init - // @param xy Value to assign to the all components - Int3(int32 xyz) - : X(xyz) - , Y(xyz) - , Z(xyz) - { - } - - // Init - // @param x X component value - // @param y Y component value - // @param z Z component value - Int3(int32 x, int32 y, int32 z) - : X(x) - , Y(y) - , Z(z) - { - } - - // Init - // @param v Vector to use X, Y and Z components - explicit Int3(const Vector3& v); - -public: - - String ToString() const; - -public: - - // Returns a vector containing the largest components of the specified vectors - // @param a The first source vector - // @param b The second source vector - // @param result When the method completes, contains an new vector composed of the largest components of the source vectors - static Int3 Max(const Int3& a, const Int3& b) - { - return Int3(a.X > b.X ? a.X : b.X, a.Y > b.Y ? a.Y : b.Y, a.Z > b.Z ? a.Z : b.Z); - } - - // Returns a vector containing the smallest components of the specified vectors - // @param a The first source vector - // @param b The second source vector - // @param result When the method completes, contains an new vector composed of the smallest components of the source vectors - static Int3 Min(const Int3& a, const Int3& b) - { - return Int3(a.X < b.X ? a.X : b.X, a.Y < b.Y ? a.Y : b.Y, a.Z < b.Z ? a.Z : b.Z); - } - - // Returns a vector containing the largest components of the specified vectors - // @param a The first source vector - // @param b The second source vector - // @param result When the method completes, contains an new vector composed of the largest components of the source vectors - static void Max(const Int3& a, const Int3& b, Int3* result) - { - *result = Int3(a.X > b.X ? a.X : b.X, a.Y > b.Y ? a.Y : b.Y, a.Z > b.Z ? a.Z : b.Z); - } - - // Returns a vector containing the smallest components of the specified vectors - // @param a The first source vector - // @param b The second source vector - // @param result When the method completes, contains an new vector composed of the smallest components of the source vectors - static void Min(const Int3& a, const Int3& b, Int3* result) - { - *result = Int3(a.X < b.X ? a.X : b.X, a.Y < b.Y ? a.Y : b.Y, a.Z < b.Z ? a.Z : b.Z); - } -}; - -/// -/// Four-components vector (32 bit integer type). -/// -API_STRUCT(InBuild) struct Int4 -{ -public: - - union - { - struct - { - // X component - int32 X; - - // Y component - int32 Y; - - // Z component - int32 Z; - - // W component - int32 W; - }; - - // Raw values - int32 Raw[4]; - }; - -public: - - // Vector with all components equal 0 - static const Int4 Zero; - - // Vector with all components equal 1 - static const Int4 One; - -public: - - /// - /// Empty constructor. - /// - Int4() - { - } - - // Init - // @param xy Value to assign to the all components - Int4(int32 xyzw) - : X(xyzw) - , Y(xyzw) - , Z(xyzw) - , W(xyzw) - { - } - - // Init - // @param x X component value - // @param y Y component value - // @param z Z component value - // @param w W component value - Int4(int32 x, int32 y, int32 z, int32 w) - : X(x) - , Y(y) - , Z(z) - , W(w) - { - } - - // Init - // @param v Vector to use X, Y, Z and W components - explicit Int4(const Vector4& v); - -public: - - String ToString() const; - -public: - - /// - /// Returns average arithmetic of all the components - /// - /// Average arithmetic of all the components - float AverageArithmetic() const - { - return (X + Y + Z + W) * 0.25f; - } - - /// - /// Gets sum of all vector components values - /// - /// Sum of X, Y, Z and W - int32 SumValues() const - { - return X + Y + Z + W; - } - - /// - /// Returns minimum value of all the components - /// - /// Minimum value - int32 MinValue() const - { - return Math::Min(X, Y, Z, W); - } - - /// - /// Returns maximum value of all the components - /// - /// Maximum value - int32 MaxValue() const - { - return Math::Max(X, Y, Z, W); - } -}; - -template<> -struct TIsPODType -{ - enum { Value = true }; -}; - -template<> -struct TIsPODType -{ - enum { Value = true }; -}; - -template<> -struct TIsPODType -{ - enum { Value = true }; -}; - -DEFINE_DEFAULT_FORMATTING(Int2, "X:{0} Y:{1}", v.X, v.Y); - -DEFINE_DEFAULT_FORMATTING(Int3, "X:{0} Y:{1} Z:{2}", v.X, v.Y, v.Z); - -DEFINE_DEFAULT_FORMATTING(Int4, "X:{0} Y:{1} Z:{2} W:{3}", v.X, v.Y, v.Z, v.W); +#include "Int2.h" +#include "Int3.h" +#include "Int4.h" diff --git a/Source/Engine/Core/Math/Viewport.h b/Source/Engine/Core/Math/Viewport.h index 0b5871f2e..e30d085d3 100644 --- a/Source/Engine/Core/Math/Viewport.h +++ b/Source/Engine/Core/Math/Viewport.h @@ -10,7 +10,7 @@ struct Matrix; struct Rectangle; // Describes the viewport dimensions. -API_STRUCT(InBuild) struct Viewport +API_STRUCT(InBuild) struct FLAXENGINE_API Viewport { public: diff --git a/Source/Engine/Core/ObjectsRemovalService.cpp b/Source/Engine/Core/ObjectsRemovalService.cpp index a36265646..f9d26f808 100644 --- a/Source/Engine/Core/ObjectsRemovalService.cpp +++ b/Source/Engine/Core/ObjectsRemovalService.cpp @@ -193,7 +193,7 @@ void ObjectsRemovalServiceService::Dispose() // Collect new objects ObjectsRemovalService::Flush(); - // Delete all reaming objects + // Delete all remaining objects { ScopeLock lock(PoolLocker); for (auto i = Pool.Begin(); i.IsNotEnd(); ++i) diff --git a/Source/Engine/Core/Types/Variant.cpp b/Source/Engine/Core/Types/Variant.cpp index 62c791875..db71c2f77 100644 --- a/Source/Engine/Core/Types/Variant.cpp +++ b/Source/Engine/Core/Types/Variant.cpp @@ -2612,7 +2612,8 @@ Variant Variant::Lerp(const Variant& a, const Variant& b, float alpha) void Variant::AllocStructure() { - const ScriptingTypeHandle typeHandle = Scripting::FindScriptingType(StringAnsiView(Type.TypeName)); + const StringAnsiView typeName(Type.TypeName); + const ScriptingTypeHandle typeHandle = Scripting::FindScriptingType(typeName); if (typeHandle) { const ScriptingType& type = typeHandle.GetType(); @@ -2620,8 +2621,26 @@ void Variant::AllocStructure() AsBlob.Data = Allocator::Allocate(AsBlob.Length); type.Struct.Ctor(AsBlob.Data); } + else if (typeName == "System.Byte") + { + // Hack for byte + AsBlob.Length = 1; + AsBlob.Data = Allocator::Allocate(AsBlob.Length); + *((byte*)AsBlob.Data) = 0; + } + else if (typeName == "System.Int16" || typeName == "System.UInt16") + { + // Hack for 16bit int + AsBlob.Length = 2; + AsBlob.Data = Allocator::Allocate(AsBlob.Length); + *((int16*)AsBlob.Data) = 0; + } else { + if (typeName.Length() != 0) + { + LOG(Warning, "Missing scripting type \'{0}\'", String(typeName.Get())); + } AsBlob.Data = nullptr; AsBlob.Length = 0; } @@ -2637,6 +2656,10 @@ void Variant::CopyStructure(void* src) auto& type = typeHandle.GetType(); type.Struct.Copy(AsBlob.Data, src); } + else + { + Platform::MemoryCopy(AsBlob.Data, src, AsBlob.Length); + } } } diff --git a/Source/Engine/Debug/DebugDraw.cpp b/Source/Engine/Debug/DebugDraw.cpp index 457c90411..f954cd7cb 100644 --- a/Source/Engine/Debug/DebugDraw.cpp +++ b/Source/Engine/Debug/DebugDraw.cpp @@ -19,6 +19,7 @@ #include "Engine/Graphics/RenderBuffers.h" #include "Engine/Animations/AnimationUtils.h" #include "Engine/Profiler/Profiler.h" +#include "Engine/Debug/DebugLog.h" // Debug draw service configuration #define DEBUG_DRAW_INITIAL_VB_CAPACITY (4 * 1024) @@ -629,7 +630,11 @@ void DebugDraw::DrawLine(const Vector3& start, const Vector3& end, const Color& void DebugDraw::DrawLines(const Span& lines, const Matrix& transform, const Color& color, float duration, bool depthTest) { - ASSERT(lines.Length() % 2 == 0); + if (lines.Length() % 2 != 0) + { + DebugLog::ThrowException("Cannot draw debug lines with uneven amount of items in array"); + return; + } // Create draw call entry DebugLine l = { Vector3::Zero, Vector3::Zero, Color32(color), duration }; @@ -637,10 +642,12 @@ void DebugDraw::DrawLines(const Span& lines, const Matrix& transform, c // Add lines const Vector3* p = lines.Get(); Array* list; - if (depthTest) + + if (depthTest) list = duration > 0 ? &DebugDrawDepthTest.DefaultLines : &DebugDrawDepthTest.OneFrameLines; else list = duration > 0 ? &DebugDrawDefault.DefaultLines : &DebugDrawDefault.OneFrameLines; + list->EnsureCapacity(list->Count() + lines.Length()); for (int32 i = 0; i < lines.Length(); i += 2) { diff --git a/Source/Engine/Debug/Exception.h b/Source/Engine/Debug/Exception.h index 481c78589..5bef7aa55 100644 --- a/Source/Engine/Debug/Exception.h +++ b/Source/Engine/Debug/Exception.h @@ -36,7 +36,7 @@ namespace Log /// /// Additional information that help describe error Exception(const String& additionalInfo) - : Exception(TEXT("An exception has occured."), additionalInfo) + : Exception(TEXT("An exception has occurred."), additionalInfo) { } diff --git a/Source/Engine/Engine/Engine.cpp b/Source/Engine/Engine/Engine.cpp index ebf268957..140f1b4a0 100644 --- a/Source/Engine/Engine/Engine.cpp +++ b/Source/Engine/Engine/Engine.cpp @@ -463,7 +463,7 @@ void Engine::OnExit() LOG_FLUSH(); - // Kill all reaming threads + // Kill all remaining threads ThreadRegistry::KillEmAll(); // Cleanup diff --git a/Source/Engine/Graphics/Enums.h b/Source/Engine/Graphics/Enums.h index 20836b300..8aa88756a 100644 --- a/Source/Engine/Graphics/Enums.h +++ b/Source/Engine/Graphics/Enums.h @@ -985,7 +985,7 @@ API_ENUM() enum class TessellationMethod enum class ShaderFlags : uint32 { /// - /// The default set fo flags. + /// The default set for flags. /// Default = 0, diff --git a/Source/Engine/Graphics/GPUBuffer.h b/Source/Engine/Graphics/GPUBuffer.h index 84611419e..97be261a4 100644 --- a/Source/Engine/Graphics/GPUBuffer.h +++ b/Source/Engine/Graphics/GPUBuffer.h @@ -215,7 +215,7 @@ public: /// Gets a CPU pointer to the resource by mapping its contents. Denies the GPU access to that resource. /// /// The map operation mode. - /// The pointer ot the mapped CPU buffer with resource data or null if failed. + /// The pointer of the mapped CPU buffer with resource data or null if failed. API_FUNCTION() virtual void* Map(GPUResourceMapMode mode) = 0; /// diff --git a/Source/Engine/Graphics/GPUDevice.cpp b/Source/Engine/Graphics/GPUDevice.cpp index a5195ff50..47ec6d3a6 100644 --- a/Source/Engine/Graphics/GPUDevice.cpp +++ b/Source/Engine/Graphics/GPUDevice.cpp @@ -239,7 +239,7 @@ void GPUDevice::preDispose() SAFE_DELETE_GPU_RESOURCE(_res->FullscreenTriangleVB); // Release GPU resources memory and unlink from device - // Note: after that noe GPU resources should be used/created, only deleted + // Note: after that no GPU resources should be used/created, only deleted Resources.OnDeviceDispose(); } diff --git a/Source/Engine/Graphics/GPULimits.h b/Source/Engine/Graphics/GPULimits.h index 485f5d866..b50714fcd 100644 --- a/Source/Engine/Graphics/GPULimits.h +++ b/Source/Engine/Graphics/GPULimits.h @@ -174,10 +174,10 @@ API_ENUM(Attributes="Flags") enum class FormatSupport : int32 DECLARE_ENUM_OPERATORS(FormatSupport); // Helper macro to check if given format does not support a given set of feature flags -#define FORMAT_FEATURES_ARE_NOT_SUPPORTED(formatSupport, formatSupportFlags) ((formatSupport & formatSupportFlags) != static_cast(formatSupportFlags)) +#define FORMAT_FEATURES_ARE_NOT_SUPPORTED(formatSupport, formatSupportFlags) ((formatSupport & (formatSupportFlags)) != static_cast(formatSupportFlags)) // Helper macro to check if given format does support a given set of feature flags -#define FORMAT_FEATURES_ARE_SUPPORTED(formatSupport, formatSupportFlags) ((formatSupport & formatSupportFlags) == static_cast(formatSupportFlags)) +#define FORMAT_FEATURES_ARE_SUPPORTED(formatSupport, formatSupportFlags) ((formatSupport & (formatSupportFlags)) == static_cast(formatSupportFlags)) /// /// The features exposed for a particular format. diff --git a/Source/Engine/Graphics/GPUSwapChain.cpp b/Source/Engine/Graphics/GPUSwapChain.cpp index 347fa6e46..68b1486ad 100644 --- a/Source/Engine/Graphics/GPUSwapChain.cpp +++ b/Source/Engine/Graphics/GPUSwapChain.cpp @@ -39,7 +39,7 @@ Task* GPUSwapChain::DownloadDataAsync(TextureData& result) { if (_downloadTask) { - LOG(Warning, "Can download window backuffer data ony once at the time."); + LOG(Warning, "Can download window backuffer data only once at the time."); return nullptr; } diff --git a/Source/Engine/Graphics/Graphics.cpp b/Source/Engine/Graphics/Graphics.cpp index 21b506ef1..69170dca0 100644 --- a/Source/Engine/Graphics/Graphics.cpp +++ b/Source/Engine/Graphics/Graphics.cpp @@ -49,6 +49,9 @@ GraphicsService GraphicsServiceInstance; void Graphics::DisposeDevice() { + // Clean any danging pointer to last task (might stay if engine is disposing after crash) + GPUDevice::Instance->CurrentTask = nullptr; + if (GPUDevice::Instance) { GPUDevice::Instance->Dispose(); diff --git a/Source/Engine/Graphics/Materials/IMaterial.h b/Source/Engine/Graphics/Materials/IMaterial.h index 8d5590354..09796c04b 100644 --- a/Source/Engine/Graphics/Materials/IMaterial.h +++ b/Source/Engine/Graphics/Materials/IMaterial.h @@ -108,7 +108,7 @@ public: /// /// Gets the mask of render passes supported by this material. /// - /// The drw passes supported by this material. + /// The draw passes supported by this material. virtual DrawPass GetDrawModes() const { return DrawPass::None; diff --git a/Source/Engine/Graphics/Mesh.cs b/Source/Engine/Graphics/Mesh.cs index f639bff8f..f96b4ce63 100644 --- a/Source/Engine/Graphics/Mesh.cs +++ b/Source/Engine/Graphics/Mesh.cs @@ -117,7 +117,7 @@ namespace FlaxEngine /// Mesh data will be cached and uploaded to the GPU with a delay. /// /// The mesh vertices positions. Cannot be null. - /// The mesh index buffer (triangles). Uses 32-bit stride buffer. Cannot be null. + /// The mesh index buffer (clockwise triangles). Uses 32-bit stride buffer. Cannot be null. /// The normal vectors (per vertex). /// The normal vectors (per vertex). Use null to compute them from normal vectors. /// The texture coordinates (per vertex). @@ -163,7 +163,7 @@ namespace FlaxEngine /// Mesh data will be cached and uploaded to the GPU with a delay. /// /// The mesh vertices positions. Cannot be null. - /// The mesh index buffer (triangles). Uses 32-bit stride buffer. Cannot be null. + /// The mesh index buffer (clockwise triangles). Uses 32-bit stride buffer. Cannot be null. /// The normal vectors (per vertex). /// The normal vectors (per vertex). Use null to compute them from normal vectors. /// The texture coordinates (per vertex). @@ -210,7 +210,7 @@ namespace FlaxEngine /// Mesh data will be cached and uploaded to the GPU with a delay. /// /// The mesh vertices positions. Cannot be null. - /// The mesh index buffer (triangles). Uses 16-bit stride buffer. Cannot be null. + /// The mesh index buffer (clockwise triangles). Uses 16-bit stride buffer. Cannot be null. /// The normal vectors (per vertex). /// The tangent vectors (per vertex). Use null to compute them from normal vectors. /// The texture coordinates (per vertex). @@ -257,7 +257,7 @@ namespace FlaxEngine /// Mesh data will be cached and uploaded to the GPU with a delay. /// /// The mesh vertices positions. Cannot be null. - /// The mesh index buffer (triangles). Uses 16-bit stride buffer. Cannot be null. + /// The mesh index buffer (clockwise triangles). Uses 16-bit stride buffer. Cannot be null. /// The normal vectors (per vertex). /// The tangent vectors (per vertex). Use null to compute them from normal vectors. /// The texture coordinates (per vertex). @@ -435,7 +435,7 @@ namespace FlaxEngine /// Downloads the third vertex buffer that contains mesh vertices data. To download data from GPU set to true and call this method from the thread other than main thread (see ). /// /// - /// If mesh has no vertex colors (stored in vertex buffer 2) the the returned value is null. + /// If mesh has no vertex colors (stored in vertex buffer 2) the returned value is null. /// /// If set to true the data will be downloaded from the GPU, otherwise it can be loaded from the drive (source asset file) or from memory (if cached). Downloading mesh from GPU requires this call to be made from the other thread than main thread. Virtual assets are always downloaded from GPU memory due to lack of dedicated storage container for the asset data. /// The gathered data or null if mesh has no vertex colors. diff --git a/Source/Engine/Graphics/Models/Mesh.cpp b/Source/Engine/Graphics/Models/Mesh.cpp index 0ba51b2d7..444d60540 100644 --- a/Source/Engine/Graphics/Models/Mesh.cpp +++ b/Source/Engine/Graphics/Models/Mesh.cpp @@ -10,6 +10,127 @@ #include "Engine/Serialization/MemoryReadStream.h" #include +namespace +{ + template + bool UpdateMesh(Mesh* mesh, uint32 vertexCount, uint32 triangleCount, Vector3* vertices, IndexType* triangles, Vector3* normals, Vector3* tangents, Vector2* uvs, Color32* colors) + { + auto model = mesh->GetModel(); + CHECK_RETURN(model && model->IsVirtual(), true); + CHECK_RETURN(triangles && vertices, true); + + // Pack mesh data into vertex buffers + Array vb1; + Array vb2; + vb1.Resize(vertexCount); + if (normals) + { + if (tangents) + { + for (uint32 i = 0; i < vertexCount; i++) + { + const Vector3 normal = normals[i]; + const Vector3 tangent = tangents[i]; + + // Calculate bitangent sign + Vector3 bitangent = Vector3::Normalize(Vector3::Cross(normal, tangent)); + byte sign = static_cast(Vector3::Dot(Vector3::Cross(bitangent, normal), tangent) < 0.0f ? 1 : 0); + + // Set tangent frame + vb1[i].Tangent = Float1010102(tangent * 0.5f + 0.5f, sign); + vb1[i].Normal = Float1010102(normal * 0.5f + 0.5f, 0); + } + } + else + { + for (uint32 i = 0; i < vertexCount; i++) + { + const Vector3 normal = normals[i]; + + // Calculate tangent + Vector3 c1 = Vector3::Cross(normal, Vector3::UnitZ); + Vector3 c2 = Vector3::Cross(normal, Vector3::UnitY); + Vector3 tangent; + if (c1.LengthSquared() > c2.LengthSquared()) + tangent = c1; + else + tangent = c2; + + // Calculate bitangent sign + Vector3 bitangent = Vector3::Normalize(Vector3::Cross(normal, tangent)); + byte sign = static_cast(Vector3::Dot(Vector3::Cross(bitangent, normal), tangent) < 0.0f ? 1 : 0); + + // Set tangent frame + vb1[i].Tangent = Float1010102(tangent * 0.5f + 0.5f, sign); + vb1[i].Normal = Float1010102(normal * 0.5f + 0.5f, 0); + } + } + } + else + { + // Set default tangent frame + const auto n = Float1010102(Vector3::UnitZ); + const auto t = Float1010102(Vector3::UnitX); + for (uint32 i = 0; i < vertexCount; i++) + { + vb1[i].Normal = n; + vb1[i].Tangent = t; + } + } + if (uvs) + { + for (uint32 i = 0; i < vertexCount; i++) + vb1[i].TexCoord = Half2(uvs[i]); + } + else + { + auto v = Half2(0, 0); + for (uint32 i = 0; i < vertexCount; i++) + vb1[i].TexCoord = v; + } + { + auto v = Half2(0, 0); + for (uint32 i = 0; i < vertexCount; i++) + vb1[i].LightmapUVs = v; + } + if (colors) + { + vb2.Resize(vertexCount); + for (uint32 i = 0; i < vertexCount; i++) + vb2[i].Color = colors[i]; + } + + return mesh->UpdateMesh(vertexCount, triangleCount, (VB0ElementType*)vertices, vb1.Get(), vb2.HasItems() ? vb2.Get() : nullptr, triangles); + } + + template + bool UpdateMesh(Mesh* mesh, uint32 vertexCount, uint32 triangleCount, MonoArray* verticesObj, MonoArray* trianglesObj, MonoArray* normalsObj, MonoArray* tangentsObj, MonoArray* uvObj, MonoArray* colorsObj) + { + ASSERT((uint32)mono_array_length(verticesObj) >= vertexCount); + ASSERT((uint32)mono_array_length(trianglesObj) / 3 >= triangleCount); + auto vertices = (Vector3*)(void*)mono_array_addr_with_size(verticesObj, sizeof(Vector3), 0); + auto triangles = (IndexType*)(void*)mono_array_addr_with_size(trianglesObj, sizeof(IndexType), 0); + const auto normals = normalsObj ? (Vector3*)(void*)mono_array_addr_with_size(normalsObj, sizeof(Vector3), 0) : nullptr; + const auto tangents = tangentsObj ? (Vector3*)(void*)mono_array_addr_with_size(tangentsObj, sizeof(Vector3), 0) : nullptr; + const auto uvs = uvObj ? (Vector2*)(void*)mono_array_addr_with_size(uvObj, sizeof(Vector2), 0) : nullptr; + const auto colors = colorsObj ? (Color32*)(void*)mono_array_addr_with_size(colorsObj, sizeof(Color32), 0) : nullptr; + return UpdateMesh(mesh, vertexCount, triangleCount, vertices, triangles, normals, tangents, uvs, colors); + } + + template + bool UpdateTriangles(Mesh* mesh, int32 triangleCount, MonoArray* trianglesObj) + { + const auto model = mesh->GetModel(); + ASSERT(model && model->IsVirtual() && trianglesObj); + + // Get buffer data + ASSERT((int32)mono_array_length(trianglesObj) / 3 >= triangleCount); + auto ib = (IndexType*)(void*)mono_array_addr_with_size(trianglesObj, sizeof(IndexType), 0); + + return mesh->UpdateTriangles(triangleCount, ib); + } +} + bool Mesh::UpdateMesh(uint32 vertexCount, uint32 triangleCount, VB0ElementType* vb0, VB1ElementType* vb1, VB2ElementType* vb2, void* ib, bool use16BitIndices) { Unload(); @@ -31,6 +152,16 @@ bool Mesh::UpdateMesh(uint32 vertexCount, uint32 triangleCount, VB0ElementType* return failed; } +bool Mesh::UpdateMesh(uint32 vertexCount, uint32 triangleCount, Vector3* vertices, uint16* triangles, Vector3* normals, Vector3* tangents, Vector2* uvs, Color32* colors) +{ + return ::UpdateMesh(this, vertexCount, triangleCount, vertices, triangles, normals, tangents, uvs, colors); +} + +bool Mesh::UpdateMesh(uint32 vertexCount, uint32 triangleCount, Vector3* vertices, uint32* triangles, Vector3* normals, Vector3* tangents, Vector2* uvs, Color32* colors) +{ + return ::UpdateMesh(this, vertexCount, triangleCount, vertices, triangles, normals, tangents, uvs, colors); +} + bool Mesh::UpdateTriangles(uint32 triangleCount, void* ib, bool use16BitIndices) { // Cache data @@ -384,108 +515,9 @@ ScriptingObject* Mesh::GetParentModel() return _model; } -template -bool UpdateMesh(Mesh* mesh, uint32 vertexCount, uint32 triangleCount, MonoArray* verticesObj, MonoArray* trianglesObj, MonoArray* normalsObj, MonoArray* tangentsObj, MonoArray* uvObj, MonoArray* colorsObj) -{ - auto model = mesh->GetModel(); - ASSERT(model && model->IsVirtual() && verticesObj && trianglesObj); - - // Get buffers data - ASSERT((uint32)mono_array_length(verticesObj) >= vertexCount); - ASSERT((uint32)mono_array_length(trianglesObj) / 3 >= triangleCount); - auto vb0 = (Vector3*)(void*)mono_array_addr_with_size(verticesObj, sizeof(Vector3), 0); - auto ib = (IndexType*)(void*)mono_array_addr_with_size(trianglesObj, sizeof(IndexType), 0); - Array vb1; - Array vb2; - vb1.Resize(vertexCount); - if (normalsObj) - { - const auto normals = (Vector3*)(void*)mono_array_addr_with_size(normalsObj, sizeof(Vector3), 0); - if (tangentsObj) - { - const auto tangents = (Vector3*)(void*)mono_array_addr_with_size(tangentsObj, sizeof(Vector3), 0); - for (uint32 i = 0; i < vertexCount; i++) - { - // Peek normal and tangent - const Vector3 normal = normals[i]; - const Vector3 tangent = tangents[i]; - - // Calculate bitangent sign - Vector3 bitangent = Vector3::Normalize(Vector3::Cross(normal, tangent)); - byte sign = static_cast(Vector3::Dot(Vector3::Cross(bitangent, normal), tangent) < 0.0f ? 1 : 0); - - // Set tangent frame - vb1[i].Tangent = Float1010102(tangent * 0.5f + 0.5f, sign); - vb1[i].Normal = Float1010102(normal * 0.5f + 0.5f, 0); - } - } - else - { - for (uint32 i = 0; i < vertexCount; i++) - { - // Peek normal - const Vector3 normal = normals[i]; - - // Calculate tangent - Vector3 c1 = Vector3::Cross(normal, Vector3::UnitZ); - Vector3 c2 = Vector3::Cross(normal, Vector3::UnitY); - Vector3 tangent; - if (c1.LengthSquared() > c2.LengthSquared()) - tangent = c1; - else - tangent = c2; - - // Calculate bitangent sign - Vector3 bitangent = Vector3::Normalize(Vector3::Cross(normal, tangent)); - byte sign = static_cast(Vector3::Dot(Vector3::Cross(bitangent, normal), tangent) < 0.0f ? 1 : 0); - - // Set tangent frame - vb1[i].Tangent = Float1010102(tangent * 0.5f + 0.5f, sign); - vb1[i].Normal = Float1010102(normal * 0.5f + 0.5f, 0); - } - } - } - else - { - const auto n = Float1010102(Vector3::UnitZ); - const auto t = Float1010102(Vector3::UnitX); - for (uint32 i = 0; i < vertexCount; i++) - { - vb1[i].Normal = n; - vb1[i].Tangent = t; - } - } - if (uvObj) - { - const auto uvs = (Vector2*)(void*)mono_array_addr_with_size(uvObj, sizeof(Vector2), 0); - for (uint32 i = 0; i < vertexCount; i++) - vb1[i].TexCoord = Half2(uvs[i]); - } - else - { - auto v = Half2(0, 0); - for (uint32 i = 0; i < vertexCount; i++) - vb1[i].TexCoord = v; - } - { - auto v = Half2(0, 0); - for (uint32 i = 0; i < vertexCount; i++) - vb1[i].LightmapUVs = v; - } - if (colorsObj) - { - vb2.Resize(vertexCount); - const auto colors = (Color32*)(void*)mono_array_addr_with_size(colorsObj, sizeof(Color32), 0); - for (uint32 i = 0; i < vertexCount; i++) - vb2[i].Color = colors[i]; - } - - return mesh->UpdateMesh(vertexCount, triangleCount, (VB0ElementType*)vb0, vb1.Get(), vb2.HasItems() ? vb2.Get() : nullptr, ib); -} - bool Mesh::UpdateMeshInt(int32 vertexCount, int32 triangleCount, MonoArray* verticesObj, MonoArray* trianglesObj, MonoArray* normalsObj, MonoArray* tangentsObj, MonoArray* uvObj, MonoArray* colorsObj) { - return ::UpdateMesh(this, (uint32)vertexCount, (uint32)triangleCount, verticesObj, trianglesObj, normalsObj, tangentsObj, uvObj, colorsObj); + return ::UpdateMesh(this, (uint32)vertexCount, (uint32)triangleCount, verticesObj, trianglesObj, normalsObj, tangentsObj, uvObj, colorsObj); } bool Mesh::UpdateMeshUShort(int32 vertexCount, int32 triangleCount, MonoArray* verticesObj, MonoArray* trianglesObj, MonoArray* normalsObj, MonoArray* tangentsObj, MonoArray* uvObj, MonoArray* colorsObj) @@ -493,22 +525,9 @@ bool Mesh::UpdateMeshUShort(int32 vertexCount, int32 triangleCount, MonoArray* v return ::UpdateMesh(this, (uint32)vertexCount, (uint32)triangleCount, verticesObj, trianglesObj, normalsObj, tangentsObj, uvObj, colorsObj); } -template -bool UpdateTriangles(Mesh* mesh, int32 triangleCount, MonoArray* trianglesObj) -{ - auto model = mesh->GetModel(); - ASSERT(model && model->IsVirtual() && trianglesObj); - - // Get buffer data - ASSERT((int32)mono_array_length(trianglesObj) / 3 >= triangleCount); - auto ib = (IndexType*)(void*)mono_array_addr_with_size(trianglesObj, sizeof(IndexType), 0); - - return mesh->UpdateTriangles(triangleCount, ib); -} - bool Mesh::UpdateTrianglesInt(int32 triangleCount, MonoArray* trianglesObj) { - return ::UpdateTriangles(this, triangleCount, trianglesObj); + return ::UpdateTriangles(this, triangleCount, trianglesObj); } bool Mesh::UpdateTrianglesUShort(int32 triangleCount, MonoArray* trianglesObj) diff --git a/Source/Engine/Graphics/Models/Mesh.h b/Source/Engine/Graphics/Models/Mesh.h index c8311a1f3..80d566fa3 100644 --- a/Source/Engine/Graphics/Models/Mesh.h +++ b/Source/Engine/Graphics/Models/Mesh.h @@ -216,9 +216,9 @@ public: /// The first vertex buffer data. /// The second vertex buffer data. /// The third vertex buffer data. - /// The index buffer. + /// The index buffer in clockwise order. /// True if failed, otherwise false. - FORCE_INLINE bool UpdateMesh(uint32 vertexCount, uint32 triangleCount, VB0ElementType* vb0, VB1ElementType* vb1, VB2ElementType* vb2, int32* ib) + FORCE_INLINE bool UpdateMesh(uint32 vertexCount, uint32 triangleCount, VB0ElementType* vb0, VB1ElementType* vb1, VB2ElementType* vb2, uint32* ib) { return UpdateMesh(vertexCount, triangleCount, vb0, vb1, vb2, ib, false); } @@ -231,7 +231,7 @@ public: /// The first vertex buffer data. /// The second vertex buffer data. /// The third vertex buffer data. - /// The index buffer. + /// The index buffer in clockwise order. /// True if failed, otherwise false. FORCE_INLINE bool UpdateMesh(uint32 vertexCount, uint32 triangleCount, VB0ElementType* vb0, VB1ElementType* vb1, VB2ElementType* vb2, uint16* ib) { @@ -240,17 +240,51 @@ public: /// /// Updates the model mesh (used by the virtual models created with Init rather than Load). + /// Can be used only for virtual assets (see and ). + /// Mesh data will be cached and uploaded to the GPU with a delay. /// /// The amount of vertices in the vertex buffer. /// The amount of triangles in the index buffer. /// The first vertex buffer data. /// The second vertex buffer data. /// The third vertex buffer data. - /// The index buffer. + /// The index buffer in clockwise order. /// True if index buffer uses 16-bit index buffer, otherwise 32-bit. /// True if failed, otherwise false. bool UpdateMesh(uint32 vertexCount, uint32 triangleCount, VB0ElementType* vb0, VB1ElementType* vb1, VB2ElementType* vb2, void* ib, bool use16BitIndices); + /// + /// Updates the model mesh (used by the virtual models created with Init rather than Load). + /// Can be used only for virtual assets (see and ). + /// Mesh data will be cached and uploaded to the GPU with a delay. + /// + /// The amount of vertices in the vertex buffer. + /// The amount of triangles in the index buffer. + /// The mesh vertices positions. Cannot be null. + /// The mesh index buffer (clockwise triangles). Uses 32-bit stride buffer. Cannot be null. + /// The normal vectors (per vertex). + /// The normal vectors (per vertex). Use null to compute them from normal vectors. + /// The texture coordinates (per vertex). + /// The vertex colors (per vertex). + /// True if failed, otherwise false. + bool UpdateMesh(uint32 vertexCount, uint32 triangleCount, Vector3* vertices, uint16* triangles, Vector3* normals = nullptr, Vector3* tangents = nullptr, Vector2* uvs = nullptr, Color32* colors = nullptr); + + /// + /// Updates the model mesh (used by the virtual models created with Init rather than Load). + /// Can be used only for virtual assets (see and ). + /// Mesh data will be cached and uploaded to the GPU with a delay. + /// + /// The amount of vertices in the vertex buffer. + /// The amount of triangles in the index buffer. + /// The mesh vertices positions. Cannot be null. + /// The mesh index buffer (clockwise triangles). Uses 32-bit stride buffer. Cannot be null. + /// The normal vectors (per vertex). + /// The normal vectors (per vertex). Use null to compute them from normal vectors. + /// The texture coordinates (per vertex). + /// The vertex colors (per vertex). + /// True if failed, otherwise false. + bool UpdateMesh(uint32 vertexCount, uint32 triangleCount, Vector3* vertices, uint32* triangles, Vector3* normals = nullptr, Vector3* tangents = nullptr, Vector2* uvs = nullptr, Color32* colors = nullptr); + public: /// @@ -259,7 +293,7 @@ public: /// The amount of triangles in the index buffer. /// The index buffer. /// True if failed, otherwise false. - FORCE_INLINE bool UpdateTriangles(uint32 triangleCount, int32* ib) + FORCE_INLINE bool UpdateTriangles(uint32 triangleCount, uint32* ib) { return UpdateTriangles(triangleCount, ib, false); } diff --git a/Source/Engine/Graphics/Models/ModelData.Tool.cpp b/Source/Engine/Graphics/Models/ModelData.Tool.cpp index ee1b219dd..ee0fcb348 100644 --- a/Source/Engine/Graphics/Models/ModelData.Tool.cpp +++ b/Source/Engine/Graphics/Models/ModelData.Tool.cpp @@ -791,7 +791,7 @@ void MeshData::ImproveCacheLocality() Allocator::Free(piCandidates); const auto endTime = Platform::GetTimeSeconds(); - LOG(Info, "Cache relevant optimzie for {0} vertices and {1} indices. Average output ACMR is {2}. Time: {3}s", vertexCount, indexCount, (float)iCacheMisses / indexCount / 3, Utilities::RoundTo2DecimalPlaces(endTime - startTime)); + LOG(Info, "Cache relevant optimize for {0} vertices and {1} indices. Average output ACMR is {2}. Time: {3}s", vertexCount, indexCount, (float)iCacheMisses / indexCount / 3, Utilities::RoundTo2DecimalPlaces(endTime - startTime)); } float MeshData::CalculateTrianglesArea() const diff --git a/Source/Engine/Graphics/Models/ModelData.h b/Source/Engine/Graphics/Models/ModelData.h index 1e4b59d0a..a3bb47fcc 100644 --- a/Source/Engine/Graphics/Models/ModelData.h +++ b/Source/Engine/Graphics/Models/ModelData.h @@ -5,7 +5,7 @@ #include "Engine/Core/Common.h" #include "Engine/Core/Math/BoundingSphere.h" #include "Engine/Core/Math/BoundingBox.h" -#include "Engine/Core/Math/VectorInt.h" +#include "Engine/Core/Math/Int4.h" #include "Engine/Serialization/Stream.h" #include "Engine/Graphics/Enums.h" #include "Types.h" diff --git a/Source/Engine/Graphics/Models/SkinnedMesh.h b/Source/Engine/Graphics/Models/SkinnedMesh.h index 2c168980b..bbdf38014 100644 --- a/Source/Engine/Graphics/Models/SkinnedMesh.h +++ b/Source/Engine/Graphics/Models/SkinnedMesh.h @@ -163,7 +163,7 @@ public: /// The amount of vertices in the vertex buffer. /// The amount of triangles in the index buffer. /// The vertex buffer data. - /// The index buffer. + /// The index buffer in clockwise order. /// True if failed, otherwise false. FORCE_INLINE bool UpdateMesh(uint32 vertexCount, uint32 triangleCount, VB0SkinnedElementType* vb, int32* ib) { @@ -176,7 +176,7 @@ public: /// The amount of vertices in the vertex buffer. /// The amount of triangles in the index buffer. /// The vertex buffer data. - /// The index buffer. + /// The index buffer, clockwise order. /// True if failed, otherwise false. FORCE_INLINE bool UpdateMesh(uint32 vertexCount, uint32 triangleCount, VB0SkinnedElementType* vb, uint16* ib) { @@ -189,7 +189,7 @@ public: /// The amount of vertices in the vertex buffer. /// The amount of triangles in the index buffer. /// The vertex buffer data. - /// The index buffer. + /// The index buffer in clockwise order. /// True if index buffer uses 16-bit index buffer, otherwise 32-bit. /// True if failed, otherwise false. bool UpdateMesh(uint32 vertexCount, uint32 triangleCount, VB0SkinnedElementType* vb, void* ib, bool use16BitIndices); diff --git a/Source/Engine/Graphics/Models/Types.h b/Source/Engine/Graphics/Models/Types.h index 248988243..9a95b8815 100644 --- a/Source/Engine/Graphics/Models/Types.h +++ b/Source/Engine/Graphics/Models/Types.h @@ -9,7 +9,7 @@ #include "Engine/Core/Math/Vector4.h" #include "Engine/Core/Math/Color.h" #include "Engine/Core/Math/Color32.h" -#include "Engine/Core/Math/VectorInt.h" +#include "Engine/Core/Math/Int4.h" class Model; class SkinnedModel; diff --git a/Source/Engine/Graphics/PostProcessSettings.h b/Source/Engine/Graphics/PostProcessSettings.h index 4279cfc9b..400deb3c1 100644 --- a/Source/Engine/Graphics/PostProcessSettings.h +++ b/Source/Engine/Graphics/PostProcessSettings.h @@ -387,7 +387,7 @@ DECLARE_SCRIPTING_TYPE_NO_SPAWN(ToneMappingSettings); float WhiteTemperature = 6500.0f; /// - /// Adjusts the white balance temperature tint for the scene by adjusting the cyan and magenta color ranges. Ideally, this setting should be used once you've adjusted the white balance temporature to get accurate colors. Under some light temperatures, the colors may appear to be more yellow or blue. This can be used to balance the resulting color to look more natural. The default value is `0`. + /// Adjusts the white balance temperature tint for the scene by adjusting the cyan and magenta color ranges. Ideally, this setting should be used once you've adjusted the white balance temperature to get accurate colors. Under some light temperatures, the colors may appear to be more yellow or blue. This can be used to balance the resulting color to look more natural. The default value is `0`. /// API_FIELD(Attributes="DefaultValue(0.0f), Limit(-1, 1, 0.001f), EditorOrder(1), PostProcessSetting((int)ToneMappingSettingsOverride.WhiteTint)") float WhiteTint = 0.0f; diff --git a/Source/Engine/Graphics/RenderTargetPool.cpp b/Source/Engine/Graphics/RenderTargetPool.cpp index 109ff8de9..a9db0283d 100644 --- a/Source/Engine/Graphics/RenderTargetPool.cpp +++ b/Source/Engine/Graphics/RenderTargetPool.cpp @@ -108,5 +108,5 @@ void RenderTargetPool::Release(GPUTexture* rt) } } - LOG(Error, "Trying to release temporary render target which has not been registred in service!"); + LOG(Error, "Trying to release temporary render target which has not been registered in service!"); } diff --git a/Source/Engine/Graphics/RenderTask.cpp b/Source/Engine/Graphics/RenderTask.cpp index e3e261c96..405e0fe45 100644 --- a/Source/Engine/Graphics/RenderTask.cpp +++ b/Source/Engine/Graphics/RenderTask.cpp @@ -260,6 +260,11 @@ void SceneRenderTask::OnCollectDrawCalls(RenderContext& renderContext) CollectDrawCalls(renderContext); } +void SceneRenderTask::OnPreRender(GPUContext* context, RenderContext& renderContext) +{ + PreRender(context, renderContext); +} + void SceneRenderTask::OnPostRender(GPUContext* context, RenderContext& renderContext) { PostRender(context, renderContext); @@ -278,11 +283,10 @@ Viewport SceneRenderTask::GetViewport() const GPUTextureView* SceneRenderTask::GetOutputView() const { - if (Output) + if (Output && Output->IsAllocated()) return Output->View(); if (SwapChain) return SwapChain->GetBackBufferView(); - CRASH; return nullptr; } diff --git a/Source/Engine/Graphics/RenderTask.h b/Source/Engine/Graphics/RenderTask.h index b00d80a5f..265905e2a 100644 --- a/Source/Engine/Graphics/RenderTask.h +++ b/Source/Engine/Graphics/RenderTask.h @@ -313,6 +313,18 @@ public: /// The rendering context. virtual void OnCollectDrawCalls(RenderContext& renderContext); + /// + /// The action called after scene rendering. Can be used to perform custom pre-rendering or to modify the render view. + /// + API_EVENT() Delegate PreRender; + + /// + /// Called before scene rendering. Can be used to perform custom pre-rendering or to modify the render view. + /// + /// The GPU commands context. + /// The rendering context. + virtual void OnPreRender(GPUContext* context, RenderContext& renderContext); + /// /// The action called after scene rendering. Can be used to render additional visual elements to the output. /// diff --git a/Source/Engine/Graphics/Shaders/GPUShader.h b/Source/Engine/Graphics/Shaders/GPUShader.h index 79b165396..76c8df546 100644 --- a/Source/Engine/Graphics/Shaders/GPUShader.h +++ b/Source/Engine/Graphics/Shaders/GPUShader.h @@ -97,7 +97,7 @@ public: /// /// Gets the vertex shader. /// - /// Thr shader program name. + /// The shader program name. /// The shader permutation index. /// The shader object. API_FUNCTION() FORCE_INLINE GPUShaderProgramVS* GetVS(const StringAnsiView& name, int32 permutationIndex = 0) const @@ -108,7 +108,7 @@ public: /// /// Gets the hull shader. /// - /// Thr shader program name. + /// The shader program name. /// The shader permutation index. /// The shader object. API_FUNCTION() FORCE_INLINE GPUShaderProgramHS* GetHS(const StringAnsiView& name, int32 permutationIndex = 0) const @@ -119,7 +119,7 @@ public: /// /// Gets domain shader. /// - /// Thr shader program name. + /// The shader program name. /// The shader permutation index. /// The shader object. API_FUNCTION() FORCE_INLINE GPUShaderProgramDS* GetDS(const StringAnsiView& name, int32 permutationIndex = 0) const @@ -130,7 +130,7 @@ public: /// /// Gets the geometry shader. /// - /// Thr shader program name. + /// The shader program name. /// The shader permutation index. /// The shader object. API_FUNCTION() FORCE_INLINE GPUShaderProgramGS* GetGS(const StringAnsiView& name, int32 permutationIndex = 0) const @@ -141,7 +141,7 @@ public: /// /// Gets the pixel shader. /// - /// Thr shader program name. + /// The shader program name. /// The shader permutation index. /// The shader object. API_FUNCTION() FORCE_INLINE GPUShaderProgramPS* GetPS(const StringAnsiView& name, int32 permutationIndex = 0) const @@ -152,7 +152,7 @@ public: /// /// Gets the compute shader. /// - /// Thr shader program name. + /// The shader program name. /// The shader permutation index. /// The shader object. API_FUNCTION() FORCE_INLINE GPUShaderProgramCS* GetCS(const StringAnsiView& name, int32 permutationIndex = 0) const @@ -176,7 +176,7 @@ public: /// /// Determines whether the specified shader program is in the shader. /// - /// Thr shader program name. + /// The shader program name. /// The shader permutation index. /// true if the shader is valid; otherwise, false. FORCE_INLINE bool HasShader(const StringAnsiView& name, int32 permutationIndex = 0) const diff --git a/Source/Engine/Graphics/SkinnedMesh.cs b/Source/Engine/Graphics/SkinnedMesh.cs index e8e0d8353..ea4cf283e 100644 --- a/Source/Engine/Graphics/SkinnedMesh.cs +++ b/Source/Engine/Graphics/SkinnedMesh.cs @@ -104,7 +104,7 @@ namespace FlaxEngine /// Mesh data will be cached and uploaded to the GPU with a delay. /// /// The mesh vertices positions. Cannot be null. - /// The mesh index buffer (triangles). Uses 32-bit stride buffer. Cannot be null. + /// The mesh index buffer (clockwise triangles). Uses 32-bit stride buffer. Cannot be null. /// The skinned mesh blend indices buffer. Contains indices of the skeleton bones (up to 4 bones per vertex) to use for vertex position blending. Cannot be null. /// The skinned mesh blend weights buffer (normalized). Contains weights per blend bone (up to 4 bones per vertex) of the skeleton bones to mix for vertex position blending. Cannot be null. /// The normal vectors (per vertex). @@ -140,7 +140,7 @@ namespace FlaxEngine /// Mesh data will be cached and uploaded to the GPU with a delay. /// /// The mesh vertices positions. Cannot be null. - /// The mesh index buffer (triangles). Uses 16-bit stride buffer. Cannot be null. + /// The mesh index buffer (clockwise triangles). Uses 16-bit stride buffer. Cannot be null. /// The skinned mesh blend indices buffer. Contains indices of the skeleton bones (up to 4 bones per vertex) to use for vertex position blending. Cannot be null. /// The skinned mesh blend weights buffer (normalized). Contains weights per blend bone (up to 4 bones per vertex) of the skeleton bones to mix for vertex position blending. Cannot be null. /// The normal vectors (per vertex). diff --git a/Source/Engine/Graphics/Textures/GPUTexture.cpp b/Source/Engine/Graphics/Textures/GPUTexture.cpp index e51d2e510..29268e721 100644 --- a/Source/Engine/Graphics/Textures/GPUTexture.cpp +++ b/Source/Engine/Graphics/Textures/GPUTexture.cpp @@ -458,43 +458,13 @@ protected: // [ThreadPoolTask] bool Run() override { - // Check resources auto texture = _texture.Get(); if (texture == nullptr || _staging == nullptr || _data == nullptr) { LOG(Warning, "Cannot download texture data. Missing objects."); return true; } - - const auto arraySize = texture->ArraySize(); - const auto mipLevels = texture->MipLevels(); - - // Get all mip maps for each array slice - auto& rawResultData = _data->Items; - rawResultData.Resize(arraySize, false); - for (int32 arrayIndex = 0; arrayIndex < arraySize; arrayIndex++) - { - auto& arraySlice = rawResultData[arrayIndex]; - arraySlice.Mips.Resize(mipLevels); - - for (int32 mipMapIndex = 0; mipMapIndex < mipLevels; mipMapIndex++) - { - auto& mip = arraySlice.Mips[mipMapIndex]; - const int32 mipWidth = _data->Width >> mipMapIndex; - const int32 mipHeight = _data->Height >> mipMapIndex; - uint32 mipRowPitch, mipSlicePitch; - RenderTools::ComputePitch(_data->Format, mipWidth, mipHeight, mipRowPitch, mipSlicePitch); - - // Gather data - if (_staging->GetData(arrayIndex, mipMapIndex, mip, mipRowPitch)) - { - LOG(Warning, "Staging resource of \'{0}\' get data failed.", texture->ToString()); - return true; - } - } - } - - return false; + return _staging->DownloadData(*_data); } void OnEnd() override @@ -508,6 +478,57 @@ protected: bool GPUTexture::DownloadData(TextureData& result) { + // Skip for empty ones + if (MipLevels() == 0) + { + LOG(Warning, "Cannot download GPU texture data from an empty texture."); + return true; + } + if (Depth() != 1) + { + MISSING_CODE("support volume texture data downloading."); + } + + // Use faster path for staging resources + if (IsStaging()) + { + const auto arraySize = ArraySize(); + const auto mipLevels = MipLevels(); + + // Set texture info + result.Width = Width(); + result.Height = Height(); + result.Depth = Depth(); + result.Format = Format(); + + // Get all mip maps for each array slice + auto& rawResultData = result.Items; + rawResultData.Resize(arraySize, false); + for (int32 arrayIndex = 0; arrayIndex < arraySize; arrayIndex++) + { + auto& arraySlice = rawResultData[arrayIndex]; + arraySlice.Mips.Resize(mipLevels); + + for (int32 mipMapIndex = 0; mipMapIndex < mipLevels; mipMapIndex++) + { + auto& mip = arraySlice.Mips[mipMapIndex]; + const int32 mipWidth = result.Width >> mipMapIndex; + const int32 mipHeight = result.Height >> mipMapIndex; + uint32 mipRowPitch, mipSlicePitch; + RenderTools::ComputePitch(result.Format, mipWidth, mipHeight, mipRowPitch, mipSlicePitch); + + // Gather data + if (GetData(arrayIndex, mipMapIndex, mip, mipRowPitch)) + { + LOG(Warning, "Staging resource of \'{0}\' get data failed.", ToString()); + return true; + } + } + } + + return false; + } + const auto name = ToString(); // Ensure not running on main thread - we support DownloadData from textures only on a worker threads (Thread Pool Workers or Content Loaders) @@ -538,7 +559,8 @@ bool GPUTexture::DownloadData(TextureData& result) Task* GPUTexture::DownloadDataAsync(TextureData& result) { - if (!IsAllocated()) + // Skip for empty ones + if (MipLevels() == 0) { LOG(Warning, "Cannot download texture data. It has not ben created yet."); return nullptr; @@ -548,19 +570,12 @@ Task* GPUTexture::DownloadDataAsync(TextureData& result) MISSING_CODE("support volume texture data downloading."); } - // Set texture info - result.Width = Width(); - result.Height = Height(); - result.Depth = Depth(); - result.Format = Format(); - - // Quicker path if texture is already readback - if (_desc.Usage == GPUResourceUsage::StagingReadback) + // Use faster path for staging resources + if (IsStaging()) { // Create task to copy downloaded data to TextureData container auto getDataTask = ::New(this, this, result); ASSERT(getDataTask->HasReference(this)); - return getDataTask; } diff --git a/Source/Engine/Graphics/Textures/GPUTexture.h b/Source/Engine/Graphics/Textures/GPUTexture.h index d0ae27e7f..8b5385f9e 100644 --- a/Source/Engine/Graphics/Textures/GPUTexture.h +++ b/Source/Engine/Graphics/Textures/GPUTexture.h @@ -483,7 +483,7 @@ public: /// /// Creates new staging readback texture with the same dimensions and properties as a source texture (but without a data transferred; warning: caller must delete object). /// - /// Thr staging readback texture. + /// The staging readback texture. GPUTexture* ToStagingReadback() const; /// diff --git a/Source/Engine/GraphicsDevice/DirectX/DX11/GPUTimerQueryDX11.cpp b/Source/Engine/GraphicsDevice/DirectX/DX11/GPUTimerQueryDX11.cpp index 63cd9bcb4..5df4a45a5 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX11/GPUTimerQueryDX11.cpp +++ b/Source/Engine/GraphicsDevice/DirectX/DX11/GPUTimerQueryDX11.cpp @@ -118,7 +118,7 @@ float GPUTimerQueryDX11::GetResult() if (!SingleShotLog) { SingleShotLog = true; - LOG(Warning, "Unrealiable GPU timer query detected."); + LOG(Warning, "Unreliable GPU timer query detected."); } #endif } diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.cpp b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.cpp index ce36a7347..4d1bd3e17 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.cpp +++ b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.cpp @@ -216,7 +216,7 @@ uint64 GPUContextDX12::Execute(bool waitForCompletion) ASSERT(_currentAllocator != nullptr); auto queue = _device->GetCommandQueue(); - // Flush reaming and buffered commands + // Flush remaining and buffered commands FlushState(); _currentState = nullptr; diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.cpp b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.cpp index 29d417834..096a0ec11 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.cpp +++ b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.cpp @@ -391,7 +391,7 @@ bool GPUDeviceDX12::Init() { // Descriptor tables D3D12_DESCRIPTOR_RANGE r[2]; - // TODO: separate ranges for pixel/vertex visiblity and one shared for all? + // TODO: separate ranges for pixel/vertex visibility and one shared for all? { D3D12_DESCRIPTOR_RANGE& range = r[0]; range.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV; diff --git a/Source/Engine/GraphicsDevice/DirectX/IncludeDirectXHeaders.h b/Source/Engine/GraphicsDevice/DirectX/IncludeDirectXHeaders.h index 8351deb71..ea621c22a 100644 --- a/Source/Engine/GraphicsDevice/DirectX/IncludeDirectXHeaders.h +++ b/Source/Engine/GraphicsDevice/DirectX/IncludeDirectXHeaders.h @@ -8,7 +8,7 @@ #include "Engine/Platform/Win32/IncludeWindowsHeaders.h" #include "Engine/Platform/Windows/ComPtr.h" -// Helper define to dispose the COM object with reaming references counter checking +// Helper define to dispose the COM object with remaining references counter checking #define DX_SAFE_RELEASE_CHECK(x, refs) if(x) { auto res = (x)->Release(); (x) = nullptr; CHECK(res == refs); } #endif diff --git a/Source/Engine/GraphicsDevice/Vulkan/CmdBufferVulkan.cpp b/Source/Engine/GraphicsDevice/Vulkan/CmdBufferVulkan.cpp index c327fd32a..f9a39574c 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/CmdBufferVulkan.cpp +++ b/Source/Engine/GraphicsDevice/Vulkan/CmdBufferVulkan.cpp @@ -44,7 +44,7 @@ void CmdBufferVulkan::End() ASSERT(IsOutsideRenderPass()); #if GPU_ALLOW_PROFILE_EVENTS - // End reaming events + // End remaining events while (_eventsBegin--) vkCmdEndDebugUtilsLabelEXT(GetHandle()); #endif diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.cpp b/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.cpp index ea8fa6502..e8f770831 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.cpp +++ b/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.cpp @@ -1155,7 +1155,7 @@ void GPUContextVulkan::FlushState() void GPUContextVulkan::Flush() { - // Flush reaming and buffered commands + // Flush remaining and buffered commands FlushState(); _currentState = nullptr; diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.cpp b/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.cpp index 33df0db47..ea9e6a990 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.cpp +++ b/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.cpp @@ -1363,23 +1363,7 @@ PixelFormat GPUDeviceVulkan::GetClosestSupportedPixelFormat(PixelFormat format, if (flags & GPUTextureFlags::UnorderedAccess) wantedFeatureFlags |= VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT; - // Check actual device for format support - const auto isSupported = [&](VkFormat vkFormat) - { - VkFormatProperties props; - vkGetPhysicalDeviceFormatProperties(Adapter->Gpu, vkFormat, &props); - const VkFormatFeatureFlags featureFlags = optimalTiling ? props.optimalTilingFeatures : props.linearTilingFeatures; - if ((featureFlags & wantedFeatureFlags) != wantedFeatureFlags) - return false; - - //VkImageFormatProperties imageProps; - //vkGetPhysicalDeviceImageFormatProperties(Adapter->Gpu, vkFormat, , &imageProps); - - return true; - }; - - VkFormat vkFormat = RenderToolsVulkan::ToVulkanFormat(format); - if (!isSupported(vkFormat)) + if (!IsVkFormatSupported(RenderToolsVulkan::ToVulkanFormat(format), wantedFeatureFlags, optimalTiling)) { // Special case for depth-stencil formats if (flags & GPUTextureFlags::DepthStencil) @@ -1389,7 +1373,7 @@ PixelFormat GPUDeviceVulkan::GetClosestSupportedPixelFormat(PixelFormat format, // Spec guarantees at least one depth-only, and one depth-stencil format to be supported if (hasStencil) { - if (isSupported(VK_FORMAT_D32_SFLOAT_S8_UINT)) + if (IsVkFormatSupported(VK_FORMAT_D32_SFLOAT_S8_UINT, wantedFeatureFlags, optimalTiling)) format = PixelFormat::D32_Float; else format = PixelFormat::D24_UNorm_S8_UInt; @@ -1493,6 +1477,20 @@ bool GPUDeviceVulkan::SaveValidationCache() #endif +bool GPUDeviceVulkan::IsVkFormatSupported(VkFormat vkFormat, VkFormatFeatureFlags wantedFeatureFlags, bool optimalTiling) const +{ + VkFormatProperties props; + vkGetPhysicalDeviceFormatProperties(Adapter->Gpu, vkFormat, &props); + const VkFormatFeatureFlags featureFlags = optimalTiling ? props.optimalTilingFeatures : props.linearTilingFeatures; + if ((featureFlags & wantedFeatureFlags) != wantedFeatureFlags) + return false; + + //VkImageFormatProperties imageProps; + //vkGetPhysicalDeviceImageFormatProperties(Adapter->Gpu, vkFormat, , &imageProps); + + return true; +} + GPUContext* GPUDeviceVulkan::GetMainContext() { return reinterpret_cast(MainContext); diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.h b/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.h index 9bab15dda..ba972932b 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.h +++ b/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.h @@ -720,6 +720,10 @@ public: #endif +private: + + bool IsVkFormatSupported(VkFormat vkFormat, VkFormatFeatureFlags wantedFeatureFlags, bool optimalTiling) const; + public: // [GPUDevice] diff --git a/Source/Engine/GraphicsDevice/Vulkan/RenderToolsVulkan.h b/Source/Engine/GraphicsDevice/Vulkan/RenderToolsVulkan.h index b18ae9c1b..cf9b7e9cf 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/RenderToolsVulkan.h +++ b/Source/Engine/GraphicsDevice/Vulkan/RenderToolsVulkan.h @@ -232,10 +232,10 @@ public: } /// - /// Converts Flax comparision function to the Vulkan comparision operation. + /// Converts Flax comparison function to the Vulkan comparison operation. /// - /// The Flax comparision function. - /// The Vulkan comparision operation. + /// The Flax comparison function. + /// The Vulkan comparison operation. static FORCE_INLINE VkCompareOp ToVulkanCompareOp(const ComparisonFunc value) { return ComparisonFuncToVkCompareOp[(int32)value]; diff --git a/Source/Engine/Input/Enums.h b/Source/Engine/Input/Enums.h index 764a17922..e397dd3c2 100644 --- a/Source/Engine/Input/Enums.h +++ b/Source/Engine/Input/Enums.h @@ -8,7 +8,7 @@ #define MAX_GAMEPADS 8 /// -/// Hardware mouse cursor behaviour. +/// Hardware mouse cursor behavior. /// API_ENUM() enum class CursorLockMode { diff --git a/Source/Engine/Input/Input.cpp b/Source/Engine/Input/Input.cpp index 1dfa0e5fe..825e7f6b2 100644 --- a/Source/Engine/Input/Input.cpp +++ b/Source/Engine/Input/Input.cpp @@ -98,6 +98,202 @@ Delegate Input::ActionTriggered; Array Input::ActionMappings; Array Input::AxisMappings; +void Mouse::OnMouseMoved(const Vector2& newPosition) +{ + _prevState.MousePosition = newPosition; + _state.MousePosition = newPosition; +} + +void Mouse::OnMouseDown(const Vector2& position, const MouseButton button, Window* target) +{ + Event& e = _queue.AddOne(); + e.Type = EventType::MouseDown; + e.Target = target; + e.MouseData.Button = button; + e.MouseData.Position = position; +} + +void Mouse::OnMouseUp(const Vector2& position, const MouseButton button, Window* target) +{ + Event& e = _queue.AddOne(); + e.Type = EventType::MouseUp; + e.Target = target; + e.MouseData.Button = button; + e.MouseData.Position = position; +} + +void Mouse::OnMouseDoubleClick(const Vector2& position, const MouseButton button, Window* target) +{ + Event& e = _queue.AddOne(); + e.Type = EventType::MouseDoubleClick; + e.Target = target; + e.MouseData.Button = button; + e.MouseData.Position = position; +} + +void Mouse::OnMouseMove(const Vector2& position, Window* target) +{ + Event& e = _queue.AddOne(); + e.Type = EventType::MouseMove; + e.Target = target; + e.MouseData.Position = position; +} + +void Mouse::OnMouseLeave(Window* target) +{ + Event& e = _queue.AddOne(); + e.Type = EventType::MouseLeave; + e.Target = target; +} + +void Mouse::OnMouseWheel(const Vector2& position, float delta, Window* target) +{ + Event& e = _queue.AddOne(); + e.Type = EventType::MouseWheel; + e.Target = target; + e.MouseWheelData.WheelDelta = delta; + e.MouseWheelData.Position = position; +} + +void Mouse::ResetState() +{ + InputDevice::ResetState(); + + _prevState.Clear(); + _state.Clear(); +} + +bool Mouse::Update(EventQueue& queue) +{ + // Move the current state to the previous + Platform::MemoryCopy(&_prevState, &_state, sizeof(State)); + + // Gather new events + if (UpdateState()) + return true; + + // Handle events + _state.MouseWheelDelta = 0; + for (int32 i = 0; i < _queue.Count(); i++) + { + const Event& e = _queue[i]; + switch (e.Type) + { + case EventType::MouseDown: + { + _state.MouseButtons[static_cast(e.MouseData.Button)] = true; + break; + } + case EventType::MouseUp: + { + _state.MouseButtons[static_cast(e.MouseData.Button)] = false; + break; + } + case EventType::MouseDoubleClick: + { + _state.MouseButtons[static_cast(e.MouseData.Button)] = true; + break; + } + case EventType::MouseWheel: + { + _state.MouseWheelDelta += e.MouseWheelData.WheelDelta; + break; + } + case EventType::MouseMove: + { + _state.MousePosition = e.MouseData.Position; + break; + } + case EventType::MouseLeave: + { + break; + } + } + } + + // Send events further + queue.Add(_queue); + _queue.Clear(); + return false; +} + +void Keyboard::OnCharInput(Char c, Window* target) +{ + // Skip control characters + if (c < 32) + return; + + Event& e = _queue.AddOne(); + e.Type = EventType::Char; + e.Target = target; + e.CharData.Char = c; +} + +void Keyboard::OnKeyUp(KeyboardKeys key, Window* target) +{ + Event& e = _queue.AddOne(); + e.Type = EventType::KeyUp; + e.Target = target; + e.KeyData.Key = key; +} + +void Keyboard::OnKeyDown(KeyboardKeys key, Window* target) +{ + Event& e = _queue.AddOne(); + e.Type = EventType::KeyDown; + e.Target = target; + e.KeyData.Key = key; +} + +void Keyboard::ResetState() +{ + InputDevice::ResetState(); + + _prevState.Clear(); + _state.Clear(); +} + +bool Keyboard::Update(EventQueue& queue) +{ + // Move the current state to the previous + Platform::MemoryCopy(&_prevState, &_state, sizeof(State)); + + // Gather new events + if (UpdateState()) + return true; + + // Handle events + _state.InputTextLength = 0; + for (int32 i = 0; i < _queue.Count(); i++) + { + const Event& e = _queue[i]; + switch (e.Type) + { + case EventType::Char: + { + if (_state.InputTextLength < ARRAY_COUNT(_state.InputText) - 1) + _state.InputText[_state.InputTextLength++] = e.CharData.Char; + break; + } + case EventType::KeyDown: + { + _state.Keys[static_cast(e.KeyData.Key)] = true; + break; + } + case EventType::KeyUp: + { + _state.Keys[static_cast(e.KeyData.Key)] = false; + break; + } + } + } + + // Send events further + queue.Add(_queue); + _queue.Clear(); + return false; +} + int32 Input::GetGamepadsCount() { return Gamepads.Count(); diff --git a/Source/Engine/Input/Keyboard.h b/Source/Engine/Input/Keyboard.h index ae958bd86..f182f9731 100644 --- a/Source/Engine/Input/Keyboard.h +++ b/Source/Engine/Input/Keyboard.h @@ -13,7 +13,7 @@ DECLARE_SCRIPTING_TYPE_NO_SPAWN(Keyboard); public: /// - /// The mouse state. + /// The keyboard state. /// struct State { @@ -94,97 +94,32 @@ public: return !_state.Keys[static_cast(key)] && _prevState.Keys[static_cast(key)]; } +public: + /// /// Called when keyboard enters input character. /// /// The Unicode character entered by the user. /// The target window to receive this event, otherwise input system will pick the window automatically. - void OnCharInput(const Char c, Window* target = nullptr) - { - // Skip control characters - if (c < 32) - return; - - Event& e = _queue.AddOne(); - e.Type = EventType::Char; - e.Target = target; - e.CharData.Char = c; - } + void OnCharInput(Char c, Window* target = nullptr); /// /// Called when key goes up. /// /// The keyboard key. /// The target window to receive this event, otherwise input system will pick the window automatically. - void OnKeyUp(const KeyboardKeys key, Window* target = nullptr) - { - Event& e = _queue.AddOne(); - e.Type = EventType::KeyUp; - e.Target = target; - e.KeyData.Key = key; - } + void OnKeyUp(KeyboardKeys key, Window* target = nullptr); /// /// Called when key goes down. /// /// The keyboard key. /// The target window to receive this event, otherwise input system will pick the window automatically. - void OnKeyDown(const KeyboardKeys key, Window* target = nullptr) - { - Event& e = _queue.AddOne(); - e.Type = EventType::KeyDown; - e.Target = target; - e.KeyData.Key = key; - } + void OnKeyDown(KeyboardKeys key, Window* target = nullptr); public: // [InputDevice] - void ResetState() override - { - InputDevice::ResetState(); - - _prevState.Clear(); - _state.Clear(); - } - - bool Update(EventQueue& queue) final override - { - // Move the current state to the previous - Platform::MemoryCopy(&_prevState, &_state, sizeof(State)); - - // Gather new events - if (UpdateState()) - return true; - - // Handle events - for (int32 i = 0; i < _queue.Count(); i++) - { - const Event& e = _queue[i]; - switch (e.Type) - { - case EventType::Char: - { - if (_state.InputTextLength < ARRAY_COUNT(_state.InputText) - 1) - _state.InputText[_state.InputTextLength++] = e.CharData.Char; - break; - } - case EventType::KeyDown: - { - _state.Keys[static_cast(e.KeyData.Key)] = true; - break; - } - case EventType::KeyUp: - { - _state.Keys[static_cast(e.KeyData.Key)] = false; - break; - } - } - } - - // Send events further - queue.Add(_queue); - _queue.Clear(); - return false; - } + void ResetState() override;; + bool Update(EventQueue& queue) final override; }; diff --git a/Source/Engine/Input/Mouse.h b/Source/Engine/Input/Mouse.h index 15fa17e6d..d58350956 100644 --- a/Source/Engine/Input/Mouse.h +++ b/Source/Engine/Input/Mouse.h @@ -124,14 +124,10 @@ public: virtual void SetMousePosition(const Vector2& newPosition) = 0; /// - /// Called when mouse cursor gets moved by the application. Invalidates the previous cached mouse position to prevent mouse jitter when locking the cursor programatically. + /// Called when mouse cursor gets moved by the application. Invalidates the previous cached mouse position to prevent mouse jitter when locking the cursor programmatically. /// /// The new mouse position. - void OnMouseMoved(const Vector2& newPosition) - { - _prevState.MousePosition = newPosition; - _state.MousePosition = newPosition; - } + void OnMouseMoved(const Vector2& newPosition); /// /// Called when mouse button goes down. @@ -139,14 +135,7 @@ public: /// The mouse position. /// The button. /// The target window to receive this event, otherwise input system will pick the window automatically. - void OnMouseDown(const Vector2& position, const MouseButton button, Window* target = nullptr) - { - Event& e = _queue.AddOne(); - e.Type = EventType::MouseDown; - e.Target = target; - e.MouseData.Button = button; - e.MouseData.Position = position; - } + void OnMouseDown(const Vector2& position, const MouseButton button, Window* target = nullptr); /// /// Called when mouse button goes up. @@ -154,14 +143,7 @@ public: /// The mouse position. /// The button. /// The target window to receive this event, otherwise input system will pick the window automatically. - void OnMouseUp(const Vector2& position, const MouseButton button, Window* target = nullptr) - { - Event& e = _queue.AddOne(); - e.Type = EventType::MouseUp; - e.Target = target; - e.MouseData.Button = button; - e.MouseData.Position = position; - } + void OnMouseUp(const Vector2& position, const MouseButton button, Window* target = nullptr); /// /// Called when mouse double clicks. @@ -169,38 +151,20 @@ public: /// The mouse position. /// The button. /// The target window to receive this event, otherwise input system will pick the window automatically. - void OnMouseDoubleClick(const Vector2& position, const MouseButton button, Window* target = nullptr) - { - Event& e = _queue.AddOne(); - e.Type = EventType::MouseDoubleClick; - e.Target = target; - e.MouseData.Button = button; - e.MouseData.Position = position; - } + void OnMouseDoubleClick(const Vector2& position, const MouseButton button, Window* target = nullptr); /// /// Called when mouse moves. /// /// The mouse position. /// The target window to receive this event, otherwise input system will pick the window automatically. - void OnMouseMove(const Vector2& position, Window* target = nullptr) - { - Event& e = _queue.AddOne(); - e.Type = EventType::MouseMove; - e.Target = target; - e.MouseData.Position = position; - } + void OnMouseMove(const Vector2& position, Window* target = nullptr); /// /// Called when mouse leaves the input source area. /// /// The target window to receive this event, otherwise input system will pick the window automatically. - void OnMouseLeave(Window* target = nullptr) - { - Event& e = _queue.AddOne(); - e.Type = EventType::MouseLeave; - e.Target = target; - } + void OnMouseLeave(Window* target = nullptr); /// /// Called when mouse wheel moves. @@ -208,76 +172,11 @@ public: /// The mouse position. /// The normalized delta (range [-1;1]). /// The target window to receive this event, otherwise input system will pick the window automatically. - void OnMouseWheel(const Vector2& position, const float delta, Window* target = nullptr) - { - Event& e = _queue.AddOne(); - e.Type = EventType::MouseWheel; - e.Target = target; - e.MouseWheelData.WheelDelta = delta; - e.MouseWheelData.Position = position; - } + void OnMouseWheel(const Vector2& position, float delta, Window* target = nullptr); public: // [InputDevice] - void ResetState() override - { - InputDevice::ResetState(); - - _prevState.Clear(); - _state.Clear(); - } - - bool Update(EventQueue& queue) final override - { - // Move the current state to the previous - Platform::MemoryCopy(&_prevState, &_state, sizeof(State)); - - // Gather new events - if (UpdateState()) - return true; - - // Handle events - _state.MouseWheelDelta = 0; - for (int32 i = 0; i < _queue.Count(); i++) - { - const Event& e = _queue[i]; - switch (e.Type) - { - case EventType::MouseDown: - { - _state.MouseButtons[static_cast(e.MouseData.Button)] = true; - break; - } - case EventType::MouseUp: - { - _state.MouseButtons[static_cast(e.MouseData.Button)] = false; - break; - } - case EventType::MouseDoubleClick: - { - break; - } - case EventType::MouseWheel: - { - _state.MouseWheelDelta += e.MouseWheelData.WheelDelta; - break; - } - case EventType::MouseMove: - { - _state.MousePosition = e.MouseData.Position; - break; - } - case EventType::MouseLeave: - { - break; - } - } - } - - // Send events further - queue.Add(_queue); - _queue.Clear(); - return false; - } + void ResetState() override; + bool Update(EventQueue& queue) final override; }; diff --git a/Source/Engine/Level/Actor.cpp b/Source/Engine/Level/Actor.cpp index 81a5b3155..f558f25a0 100644 --- a/Source/Engine/Level/Actor.cpp +++ b/Source/Engine/Level/Actor.cpp @@ -43,6 +43,8 @@ Actor::Actor(const SpawnParams& params) , _staticFlags(StaticFlags::FullyStatic) , _localTransform(Transform::Identity) , _transform(Transform::Identity) + , _sphere(BoundingSphere::Empty) + , _box(BoundingBox::Zero) , HideFlags(HideFlags::None) { } diff --git a/Source/Engine/Level/Actor.h b/Source/Engine/Level/Actor.h index 334021c28..7621e4a2a 100644 --- a/Source/Engine/Level/Actor.h +++ b/Source/Engine/Level/Actor.h @@ -437,13 +437,13 @@ public: } /// - /// Sets actor orientation in 3D space + /// Sets actor orientation in 3D space. /// /// The value to set. API_PROPERTY() void SetOrientation(const Quaternion& value); /// - /// Gets actor scale in 3D space + /// Gets actor scale in 3D space. /// API_PROPERTY(Attributes="HideInEditor, NoSerialize") FORCE_INLINE Vector3 GetScale() const @@ -458,13 +458,13 @@ public: API_PROPERTY() void SetScale(const Vector3& value); /// - /// Gets actor rotation matrix + /// Gets actor rotation matrix. /// API_PROPERTY(Attributes="HideInEditor, NoSerialize") Matrix GetRotation() const; /// - /// Sets actor rotation matrix + /// Sets actor rotation matrix. /// /// The value to set. API_PROPERTY() void SetRotation(const Matrix& value); diff --git a/Source/Engine/Level/Actors/AnimatedModel.cpp b/Source/Engine/Level/Actors/AnimatedModel.cpp index cf3e71f13..3d6450716 100644 --- a/Source/Engine/Level/Actors/AnimatedModel.cpp +++ b/Source/Engine/Level/Actors/AnimatedModel.cpp @@ -146,8 +146,32 @@ void AnimatedModel::GetNodeTransformation(const StringView& nodeName, Matrix& no GetNodeTransformation(SkinnedModel ? SkinnedModel->FindNode(nodeName) : -1, nodeTransformation, worldSpace); } +#define CHECK_ANIM_GRAPH_PARAM_ACCESS() \ + if (!AnimationGraph) \ + { \ + LOG(Warning, "Missing animation graph for animated model '{0}'", ToString()); \ + return; \ + } \ + if (AnimationGraph->WaitForLoaded()) \ + { \ + LOG(Warning, "Failed to load animation graph for animated model '{0}'", ToString()); \ + return; \ + } +#define CHECK_ANIM_GRAPH_PARAM_ACCESS_RESULT(result) \ + if (!AnimationGraph) \ + { \ + LOG(Warning, "Missing animation graph for animated model '{0}'", ToString()); \ + return result; \ + } \ + if (AnimationGraph->WaitForLoaded()) \ + { \ + LOG(Warning, "Failed to load animation graph for animated model '{0}'", ToString()); \ + return result; \ + } + AnimGraphParameter* AnimatedModel::GetParameter(const StringView& name) { + CHECK_ANIM_GRAPH_PARAM_ACCESS_RESULT(nullptr); for (auto& param : GraphInstance.Parameters) { if (param.Name == name) @@ -159,6 +183,7 @@ AnimGraphParameter* AnimatedModel::GetParameter(const StringView& name) Variant AnimatedModel::GetParameterValue(const StringView& name) { + CHECK_ANIM_GRAPH_PARAM_ACCESS_RESULT(Variant::Null); for (auto& param : GraphInstance.Parameters) { if (param.Name == name) @@ -170,6 +195,7 @@ Variant AnimatedModel::GetParameterValue(const StringView& name) void AnimatedModel::SetParameterValue(const StringView& name, const Variant& value) { + CHECK_ANIM_GRAPH_PARAM_ACCESS(); for (auto& param : GraphInstance.Parameters) { if (param.Name == name) @@ -183,6 +209,7 @@ void AnimatedModel::SetParameterValue(const StringView& name, const Variant& val Variant AnimatedModel::GetParameterValue(const Guid& id) { + CHECK_ANIM_GRAPH_PARAM_ACCESS_RESULT(Variant::Null); for (auto& param : GraphInstance.Parameters) { if (param.Identifier == id) @@ -194,6 +221,7 @@ Variant AnimatedModel::GetParameterValue(const Guid& id) void AnimatedModel::SetParameterValue(const Guid& id, const Variant& value) { + CHECK_ANIM_GRAPH_PARAM_ACCESS(); for (auto& param : GraphInstance.Parameters) { if (param.Identifier == id) diff --git a/Source/Engine/Level/Actors/Camera.h b/Source/Engine/Level/Actors/Camera.h index 8076e6a60..edf94ad22 100644 --- a/Source/Engine/Level/Actors/Camera.h +++ b/Source/Engine/Level/Actors/Camera.h @@ -211,7 +211,7 @@ public: /// The result camera view matrix. /// The result camera projection matrix. /// The custom output viewport. Use null to skip it. - API_FUNCTION() void GetMatrices(API_PARAM(Out) Matrix& view, API_PARAM(Out) Matrix& projection, API_PARAM(Ref) const Viewport& viewport) const; + API_FUNCTION() virtual void GetMatrices(API_PARAM(Out) Matrix& view, API_PARAM(Out) Matrix& projection, API_PARAM(Ref) const Viewport& viewport) const; #if USE_EDITOR // Intersection check for editor picking the camera diff --git a/Source/Engine/Level/Actors/StaticModel.cpp b/Source/Engine/Level/Actors/StaticModel.cpp index cba13a601..a884c9707 100644 --- a/Source/Engine/Level/Actors/StaticModel.cpp +++ b/Source/Engine/Level/Actors/StaticModel.cpp @@ -98,7 +98,7 @@ void StaticModel::SetVertexColor(int32 lodIndex, int32 meshIndex, int32 vertexIn { if (!Model || Model->WaitForLoaded()) { - LOG(Warning, "Cannot set vertex color if model is missing or faied to load."); + LOG(Warning, "Cannot set vertex color if model is missing or failed to load."); return; } diff --git a/Source/Engine/Level/Level.cpp b/Source/Engine/Level/Level.cpp index 18d9d4a52..62d066803 100644 --- a/Source/Engine/Level/Level.cpp +++ b/Source/Engine/Level/Level.cpp @@ -961,7 +961,7 @@ bool Level::loadScene(rapidjson_flax::Value& data, int32 engineBuild, bool autoI } // Synchronize prefab instances (prefab may have new objects added or some removed so deserialized instances need to synchronize with it) - // TODO: resave and force sync scenes durign game cooking so this step could be skipped in game + // TODO: resave and force sync scenes during game cooking so this step could be skipped in game Scripting::ObjectsLookupIdMapping.Set(&modifier.Value->IdsMapping); SceneObjectsFactory::SynchronizePrefabInstances(*sceneObjects.Value, actorToRemovedObjectsData, modifier.Value); Scripting::ObjectsLookupIdMapping.Set(nullptr); @@ -973,7 +973,7 @@ bool Level::loadScene(rapidjson_flax::Value& data, int32 engineBuild, bool autoI if (obj && obj->GetParent() == nullptr) { sceneObjects->At(i) = nullptr; - LOG(Warning, "Scene object {0} {1} has missing parent objct after scene load. Removing it.", obj->GetID(), obj->ToString()); + LOG(Warning, "Scene object {0} {1} has missing parent object after load. Removing it.", obj->GetID(), obj->ToString()); obj->DeleteObject(); } } @@ -1326,12 +1326,8 @@ Actor* Level::FindActor(const MClass* type) CHECK_RETURN(type, nullptr); Actor* result = nullptr; ScopeLock lock(ScenesLock); - for (int32 i = 0; result == nullptr && i < Scenes.Count(); i++) - { result = Scenes[i]->FindActor(type); - } - return result; } @@ -1340,25 +1336,57 @@ Script* Level::FindScript(const MClass* type) CHECK_RETURN(type, nullptr); Script* result = nullptr; ScopeLock lock(ScenesLock); - for (int32 i = 0; result == nullptr && i < Scenes.Count(); i++) - { result = Scenes[i]->FindScript(type); + return result; +} + +namespace +{ + void GetActors(const MClass* type, Actor* actor, Array& result) + { + if (actor->GetClass()->IsSubClassOf(type)) + result.Add(actor); + for (auto child : actor->Children) + GetActors(type, child, result); } + void GetScripts(const MClass* type, Actor* actor, Array& result) + { + for (auto script : actor->Scripts) + if (script->GetClass()->IsSubClassOf(type)) + result.Add(script); + for (auto child : actor->Children) + GetScripts(type, child, result); + } +} + +Array Level::GetActors(const MClass* type) +{ + Array result; + CHECK_RETURN(type, result); + ScopeLock lock(ScenesLock); + for (int32 i = 0; i < Scenes.Count(); i++) + ::GetActors(type, Scenes[i], result); + return result; +} + +Array Level::GetScripts(const MClass* type) +{ + Array result; + CHECK_RETURN(type, result); + ScopeLock lock(ScenesLock); + for (int32 i = 0; i < Scenes.Count(); i++) + ::GetScripts(type, Scenes[i], result); return result; } Scene* Level::FindScene(const Guid& id) { ScopeLock lock(ScenesLock); - for (int32 i = 0; i < Scenes.Count(); i++) - { if (Scenes[i]->GetID() == id) return Scenes[i]; - } - return nullptr; } diff --git a/Source/Engine/Level/Level.cs b/Source/Engine/Level/Level.cs index 6232d2e54..1eafe08d5 100644 --- a/Source/Engine/Level/Level.cs +++ b/Source/Engine/Level/Level.cs @@ -77,5 +77,33 @@ namespace FlaxEngine { return FindActor(id) as T; } + + /// + /// Finds all the scripts of the given type in all the loaded scenes. + /// + /// Type of the object. + /// Found scripts list. + public static T[] GetScripts() where T : Script + { + var scripts = GetScripts(typeof(T)); + var result = new T[scripts.Length]; + for (int i = 0; i < scripts.Length; i++) + result[i] = scripts[i] as T; + return result; + } + + /// + /// Finds all the actors of the given type in all the loaded scenes. + /// + /// Type of the object. + /// Found actors list. + public static T[] GetActors() where T : Actor + { + var actors = GetActors(typeof(T)); + var result = new T[actors.Length]; + for (int i = 0; i < actors.Length; i++) + result[i] = actors[i] as T; + return result; + } } } diff --git a/Source/Engine/Level/Level.h b/Source/Engine/Level/Level.h index b1ee090f4..5e274978a 100644 --- a/Source/Engine/Level/Level.h +++ b/Source/Engine/Level/Level.h @@ -355,7 +355,7 @@ public: /// /// Actor instance if found, null otherwise. template - FORCE_INLINE T* FindActor() const + FORCE_INLINE static T* FindActor() { return (T*)FindActor(T::GetStaticClass()); } @@ -372,11 +372,25 @@ public: /// /// Script instance if found, null otherwise. template - FORCE_INLINE T* FindScript() const + FORCE_INLINE static T* FindScript() { return (T*)FindScript(T::GetStaticClass()); } + /// + /// Finds all the actors of the given type in all the loaded scenes. + /// + /// Type of the actor to search for. Includes any actors derived from the type. + /// Found actors list. + API_FUNCTION() static Array GetActors(const MClass* type); + + /// + /// Finds all the scripts of the given type in all the loaded scenes. + /// + /// Type of the script to search for. Includes any scripts derived from the type. + /// Found scripts list. + API_FUNCTION() static Array GetScripts(const MClass* type); + /// /// Tries to find scene with given ID. /// diff --git a/Source/Engine/Level/Prefabs/Prefab.Apply.cpp b/Source/Engine/Level/Prefabs/Prefab.Apply.cpp index 84bd17962..c0029aa93 100644 --- a/Source/Engine/Level/Prefabs/Prefab.Apply.cpp +++ b/Source/Engine/Level/Prefabs/Prefab.Apply.cpp @@ -509,7 +509,7 @@ bool FindCyclicReferences(Actor* actor, const Guid& prefabRootId) bool Prefab::ApplyAll(Actor* targetActor) { - // TODO: use more cached dictionaries and other collections containers to prevent memory allocations during apply (optimize fo apply 10 times per second the same prefab on many changes in editor) + // TODO: use more cached dictionaries and other collections containers to prevent memory allocations during apply (optimize for apply 10 times per second the same prefab on many changes in editor) PROFILE_CPU(); const auto startTime = DateTime::NowUTC(); @@ -850,7 +850,11 @@ bool Prefab::ApplyAllInternal(Actor* targetActor, bool linkTargetActorObjectToPr } // Keep root unlinked - root->_parent = nullptr; + if (root->_parent) + { + root->_parent->Children.Remove(root); + root->_parent = nullptr; + } } // Link objects hierarchy diff --git a/Source/Engine/Level/Prefabs/PrefabManager.cpp b/Source/Engine/Level/Prefabs/PrefabManager.cpp index f1fbf4766..512d6e9b7 100644 --- a/Source/Engine/Level/Prefabs/PrefabManager.cpp +++ b/Source/Engine/Level/Prefabs/PrefabManager.cpp @@ -173,9 +173,16 @@ Actor* PrefabManager::SpawnPrefab(Prefab* prefab, Actor* parent, DictionaryAt(0); + if (!root) + { + LOG(Warning, "Failed to load prefab root object."); + return nullptr; + } // Prepare parent linkage for prefab root actor root->_parent = parent; + if (parent) + parent->Children.Add(root); // Link actors hierarchy for (int32 i = 0; i < sceneObjects->Count(); i++) @@ -221,7 +228,7 @@ Actor* PrefabManager::SpawnPrefab(Prefab* prefab, Actor* parent, DictionaryGetParent() == nullptr) { sceneObjects->At(i) = nullptr; - LOG(Warning, "Scene object {0} {1} has missing parent objct after prefab spawn. Removing it.", obj->GetID(), obj->ToString()); + LOG(Warning, "Scene object {0} {1} has missing parent object after load. Removing it.", obj->GetID(), obj->ToString()); obj->DeleteObject(); } } diff --git a/Source/Engine/Navigation/NavMesh.cpp b/Source/Engine/Navigation/NavMesh.cpp index 406aada30..d0bdba81f 100644 --- a/Source/Engine/Navigation/NavMesh.cpp +++ b/Source/Engine/Navigation/NavMesh.cpp @@ -79,7 +79,7 @@ void NavMesh::EnsureCapacity(int32 tilesToAddCount) // Ensure to have size assigned ASSERT(_tileSize != 0); - // Fre previous data (if any) + // Free previous data (if any) if (_navMesh) { dtFreeNavMesh(_navMesh); diff --git a/Source/Engine/Navigation/NavMeshBuilder.cpp b/Source/Engine/Navigation/NavMeshBuilder.cpp index b8e9a7544..2220d2cb6 100644 --- a/Source/Engine/Navigation/NavMeshBuilder.cpp +++ b/Source/Engine/Navigation/NavMeshBuilder.cpp @@ -4,7 +4,6 @@ #include "NavMeshBuilder.h" #include "Engine/Core/Math/BoundingBox.h" -#include "Engine/Core/Math/VectorInt.h" #include "Engine/Physics/Colliders/BoxCollider.h" #include "Engine/Physics/Colliders/MeshCollider.h" #include "Engine/Threading/ThreadPoolTask.h" @@ -15,6 +14,7 @@ #include "Engine/Level/Level.h" #include "Engine/Level/SceneQuery.h" #include "Engine/Core/Log.h" +#include "Engine/Core/Math/Int3.h" #include "NavigationScene.h" #include "NavigationSettings.h" #include "NavMeshBoundsVolume.h" diff --git a/Source/Engine/Particles/Graph/CPU/ParticleEmitterGraph.CPU.ParticleModules.cpp b/Source/Engine/Particles/Graph/CPU/ParticleEmitterGraph.CPU.ParticleModules.cpp index bebe80479..7aa68e32e 100644 --- a/Source/Engine/Particles/Graph/CPU/ParticleEmitterGraph.CPU.ParticleModules.cpp +++ b/Source/Engine/Particles/Graph/CPU/ParticleEmitterGraph.CPU.ParticleModules.cpp @@ -69,7 +69,7 @@ namespace scale *= 1.72531f; } - return noise / weight; + return noise / Math::Max(weight, ZeroTolerance); } VariantType::Types GetVariantType(ParticleAttribute::ValueTypes type) @@ -486,7 +486,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* float particleDrag = drag; \ if (useSpriteSize) \ particleDrag *= ((Vector2*)spriteSizePtr)->MulValues(); \ - *((Vector3*)velocityPtr) *= Math::Max(0.0f, 1.0f - (particleDrag * _deltaTime) / *(float*)massPtr); \ + *((Vector3*)velocityPtr) *= Math::Max(0.0f, 1.0f - (particleDrag * _deltaTime) / Math::Max(*(float*)massPtr, ZeroTolerance)); \ velocityPtr += stride; \ massPtr += stride; \ spriteSizePtr += stride @@ -545,7 +545,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* Vector3 vectorFieldUVW = Vector3::Transform(*((Vector3*)positionPtr), invFieldTransformMatrix); \ Vector3 force = Noise3D(vectorFieldUVW + 0.5f, octavesCount, roughness); \ force = Vector3::Transform(force, fieldTransformMatrix) * intensity; \ - *((Vector3*)velocityPtr) += force * (_deltaTime / *(float*)massPtr); \ + *((Vector3*)velocityPtr) += force * (_deltaTime / Math::Max(*(float*)massPtr, ZeroTolerance)); \ positionPtr += stride; \ velocityPtr += stride; \ massPtr += stride @@ -1009,7 +1009,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* #define INPUTS_FETCH() \ const Vector3 center = (Vector3)GetValue(centerBox, 2); \ - const float radius = (float)GetValue(radiusBox, 3); \ + const float radius = Math::Max((float)GetValue(radiusBox, 3), ZeroTolerance); \ const float thickness = (float)GetValue(thicknessBox, 4); \ const float arc = (float)GetValue(arcBox, 5) * DegreesToRadians #define LOGIC() \ @@ -1017,20 +1017,20 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* float sinTheta, cosTheta; \ Math::SinCos(u.X * TWO_PI, sinTheta, cosTheta); \ float r = Math::Saturate(thickness / radius); \ - Vector2 s1_1 = r * Vector2(cosTheta, sinTheta) + Vector2(1, 0); \ - Vector2 s1_2 = r * Vector2(-cosTheta, sinTheta) + Vector2(1, 0); \ - float w = s1_1.X / (s1_1.X + s1_2.X); \ + Vector2 s11 = r * Vector2(cosTheta, sinTheta) + Vector2(1, 0); \ + Vector2 s12 = r * Vector2(-cosTheta, sinTheta) + Vector2(1, 0); \ + float w = s11.X / (s11.X + s12.X); \ Vector3 t; \ float phi; \ if (u.Y < w) \ { \ phi = arc * u.Y / w; \ - t = Vector3(s1_1.X, 0, s1_1.Y); \ + t = Vector3(s11.X, 0, s11.Y); \ } \ else \ { \ phi = arc * (u.Y - w) / (1.0f - w); \ - t = Vector3(s1_2.X, 0, s1_2.Y); \ + t = Vector3(s12.X, 0, s12.Y); \ } \ float s, c; \ Math::SinCos(phi, c, s); \ @@ -1262,7 +1262,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* if (sign * sqrLength <= sign * totalRadius * totalRadius) \ { \ float dist = Math::Sqrt(sqrLength); \ - Vector3 n = sign * dir / dist; \ + Vector3 n = sign * dir / Math::Max(dist, ZeroTolerance); \ *(Vector3*)positionPtr = position - n * (dist - totalRadius) * sign; \ COLLISION_LOGIC() @@ -1374,7 +1374,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* collision = Math::Abs(dir.Y) > halfHeight || sqrLength > cylinderRadiusT * cylinderRadiusT; \ if (collision) \ { \ - float dist = Math::Sqrt(sqrLength); \ + float dist = Math::Max(Math::Sqrt(sqrLength), ZeroTolerance); \ float distToCap = sign * (halfHeight - Math::Abs(dir.Y)); \ float distToSide = sign * (cylinderRadiusT - dist); \ Vector3 n = Vector3(dir.X / dist, Math::Sign(dir.Y), dir.Z / dist) * sign; \ diff --git a/Source/Engine/Particles/Graph/CPU/ParticleEmitterGraph.CPU.Particles.cpp b/Source/Engine/Particles/Graph/CPU/ParticleEmitterGraph.CPU.Particles.cpp index 08db1c0d3..d56bf6d5c 100644 --- a/Source/Engine/Particles/Graph/CPU/ParticleEmitterGraph.CPU.Particles.cpp +++ b/Source/Engine/Particles/Graph/CPU/ParticleEmitterGraph.CPU.Particles.cpp @@ -284,7 +284,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessGroupParticles(Box* box, Node* node { const float age = GET_PARTICLE_ATTRIBUTE(0, float); const float lifetime = GET_PARTICLE_ATTRIBUTE(1, float); - value = age / lifetime; + value = age / Math::Max(lifetime, ZeroTolerance); break; } // Effect Position diff --git a/Source/Engine/Particles/Graph/CPU/ParticleEmitterGraph.CPU.h b/Source/Engine/Particles/Graph/CPU/ParticleEmitterGraph.CPU.h index a93c50ec2..42aca7dbb 100644 --- a/Source/Engine/Particles/Graph/CPU/ParticleEmitterGraph.CPU.h +++ b/Source/Engine/Particles/Graph/CPU/ParticleEmitterGraph.CPU.h @@ -100,7 +100,7 @@ public: public: /// - /// Determinates whenever this emitter uses lights rendering. + /// Determines whenever this emitter uses lights rendering. /// /// True if emitter uses lights rendering, otherwise false. FORCE_INLINE bool UsesLightRendering() const diff --git a/Source/Engine/Particles/Graph/GPU/ParticleEmitterGraph.GPU.Parameters.cpp b/Source/Engine/Particles/Graph/GPU/ParticleEmitterGraph.GPU.Parameters.cpp deleted file mode 100644 index bcb672eb3..000000000 --- a/Source/Engine/Particles/Graph/GPU/ParticleEmitterGraph.GPU.Parameters.cpp +++ /dev/null @@ -1,98 +0,0 @@ -// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. - -#if COMPILE_WITH_PARTICLE_GPU_GRAPH - -#include "ParticleEmitterGraph.GPU.h" - -void ParticleEmitterGPUGenerator::ProcessGroupParameters(Box* box, Node* node, Value& value) -{ - switch (node->TypeID) - { - // Get - case 1: - case 2: - { - // Get parameter - const auto param = findParam((Guid)node->Values[0]); - if (param) - { - switch (param->Type) - { - case MaterialParameterType::Bool: - value = Value(VariantType::Bool, param->ShaderName); - break; - case MaterialParameterType::Integer: - case MaterialParameterType::SceneTexture: - value = Value(VariantType::Int, param->ShaderName); - break; - case MaterialParameterType::Float: - value = Value(VariantType::Float, param->ShaderName); - break; - case MaterialParameterType::Vector2: - case MaterialParameterType::Vector3: - case MaterialParameterType::Vector4: - case MaterialParameterType::Color: - { - // Set result values based on box ID - const Value sample(box->Type.Type, param->ShaderName); - switch (box->ID) - { - case 0: - value = sample; - break; - case 1: - value.Value = sample.Value + _subs[0]; - break; - case 2: - value.Value = sample.Value + _subs[1]; - break; - case 3: - value.Value = sample.Value + _subs[2]; - break; - case 4: - value.Value = sample.Value + _subs[3]; - break; - default: CRASH; - break; - } - value.Type = box->Type.Type; - break; - } - - case MaterialParameterType::Matrix: - { - value = Value(box->Type.Type, String::Format(TEXT("{0}[{1}]"), param->ShaderName, box->ID)); - break; - } - case MaterialParameterType::ChannelMask: - { - const auto input = tryGetValue(node->GetBox(0), Value::Zero); - value = writeLocal(VariantType::Float, String::Format(TEXT("dot({0}, {1})"), input.Value, param->ShaderName), node); - break; - } - case MaterialParameterType::CubeTexture: - case MaterialParameterType::Texture: - case MaterialParameterType::GPUTextureArray: - case MaterialParameterType::GPUTextureCube: - case MaterialParameterType::GPUTextureVolume: - case MaterialParameterType::GPUTexture: - value = Value(VariantType::Object, param->ShaderName); - break; - default: - CRASH; - break; - } - } - else - { - OnError(node, box, String::Format(TEXT("Missing graph parameter {0}."), node->Values[0])); - value = Value::Zero; - } - break; - } - default: - break; - } -} - -#endif diff --git a/Source/Engine/Particles/Graph/GPU/ParticleEmitterGraph.GPU.ParticleModules.cpp b/Source/Engine/Particles/Graph/GPU/ParticleEmitterGraph.GPU.ParticleModules.cpp index 23dac1622..57d4ededc 100644 --- a/Source/Engine/Particles/Graph/GPU/ParticleEmitterGraph.GPU.ParticleModules.cpp +++ b/Source/Engine/Particles/Graph/GPU/ParticleEmitterGraph.GPU.ParticleModules.cpp @@ -177,7 +177,7 @@ void ParticleEmitterGPUGenerator::ProcessModule(Node* node) " {{\n" " // Linear Drag\n" " float drag = {2} * {3}.x * {3}.y;\n" - " {0} *= max(0.0f, 1.0f - (drag * DeltaTime) / {1});\n" + " {0} *= max(0.0f, 1.0f - (drag * DeltaTime) / max({1}, PARTICLE_THRESHOLD));\n" " }}\n" ), velocity.Value, mass.Value, drag.Value, spriteSize.Value); } @@ -188,7 +188,7 @@ void ParticleEmitterGPUGenerator::ProcessModule(Node* node) " {{\n" " // Linear Drag\n" " float drag = {2};\n" - " {0} *= max(0.0f, 1.0f - (drag * DeltaTime) / {1});\n" + " {0} *= max(0.0f, 1.0f - (drag * DeltaTime) / max({1}, PARTICLE_THRESHOLD));\n" " }}\n" ), velocity.Value, mass.Value, drag.Value); } @@ -219,7 +219,7 @@ void ParticleEmitterGPUGenerator::ProcessModule(Node* node) " float3 vectorFieldUVW = mul(invFieldTransformMatrix, float4({0}, 1.0f)).xyz;\n" " float3 force = Noise3D(vectorFieldUVW + 0.5f, {8}, {6});\n" " force = mul(fieldTransformMatrix, float4(force, 0.0f)).xyz * {7};\n" - " {1} += force * (DeltaTime / {2});\n" + " {1} += force * (DeltaTime / max({2}, PARTICLE_THRESHOLD));\n" " }}\n" ), position.Value, velocity.Value, mass.Value, fieldPosition.Value, fieldRotation.Value, fieldScale.Value, roughness.Value, intensity.Value, octavesCount.Value); break; @@ -486,21 +486,21 @@ void ParticleEmitterGPUGenerator::ProcessModule(Node* node) " float3 u = RAND3;\n" " float sinTheta, cosTheta;\n" " sincos(u.x * PI * 2.0f, sinTheta, cosTheta);\n" - " float r = saturate((float){4} / {3});\n" - " float2 s1_1 = r * float2( cosTheta, sinTheta) + float2(1, 0);\n" - " float2 s1_2 = r * float2(-cosTheta, sinTheta) + float2(1, 0);\n" - " float w = s1_1.x / (s1_1.x + s1_2.x);\n" + " float r = saturate((float){4} / max({3}, PARTICLE_THRESHOLD));\n" + " float2 s11 = r * float2( cosTheta, sinTheta) + float2(1, 0);\n" + " float2 s12 = r * float2(-cosTheta, sinTheta) + float2(1, 0);\n" + " float w = s11.x / (s11.x + s12.x);\n" " float3 t;\n" " float phi;\n" " if (u.y < w)\n" " {{\n" " phi = radians({5}) * u.y / w;\n" - " t = float3(s1_1.x, 0, s1_1.y);\n" + " t = float3(s11.x, 0, s11.y);\n" " }}\n" " else\n" " {{\n" " phi = radians({5}) * (u.y - w) / (1.0f - w);\n" - " t = float3(s1_2.x, 0, s1_2.y);\n" + " t = float3(s12.x, 0, s12.y);\n" " }}\n" " float s, c;\n" " sincos(phi, c, s);\n" @@ -693,7 +693,7 @@ void ParticleEmitterGPUGenerator::ProcessModule(Node* node) " if ({4} * sqrLength <= {4} * totalRadius * totalRadius)\n" " {{\n" " float dist = sqrt(sqrLength);\n" - " float3 n = {4} * dir / dist;\n" + " float3 n = {4} * dir / max(dist, PARTICLE_THRESHOLD);\n" " {0} -= n * (dist - totalRadius) * {4};\n" COLLISION_LOGIC() " }}\n" @@ -787,7 +787,7 @@ void ParticleEmitterGPUGenerator::ProcessModule(Node* node) " collision = abs(dir.y) > halfHeight || sqrLength > cylinderRadiusT * cylinderRadiusT;\n" " if (collision)\n" " {{\n" - " float dist = sqrt(sqrLength);\n" + " float dist = max(sqrt(sqrLength), PARTICLE_THRESHOLD);\n" " float distToCap = {4} * (halfHeight - abs(dir.y));\n" " float distToSide = {4} * (cylinderRadiusT - dist);\n" " float3 n = float3(dir.x / dist, sign(dir.y), dir.z / dist) * {4};\n" diff --git a/Source/Engine/Particles/Graph/GPU/ParticleEmitterGraph.GPU.Particles.cpp b/Source/Engine/Particles/Graph/GPU/ParticleEmitterGraph.GPU.Particles.cpp index 4839d0415..08e7b24ca 100644 --- a/Source/Engine/Particles/Graph/GPU/ParticleEmitterGraph.GPU.Particles.cpp +++ b/Source/Engine/Particles/Graph/GPU/ParticleEmitterGraph.GPU.Particles.cpp @@ -113,6 +113,131 @@ ParticleEmitterGPUGenerator::Value ParticleEmitterGPUGenerator::AccessParticleAt return value.Variable; } +void ParticleEmitterGPUGenerator::ProcessGroupParameters(Box* box, Node* node, Value& value) +{ + switch (node->TypeID) + { + // Get + case 1: + case 2: + { + // Get parameter + const auto param = findParam((Guid)node->Values[0]); + if (param) + { + switch (param->Type) + { + case MaterialParameterType::Bool: + value = Value(VariantType::Bool, param->ShaderName); + break; + case MaterialParameterType::Integer: + case MaterialParameterType::SceneTexture: + value = Value(VariantType::Int, param->ShaderName); + break; + case MaterialParameterType::Float: + value = Value(VariantType::Float, param->ShaderName); + break; + case MaterialParameterType::Vector2: + case MaterialParameterType::Vector3: + case MaterialParameterType::Vector4: + case MaterialParameterType::Color: + { + // Set result values based on box ID + const Value sample(box->Type.Type, param->ShaderName); + switch (box->ID) + { + case 0: + value = sample; + break; + case 1: + value.Value = sample.Value + _subs[0]; + break; + case 2: + value.Value = sample.Value + _subs[1]; + break; + case 3: + value.Value = sample.Value + _subs[2]; + break; + case 4: + value.Value = sample.Value + _subs[3]; + break; + default: CRASH; + break; + } + value.Type = box->Type.Type; + break; + } + + case MaterialParameterType::Matrix: + { + value = Value(box->Type.Type, String::Format(TEXT("{0}[{1}]"), param->ShaderName, box->ID)); + break; + } + case MaterialParameterType::ChannelMask: + { + const auto input = tryGetValue(node->GetBox(0), Value::Zero); + value = writeLocal(VariantType::Float, String::Format(TEXT("dot({0}, {1})"), input.Value, param->ShaderName), node); + break; + } + case MaterialParameterType::CubeTexture: + case MaterialParameterType::Texture: + case MaterialParameterType::GPUTextureArray: + case MaterialParameterType::GPUTextureCube: + case MaterialParameterType::GPUTextureVolume: + case MaterialParameterType::GPUTexture: + value = Value(VariantType::Object, param->ShaderName); + break; + default: + CRASH; + break; + } + } + else + { + OnError(node, box, String::Format(TEXT("Missing graph parameter {0}."), node->Values[0])); + value = Value::Zero; + } + break; + } + default: + break; + } +} + +void ParticleEmitterGPUGenerator::ProcessGroupTools(Box* box, Node* node, Value& value) +{ + switch (node->TypeID) + { + // Linearize Depth + case 7: + { + // Get input + const Value depth = tryGetValue(node->GetBox(0), Value::Zero).AsFloat(); + + // Linearize raw device depth + linearizeSceneDepth(node, depth, value); + break; + } + // Time + case 8: + value = box->ID == 0 ? Value(VariantType::Float, TEXT("Time")) : Value(VariantType::Float, TEXT("DeltaTime")); + break; + // Transform Position To Screen UV + case 9: + { + const Value position = tryGetValue(node->GetBox(0), Value::Zero).AsVector3(); + const Value projPos = writeLocal(VariantType::Vector4, String::Format(TEXT("mul(float4({0}, 1.0f), ViewProjectionMatrix)"), position.Value), node); + _writer.Write(TEXT("\t{0}.xy /= {0}.w;\n"), projPos.Value); + _writer.Write(TEXT("\t{0}.xy = {0}.xy * 0.5f + 0.5f;\n"), projPos.Value); + value = Value(VariantType::Vector2, projPos.Value + TEXT(".xy")); + break; + } + default: + ShaderGenerator::ProcessGroupTools(box, node, value); + break; + } +} + void ParticleEmitterGPUGenerator::ProcessGroupParticles(Box* box, Node* node, Value& value) { switch (node->TypeID) diff --git a/Source/Engine/Particles/Graph/GPU/ParticleEmitterGraph.GPU.Textures.cpp b/Source/Engine/Particles/Graph/GPU/ParticleEmitterGraph.GPU.Textures.cpp index 61273b3a1..e1d864b4e 100644 --- a/Source/Engine/Particles/Graph/GPU/ParticleEmitterGraph.GPU.Textures.cpp +++ b/Source/Engine/Particles/Graph/GPU/ParticleEmitterGraph.GPU.Textures.cpp @@ -256,10 +256,8 @@ void ParticleEmitterGPUGenerator::ProcessGroupTextures(Box* box, Node* node, Val } // Scene Depth case 8: - { sampleSceneDepth(node, value, box); break; - } // Texture case 11: { diff --git a/Source/Engine/Particles/Graph/GPU/ParticleEmitterGraph.GPU.Tools.cpp b/Source/Engine/Particles/Graph/GPU/ParticleEmitterGraph.GPU.Tools.cpp deleted file mode 100644 index 299e1a422..000000000 --- a/Source/Engine/Particles/Graph/GPU/ParticleEmitterGraph.GPU.Tools.cpp +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. - -#if COMPILE_WITH_PARTICLE_GPU_GRAPH - -#include "ParticleEmitterGraph.GPU.h" - -void ParticleEmitterGPUGenerator::ProcessGroupTools(Box* box, Node* node, Value& value) -{ - switch (node->TypeID) - { - // Linearize Depth - case 7: - { - // Get input - const Value depth = tryGetValue(node->GetBox(0), Value::Zero).AsFloat(); - - // Linearize raw device depth - linearizeSceneDepth(node, depth, value); - break; - } - // Time - case 8: - { - value = box->ID == 0 ? Value(VariantType::Float, TEXT("Time")) : Value(VariantType::Float, TEXT("DeltaTime")); - break; - } - // Transform Position To Screen UV - case 9: - { - const Value position = tryGetValue(node->GetBox(0), Value::Zero).AsVector3(); - const Value projPos = writeLocal(VariantType::Vector4, String::Format(TEXT("mul(float4({0}, 1.0f), ViewProjectionMatrix)"), position.Value), node); - _writer.Write(TEXT("\t{0}.xy /= {0}.w;\n"), projPos.Value); - _writer.Write(TEXT("\t{0}.xy = {0}.xy * 0.5f + 0.5f;\n"), projPos.Value); - value = Value(VariantType::Vector2, projPos.Value + TEXT(".xy")); - break; - } - default: - ShaderGenerator::ProcessGroupTools(box, node, value); - break; - } -} - -#endif diff --git a/Source/Engine/Particles/Graph/ParticleEmitterGraph.h b/Source/Engine/Particles/Graph/ParticleEmitterGraph.h index 0f92d5f22..76af84011 100644 --- a/Source/Engine/Particles/Graph/ParticleEmitterGraph.h +++ b/Source/Engine/Particles/Graph/ParticleEmitterGraph.h @@ -103,7 +103,7 @@ protected: public: /// - /// The Particle Emitter Graph data version number. Used to sync the Particle Emitter Graph data with the instances state. Handles graph reloads to enure data is valid. + /// The Particle Emitter Graph data version number. Used to sync the Particle Emitter Graph data with the instances state. Handles graph reloads to ensure data is valid. /// uint32 Version = 0; diff --git a/Source/Engine/Particles/ParticleManager.cpp b/Source/Engine/Particles/ParticleManager.cpp index 0e5640545..40cebd892 100644 --- a/Source/Engine/Particles/ParticleManager.cpp +++ b/Source/Engine/Particles/ParticleManager.cpp @@ -1172,7 +1172,7 @@ void ParticleManagerService::Update() // Update bounds after first system update updateBounds = true; } - // TODO: if using fixed timestep quantize the dt and accumulate reaming part for the next update? + // TODO: if using fixed timestep quantize the dt and accumulate remaining part for the next update? if (dt <= 1.0f / 240.0f) continue; dt *= effect->SimulationSpeed; diff --git a/Source/Engine/Particles/ParticlesSimulation.cpp b/Source/Engine/Particles/ParticlesSimulation.cpp index bbba833ff..0b6e3691d 100644 --- a/Source/Engine/Particles/ParticlesSimulation.cpp +++ b/Source/Engine/Particles/ParticlesSimulation.cpp @@ -115,7 +115,7 @@ int32 ParticleSystemInstance::GetParticlesCount() const const auto desc = GPUBufferDescription::Buffer(Emitters.Count() * sizeof(uint32), GPUBufferFlags::None, PixelFormat::Unknown, nullptr, sizeof(uint32), GPUResourceUsage::StagingReadback); if (GPUParticlesCountReadback->Init(desc)) { - LOG(Error, "Failed to create GPU particles coun readback buffer."); + LOG(Error, "Failed to create GPU particles count readback buffer."); } } diff --git a/Source/Engine/Physics/Actors/IPhysicsActor.h b/Source/Engine/Physics/Actors/IPhysicsActor.h index 8aebe3aeb..28f7fddf4 100644 --- a/Source/Engine/Physics/Actors/IPhysicsActor.h +++ b/Source/Engine/Physics/Actors/IPhysicsActor.h @@ -2,7 +2,11 @@ #pragma once -#include +namespace physx +{ + class PxRigidActor; + class PxTransform; +} /// /// A base interface for all physical actors types/owners that can responds on transformation changed event. @@ -20,7 +24,7 @@ public: /// Gets the rigid actor (PhysX object) may be null. /// /// PhysX rigid actor or null if not using - virtual PxRigidActor* GetRigidActor() = 0; + virtual physx::PxRigidActor* GetRigidActor() = 0; /// /// Called when actor's active transformation gets changed after the physics simulation step. diff --git a/Source/Engine/Physics/Colliders/CharacterController.cpp b/Source/Engine/Physics/Colliders/CharacterController.cpp index 6c75879ad..79410bdf4 100644 --- a/Source/Engine/Physics/Colliders/CharacterController.cpp +++ b/Source/Engine/Physics/Colliders/CharacterController.cpp @@ -29,6 +29,7 @@ CharacterController::CharacterController(const SpawnParams& params) , _nonWalkableMode(CharacterController::NonWalkableModes::PreventClimbing) , _lastFlags(CollisionFlags::None) { + static_assert(sizeof(_filterData) == sizeof(PxFilterData), "Invalid filter data size."); } void CharacterController::SetRadius(const float value) @@ -55,7 +56,7 @@ void CharacterController::SetHeight(const float value) void CharacterController::SetSlopeLimit(float value) { - value = Math::Clamp(value, 0.0f, 90.0f); + value = Math::Clamp(value, 0.0f, 89.0f); if (Math::NearEqual(value, _slopeLimit)) return; @@ -112,7 +113,7 @@ CharacterController::CollisionFlags CharacterController::Move(const Vector3& dis { const float deltaTime = Time::GetCurrentSafe()->DeltaTime.GetTotalSeconds(); PxControllerFilters filters; - filters.mFilterData = &_filterData; + filters.mFilterData = (PxFilterData*)&_filterData; filters.mFilterCallback = Physics::GetCharacterQueryFilterCallback(); filters.mFilterFlags = PxQueryFlag::eDYNAMIC | PxQueryFlag::eSTATIC | PxQueryFlag::ePREFILTER; @@ -177,7 +178,7 @@ void CharacterController::CreateActor() const float scaling = _cachedScale.GetAbsolute().MaxValue(); const float minSize = 0.001f; desc.height = Math::Max(Math::Abs(_height) * scaling, minSize); - desc.radius = Math::Max(Math::Abs(_radius) * scaling, minSize); + desc.radius = Math::Max(Math::Abs(_radius) * scaling - desc.contactOffset, minSize); // Create controller _controller = (PxCapsuleController*)Physics::GetControllerManager()->createController(desc); @@ -202,7 +203,7 @@ void CharacterController::UpdateSize() const { const float scaling = _cachedScale.GetAbsolute().MaxValue(); const float minSize = 0.001f; - const float radius = Math::Max(Math::Abs(_radius) * scaling, minSize); + const float radius = Math::Max(Math::Abs(_radius) * scaling - Math::Max(_contactOffset, ZeroTolerance), minSize); const float height = Math::Max(Math::Abs(_height) * scaling, minSize); _controller->setRadius(radius); @@ -281,7 +282,7 @@ void CharacterController::UpdateLayerBits() Collider::UpdateLayerBits(); // Cache filter data - _filterData = _shape->getSimulationFilterData(); + *(PxFilterData*)&_filterData = _shape->getSimulationFilterData(); } void CharacterController::BeginPlay(SceneBeginData* data) @@ -360,6 +361,11 @@ void CharacterController::OnTransformChanged() UpdateScale(); UpdateBounds(); } + else if (!_controller) + { + _box = BoundingBox(_transform.Translation, _transform.Translation); + BoundingSphere::FromBox(_box, _sphere); + } } void CharacterController::Serialize(SerializeStream& stream, const void* otherObj) diff --git a/Source/Engine/Physics/Colliders/CharacterController.h b/Source/Engine/Physics/Colliders/CharacterController.h index e79cea5e3..3959e8ad8 100644 --- a/Source/Engine/Physics/Colliders/CharacterController.h +++ b/Source/Engine/Physics/Colliders/CharacterController.h @@ -4,7 +4,6 @@ #include "Collider.h" #include "Engine/Physics/Actors/IPhysicsActor.h" -#include /// /// Physical objects that allows to easily do player movement constrained by collisions without having to deal with a rigidbody. @@ -68,7 +67,7 @@ private: bool _isUpdatingTransform; NonWalkableModes _nonWalkableMode; CollisionFlags _lastFlags; - PxFilterData _filterData; + uint32 _filterData[4]; public: diff --git a/Source/Engine/Physics/Colliders/Collider.h b/Source/Engine/Physics/Colliders/Collider.h index 8c6eeb59a..c836a7886 100644 --- a/Source/Engine/Physics/Colliders/Collider.h +++ b/Source/Engine/Physics/Colliders/Collider.h @@ -175,7 +175,7 @@ public: } /// - /// Attaches collider to the the specified rigid body. + /// Attaches collider to the specified rigid body. /// /// The rigid body. void Attach(RigidBody* rigidBody); diff --git a/Source/Engine/Physics/CollisionCooking.cpp b/Source/Engine/Physics/CollisionCooking.cpp index ea0f2f442..070f49936 100644 --- a/Source/Engine/Physics/CollisionCooking.cpp +++ b/Source/Engine/Physics/CollisionCooking.cpp @@ -13,7 +13,7 @@ #define ENSURE_CAN_COOK \ if (Physics::GetCooking() == nullptr) \ { \ - LOG(Warning, "Physics collisions cooking is disabled at runtime. Enable Physics Settigns option SupportCookingAtRuntime to use terrain generation at runtime."); \ + LOG(Warning, "Physics collisions cooking is disabled at runtime. Enable Physics Settings option SupportCookingAtRuntime to use terrain generation at runtime."); \ return true; \ } diff --git a/Source/Engine/Platform/Base/PlatformBase.cpp b/Source/Engine/Platform/Base/PlatformBase.cpp index 2a117dc34..7401dc3bf 100644 --- a/Source/Engine/Platform/Base/PlatformBase.cpp +++ b/Source/Engine/Platform/Base/PlatformBase.cpp @@ -19,6 +19,7 @@ #include "Engine/Engine/CommandLine.h" #include "Engine/Engine/Engine.h" #include "Engine/Utilities/StringConverter.h" +#include "Engine/Platform/BatteryInfo.h" #include // Check types sizes @@ -381,6 +382,11 @@ void PlatformBase::SetHighDpiAwarenessEnabled(bool enable) { } +BatteryInfo PlatformBase::GetBatteryInfo() +{ + return BatteryInfo(); +} + int32 PlatformBase::GetDpi() { return 96; diff --git a/Source/Engine/Platform/Base/PlatformBase.h b/Source/Engine/Platform/Base/PlatformBase.h index f7162b587..d2d34c88f 100644 --- a/Source/Engine/Platform/Base/PlatformBase.h +++ b/Source/Engine/Platform/Base/PlatformBase.h @@ -11,6 +11,7 @@ struct CPUInfo; struct MemoryStats; struct ProcessMemoryStats; struct CreateWindowSettings; +struct BatteryInfo; // ReSharper disable CppFunctionIsNotImplemented @@ -548,6 +549,11 @@ public: /// static void SetHighDpiAwarenessEnabled(bool enable); + /// + /// Gets the battery information. + /// + API_PROPERTY() static BatteryInfo GetBatteryInfo(); + /// /// Gets the screen DPI setting. /// diff --git a/Source/Engine/Platform/Base/WindowBase.h b/Source/Engine/Platform/Base/WindowBase.h index 3da48bb7a..a8215bc8b 100644 --- a/Source/Engine/Platform/Base/WindowBase.h +++ b/Source/Engine/Platform/Base/WindowBase.h @@ -284,6 +284,7 @@ protected: Vector2 _trackingMouseOffset; bool _isUsingMouseOffset; + Rectangle _mouseOffsetScreenSize; bool _isTrackingMouse; explicit WindowBase(const CreateWindowSettings& settings); diff --git a/Source/Engine/Platform/Base/WindowsManager.cpp b/Source/Engine/Platform/Base/WindowsManager.cpp index b49cbd058..75082c4f9 100644 --- a/Source/Engine/Platform/Base/WindowsManager.cpp +++ b/Source/Engine/Platform/Base/WindowsManager.cpp @@ -77,7 +77,7 @@ void WindowsManagerService::Update() void WindowsManagerService::Dispose() { - // Close reaming windows + // Close remaining windows WindowsManager::WindowsLocker.Lock(); auto windows = WindowsManager::Windows; for (auto& win : windows) diff --git a/Source/Engine/Platform/BatteryInfo.h b/Source/Engine/Platform/BatteryInfo.h new file mode 100644 index 000000000..2ed6b5cac --- /dev/null +++ b/Source/Engine/Platform/BatteryInfo.h @@ -0,0 +1,49 @@ +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. + +#pragma once + +#include "Engine/Core/Types/BaseTypes.h" + +/// +/// Contains information about power supply (battery). +/// +API_STRUCT() struct BatteryInfo +{ +DECLARE_SCRIPTING_TYPE_MINIMAL(BatteryInfo); + + /// + /// Power supply status. + /// + API_ENUM() enum class States + { + /// + /// Unknown status. + /// + Unknown, + + /// + /// Power supply is connected and battery is charging. + /// + BatteryCharging, + + /// + /// Device is running on a battery. + /// + BatteryDischarging, + + /// + /// Device is connected to the stable power supply (AC). + /// + Connected, + }; + + /// + /// Power supply state. + /// + API_FIELD() BatteryInfo::States State = BatteryInfo::States::Unknown; + + /// + /// Battery percentage left (normalized to 0-1 range). + /// + API_FIELD() float BatteryLifePercent = 1.0f; +}; diff --git a/Source/Engine/Platform/Linux/LinuxPlatform.cpp b/Source/Engine/Platform/Linux/LinuxPlatform.cpp index f070c05f1..a68e40f72 100644 --- a/Source/Engine/Platform/Linux/LinuxPlatform.cpp +++ b/Source/Engine/Platform/Linux/LinuxPlatform.cpp @@ -346,7 +346,7 @@ static int X11_MessageBoxCreateWindow(MessageBoxData* data) { windowdata = data->Parent; windowdataWin = (X11::Window)windowdata->GetNativePtr(); - // TODO: place popup on the the screen that parent window is + // TODO: place popup on the screen that parent window is data->screen = X11_DefaultScreen(display); } else diff --git a/Source/Engine/Platform/Linux/LinuxWindow.cpp b/Source/Engine/Platform/Linux/LinuxWindow.cpp index 0ed5341ec..768df66bd 100644 --- a/Source/Engine/Platform/Linux/LinuxWindow.cpp +++ b/Source/Engine/Platform/Linux/LinuxWindow.cpp @@ -10,7 +10,7 @@ #include "Engine/Core/Log.h" #include "Engine/Core/Math/Math.h" #include "Engine/Core/Math/Color32.h" -#include "Engine/Core/Math/VectorInt.h" +#include "Engine/Core/Math/Int2.h" #include "Engine/Core/Collections/Array.h" #include "Engine/Core/Collections/Dictionary.h" #include "Engine/Utilities/StringConverter.h" diff --git a/Source/Engine/Platform/Win32/Win32Platform.cpp b/Source/Engine/Platform/Win32/Win32Platform.cpp index fc815b6ba..129ff4249 100644 --- a/Source/Engine/Platform/Win32/Win32Platform.cpp +++ b/Source/Engine/Platform/Win32/Win32Platform.cpp @@ -5,6 +5,7 @@ #include "Engine/Platform/Platform.h" #include "Engine/Platform/MemoryStats.h" #include "Engine/Platform/CPUInfo.h" +#include "Engine/Platform/BatteryInfo.h" #include "Engine/Core/Types/Guid.h" #include "Engine/Core/Types/String.h" #include "Engine/Core/Math/Math.h" @@ -15,6 +16,7 @@ #include #include #include +#include #pragma comment(lib, "Iphlpapi.lib") namespace @@ -308,6 +310,21 @@ bool Win32Platform::Is64BitPlatform() #endif } +BatteryInfo Win32Platform::GetBatteryInfo() +{ + BatteryInfo info; + SYSTEM_POWER_STATUS status; + GetSystemPowerStatus(&status); + info.BatteryLifePercent = (float)status.BatteryLifePercent / 255.0f; + if (status.BatteryFlag & 8) + info.State = BatteryInfo::States::BatteryCharging; + else if (status.BatteryFlag & 1 || status.BatteryFlag & 2 || status.BatteryFlag & 4) + info.State = BatteryInfo::States::BatteryDischarging; + else if (status.ACLineStatus == 1 || status.BatteryFlag & 128) + info.State = BatteryInfo::States::Connected; + return info; +} + CPUInfo Win32Platform::GetCPUInfo() { return CpuInfo; diff --git a/Source/Engine/Platform/Win32/Win32Platform.h b/Source/Engine/Platform/Win32/Win32Platform.h index 52ddeb25b..b6a4cdd62 100644 --- a/Source/Engine/Platform/Win32/Win32Platform.h +++ b/Source/Engine/Platform/Win32/Win32Platform.h @@ -43,6 +43,7 @@ public: _aligned_free(ptr); } static bool Is64BitPlatform(); + static BatteryInfo GetBatteryInfo(); static CPUInfo GetCPUInfo(); static int32 GetCacheLineSize(); static MemoryStats GetMemoryStats(); diff --git a/Source/Engine/Platform/Windows/WindowsFileSystem.cpp b/Source/Engine/Platform/Windows/WindowsFileSystem.cpp index 0c0d9788c..1816c9552 100644 --- a/Source/Engine/Platform/Windows/WindowsFileSystem.cpp +++ b/Source/Engine/Platform/Windows/WindowsFileSystem.cpp @@ -5,6 +5,7 @@ #include "WindowsFileSystem.h" #include "Engine/Platform/File.h" #include "Engine/Platform/Window.h" +#include "Engine/Platform/Windows/ComPtr.h" #include "Engine/Core/Types/StringView.h" #include "../Win32/IncludeWindowsHeaders.h" @@ -231,7 +232,9 @@ bool WindowsFileSystem::ShowOpenFileDialog(Window* parentWindow, const StringVie while (*ptr) { filenames.Add(directory / ptr); - ptr += (lstrlenW(ptr) + 1); + ptr += lstrlenW(ptr); + if (multiSelect) + ptr++; } result = false; @@ -276,7 +279,9 @@ bool WindowsFileSystem::ShowSaveFileDialog(Window* parentWindow, const StringVie while (*ptr) { filenames.Add(directory / ptr); - ptr += (lstrlenW(ptr) + 1); + ptr += lstrlenW(ptr); + if (multiSelect) + ptr++; } result = false; @@ -289,41 +294,39 @@ bool WindowsFileSystem::ShowBrowseFolderDialog(Window* parentWindow, const Strin { bool result = true; - // Allocate memory for the filenames - int32 maxPathSize = 2 * MAX_PATH; - Array pathBuffer; - pathBuffer.Resize(maxPathSize); - pathBuffer[0] = 0; + // Randomly generated GUID used for storing the last location of this dialog + const Guid folderGuid(0x53890ed9, 0xa55e47ba, 0xa970bdae, 0x72acedff); - // Setup description - BROWSEINFOW bi; - ZeroMemory(&bi, sizeof(bi)); - if (parentWindow) - bi.hwndOwner = static_cast(parentWindow->GetNativePtr()); - bi.lpszTitle = title.HasChars() ? title.Get() : nullptr; - bi.ulFlags = BIF_RETURNONLYFSDIRS | BIF_NEWDIALOGSTYLE; - bi.lpfn = BrowseCallbackProc; - bi.lParam = (LPARAM)(initialDirectory.HasChars() ? initialDirectory.Get() : nullptr); - - LPITEMIDLIST pidl = SHBrowseForFolder(&bi); - - if (pidl != nullptr) + ComPtr fd; + if (SUCCEEDED(CoCreateInstance(CLSID_FileOpenDialog, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&fd)))) { - // Get the name of the folder and put it in path - SHGetPathFromIDList(pidl, pathBuffer.Get()); + DWORD options; + fd->GetOptions(&options); + fd->SetOptions(options | FOS_PICKFOLDERS | FOS_NOCHANGEDIR); - if (pathBuffer[0] != 0) - { - path = pathBuffer.Get(); - result = false; - } + if (title.HasChars()) + fd->SetTitle(title.Get()); - // Free memory used - IMalloc* imalloc = 0; - if (SUCCEEDED(SHGetMalloc(&imalloc))) + // Associate the last selected folder with this GUID instead of overwriting the global one + fd->SetClientGuid(*reinterpret_cast(&folderGuid)); + + ComPtr defaultFolder; + if (SUCCEEDED(SHCreateItemFromParsingName(initialDirectory.Get(), NULL, IID_PPV_ARGS(&defaultFolder)))) + fd->SetFolder(defaultFolder); + + if (SUCCEEDED(fd->Show(parentWindow->GetHWND()))) { - imalloc->Free(pidl); - imalloc->Release(); + ComPtr si; + if (SUCCEEDED(fd->GetResult(&si))) + { + LPWSTR resultPath; + if (SUCCEEDED(si->GetDisplayName(SIGDN_DESKTOPABSOLUTEPARSING, &resultPath))) + { + path = resultPath; + CoTaskMemFree(resultPath); + result = false; + } + } } } diff --git a/Source/Engine/Platform/Windows/WindowsPlatform.cpp b/Source/Engine/Platform/Windows/WindowsPlatform.cpp index 0b1f70989..cb9999a08 100644 --- a/Source/Engine/Platform/Windows/WindowsPlatform.cpp +++ b/Source/Engine/Platform/Windows/WindowsPlatform.cpp @@ -630,8 +630,19 @@ void WindowsPlatform::Exit() void WindowsPlatform::Log(const StringView& msg) { - OutputDebugStringW(msg.Get()); - OutputDebugStringW(TEXT(PLATFORM_LINE_TERMINATOR)); + Char buffer[512]; + Char* str; + if (msg.Length() + 3 < ARRAY_COUNT(buffer)) + str = buffer; + else + str = (Char*)Allocate((msg.Length() + 3) * sizeof(Char), 16); + MemoryCopy(str, msg.Get(), msg.Length() * sizeof(Char)); + str[msg.Length() + 0] = '\r'; + str[msg.Length() + 1] = '\n'; + str[msg.Length() + 2] = 0; + OutputDebugStringW(str); + if (str != buffer) + Free(str); } bool WindowsPlatform::IsDebuggerPresent() diff --git a/Source/Engine/Platform/Windows/WindowsWindow.cpp b/Source/Engine/Platform/Windows/WindowsWindow.cpp index dc130949c..61c8737b8 100644 --- a/Source/Engine/Platform/Windows/WindowsWindow.cpp +++ b/Source/Engine/Platform/Windows/WindowsWindow.cpp @@ -479,6 +479,10 @@ void WindowsWindow::StartTrackingMouse(bool useMouseScreenOffset) _trackingMouseOffset = Vector2::Zero; _isUsingMouseOffset = useMouseScreenOffset; + int32 x = 0 , y = 0, width = 0, height = 0; + GetScreenInfo(x, y, width, height); + _mouseOffsetScreenSize = Rectangle(x, y, width, height); + SetCapture(_handle); } } @@ -712,18 +716,20 @@ LRESULT WindowsWindow::WndProc(UINT msg, WPARAM wParam, LPARAM lParam) if (_isTrackingMouse && _isUsingMouseOffset) { // Check if move mouse to another edge of the desktop - Vector2 destopSize = Platform::GetVirtualDesktopSize(); + Vector2 desktopLocation = _mouseOffsetScreenSize.Location; + Vector2 destopSize = _mouseOffsetScreenSize.GetBottomRight(); + const Vector2 mousePos(static_cast(WINDOWS_GET_X_LPARAM(lParam)), static_cast(WINDOWS_GET_Y_LPARAM(lParam))); Vector2 mousePosition = ClientToScreen(mousePos); Vector2 newMousePosition = mousePosition; - if (mousePosition.X <= 1) + if (mousePosition.X <= desktopLocation.X + 2) newMousePosition.X = destopSize.X - 2; else if (mousePosition.X >= destopSize.X - 1) - newMousePosition.X = 2; - if (mousePosition.Y <= 1) + newMousePosition.X = desktopLocation.X + 2; + if (mousePosition.Y <= desktopLocation.Y + 2) newMousePosition.Y = destopSize.Y - 2; else if (mousePosition.Y >= destopSize.Y - 1) - newMousePosition.Y = 2; + newMousePosition.Y = desktopLocation.Y + 2; if (!Vector2::NearEqual(mousePosition, newMousePosition)) { _trackingMouseOffset -= newMousePosition - mousePosition; diff --git a/Source/Engine/Render2D/Font.cpp b/Source/Engine/Render2D/Font.cpp index c9dd7b55c..209989d4f 100644 --- a/Source/Engine/Render2D/Font.cpp +++ b/Source/Engine/Render2D/Font.cpp @@ -33,7 +33,7 @@ Font::~Font() void Font::GetCharacter(Char c, FontCharacterEntry& result) { - // Try get character or cache it if cannot find + // Try to get the character or cache it if cannot be found if (!_characters.TryGet(c, result)) { // This thread race condition may happen in editor but in game we usually do all stuff with fonts on main thread (chars caching) diff --git a/Source/Engine/Render2D/FontAsset.cpp b/Source/Engine/Render2D/FontAsset.cpp index cf2571e4a..8b14b1cfc 100644 --- a/Source/Engine/Render2D/FontAsset.cpp +++ b/Source/Engine/Render2D/FontAsset.cpp @@ -55,7 +55,7 @@ void FontAsset::unload(bool isReloading) // Ensure to cleanup child font objects if (_fonts.HasItems()) { - LOG(Warning, "Font asset {0} is unloading but has {1} reaming font objects created", ToString(), _fonts.Count()); + LOG(Warning, "Font asset {0} is unloading but has {1} remaining font objects created", ToString(), _fonts.Count()); for (auto font : _fonts) { font->_asset = nullptr; diff --git a/Source/Engine/Render2D/FontManager.cpp b/Source/Engine/Render2D/FontManager.cpp index f05cd7280..ec7a61257 100644 --- a/Source/Engine/Render2D/FontManager.cpp +++ b/Source/Engine/Render2D/FontManager.cpp @@ -214,7 +214,7 @@ bool FontManager::AddNewEntry(Font* font, Char c, FontCharacterEntry& entry) return false; } - // Copy glyph data after rasterize (row by row) + // Copy glyph data after rasterization (row by row) for (int32 row = 0; row < glyphHeight; row++) { Platform::MemoryCopy(&GlyphImageData[row * glyphWidth], &bitmap->buffer[row * bitmap->pitch], glyphWidth); diff --git a/Source/Engine/Render2D/Render2D.cpp b/Source/Engine/Render2D/Render2D.cpp index 2413686aa..c956566ca 100644 --- a/Source/Engine/Render2D/Render2D.cpp +++ b/Source/Engine/Render2D/Render2D.cpp @@ -182,41 +182,44 @@ struct ClipMask Render2D::RenderingFeatures Render2D::Features = RenderingFeatures::VertexSnapping; -// Private Stuff -GPUContext* Context = nullptr; -GPUTextureView* Output = nullptr; -GPUTextureView* DepthBuffer = nullptr; -Viewport View; -Matrix ViewProjection; +namespace +{ + // Private Stuff + GPUContext* Context = nullptr; + GPUTextureView* Output = nullptr; + GPUTextureView* DepthBuffer = nullptr; + Viewport View; + Matrix ViewProjection; -// Drawing -Array DrawCalls; -Array Lines; -Array Lines2; -bool IsScissorsRectEmpty; -bool IsScissorsRectEnabled; + // Drawing + Array DrawCalls; + Array Lines; + Array Lines2; + bool IsScissorsRectEmpty; + bool IsScissorsRectEnabled; -// Transform -// Note: we use Matrix3x3 instead of Matrix because we use only 2D transformations on CPU side -// Matrix layout: -// [ m1, m2, 0 ] -// [ m3, m4, 0 ] -// [ t1, t2, 1 ] -// where 'm' is 2D transformation (scale, shear and rotate), 't' is translation -Array> TransformLayersStack; -Matrix3x3 TransformCached; + // Transform + // Note: we use Matrix3x3 instead of Matrix because we use only 2D transformations on CPU side + // Matrix layout: + // [ m1, m2, 0 ] + // [ m3, m4, 0 ] + // [ t1, t2, 1 ] + // where 'm' is 2D transformation (scale, shear and rotate), 't' is translation + Array> TransformLayersStack; + Matrix3x3 TransformCached; -Array> ClipLayersStack; + Array> ClipLayersStack; -// Shader -AssetReference GUIShader; -CachedPSO PsoDepth; -CachedPSO PsoNoDepth; -CachedPSO* CurrentPso = nullptr; -DynamicVertexBuffer VB(RENDER2D_INITIAL_VB_CAPACITY, (uint32)sizeof(Render2DVertex), TEXT("Render2D.VB")); -DynamicIndexBuffer IB(RENDER2D_INITIAL_IB_CAPACITY, sizeof(uint32), TEXT("Render2D.IB")); -uint32 VBIndex = 0; -uint32 IBIndex = 0; + // Shader + AssetReference GUIShader; + CachedPSO PsoDepth; + CachedPSO PsoNoDepth; + CachedPSO* CurrentPso = nullptr; + DynamicVertexBuffer VB(RENDER2D_INITIAL_VB_CAPACITY, (uint32)sizeof(Render2DVertex), TEXT("Render2D.VB")); + DynamicIndexBuffer IB(RENDER2D_INITIAL_IB_CAPACITY, sizeof(uint32), TEXT("Render2D.IB")); + uint32 VBIndex = 0; + uint32 IBIndex = 0; +} #define RENDER2D_WRITE_IB_QUAD(indices) \ indices[0] = VBIndex + 0; \ @@ -957,8 +960,8 @@ void DrawBatch(int32 startIndex, int32 count) data.Bounds.Y = bounds.Y; data.Bounds.Z = bounds.Z - bounds.X; data.Bounds.W = bounds.W - bounds.Y; - data.InvBufferSize.X = 1.0f / renderTargetWidth; - data.InvBufferSize.Y = 1.0f / renderTargetHeight; + data.InvBufferSize.X = 1.0f / (float)renderTargetWidth; + data.InvBufferSize.Y = 1.0f / (float)renderTargetHeight; data.SampleCount = ComputeBlurWeights(kernelSize, blurStrength, data.WeightAndOffsets); const auto cb = GUIShader->GetShader()->GetCB(1); Context->UpdateCB(cb, &data); @@ -967,7 +970,7 @@ void DrawBatch(int32 startIndex, int32 count) // Downscale (or not) and extract the background image for the blurring Context->ResetRenderTarget(); Context->SetRenderTarget(blurA->View()); - Context->SetViewport((float)renderTargetWidth, (float)renderTargetHeight); + Context->SetViewportAndScissors((float)renderTargetWidth, (float)renderTargetHeight); Context->BindSR(0, Output); Context->SetState(CurrentPso->PS_Downscale); Context->DrawFullscreenTriangle(); @@ -1003,11 +1006,8 @@ void DrawBatch(int32 startIndex, int32 count) break; } case DrawCallType::ClipScissors: - { - Rectangle* scissorsRect = (Rectangle*)&d.AsClipScissors.X; - Context->SetScissor(*scissorsRect); + Context->SetScissor(*(Rectangle*)&d.AsClipScissors.X); return; - } case DrawCallType::LineAA: Context->SetState(CurrentPso->PS_LineAA); break; diff --git a/Source/Engine/Renderer/AmbientOcclusionPass.h b/Source/Engine/Renderer/AmbientOcclusionPass.h index 81d64a0e4..bfaf4bc19 100644 --- a/Source/Engine/Renderer/AmbientOcclusionPass.h +++ b/Source/Engine/Renderer/AmbientOcclusionPass.h @@ -60,7 +60,7 @@ private: float ShadowMultiplier; // [0.0, 5.0] Effect strength linear multiplier float ShadowPower; // [0.5, 5.0] Effect strength pow modifier float HorizonAngleThreshold; // [0.0, 0.2] Limits self-shadowing (makes the sampling area less of a hemisphere, more of a spherical cone, to avoid self-shadowing and various artifacts due to low tessellation and depth buffer imprecision, etc.) - float FadeOutFrom; // [0.0, ~ ] Distance to start start fading out the effect. + float FadeOutFrom; // [0.0, ~ ] Distance to start fading out the effect. float FadeOutTo; // [0.0, ~ ] Distance at which the effect is faded out. int QualityLevel; // [ 0, ] Effect quality; 0 - low, 1 - medium, 2 - high, 3 - very high; each quality level is roughly 2x more costly than the previous, except the q3 which is variable but, in general, above q2. int BlurPassCount; // [ 0, 6] Number of edge-sensitive smart blur passes to apply. Quality 0 is an exception with only one 'dumb' blur pass used. diff --git a/Source/Engine/Renderer/EyeAdaptationPass.cpp b/Source/Engine/Renderer/EyeAdaptationPass.cpp index 754c1489f..a85e0a72b 100644 --- a/Source/Engine/Renderer/EyeAdaptationPass.cpp +++ b/Source/Engine/Renderer/EyeAdaptationPass.cpp @@ -2,6 +2,7 @@ #include "EyeAdaptationPass.h" #include "RenderList.h" +#include "Engine/Core/Math/Int2.h" #include "Engine/Content/Assets/Shader.h" #include "Engine/Content/Content.h" #include "Engine/Graphics/PostProcessBase.h" diff --git a/Source/Engine/Renderer/MotionBlurPass.cpp b/Source/Engine/Renderer/MotionBlurPass.cpp index f25d14c51..f10ef442b 100644 --- a/Source/Engine/Renderer/MotionBlurPass.cpp +++ b/Source/Engine/Renderer/MotionBlurPass.cpp @@ -57,11 +57,11 @@ bool MotionBlurPass::Init() // Prepare formats for the buffers auto format = MOTION_VECTORS_PIXEL_FORMAT; - if (FORMAT_FEATURES_ARE_NOT_SUPPORTED(GPUDevice::Instance->GetFormatFeatures(format).Support, (FormatSupport::RenderTarget | FormatSupport::ShaderSample | FormatSupport::Texture2D))) + if (FORMAT_FEATURES_ARE_NOT_SUPPORTED(GPUDevice::Instance->GetFormatFeatures(format).Support, FormatSupport::RenderTarget | FormatSupport::ShaderSample | FormatSupport::Texture2D)) { - if (FORMAT_FEATURES_ARE_NOT_SUPPORTED(GPUDevice::Instance->GetFormatFeatures(PixelFormat::R32G32_Float).Support, (FormatSupport::RenderTarget | FormatSupport::ShaderSample | FormatSupport::Texture2D))) + if (FORMAT_FEATURES_ARE_NOT_SUPPORTED(GPUDevice::Instance->GetFormatFeatures(PixelFormat::R32G32_Float).Support, FormatSupport::RenderTarget | FormatSupport::ShaderSample | FormatSupport::Texture2D)) format = PixelFormat::R32G32_Float; - else if (FORMAT_FEATURES_ARE_NOT_SUPPORTED(GPUDevice::Instance->GetFormatFeatures(PixelFormat::R16G16B16A16_Float).Support, (FormatSupport::RenderTarget | FormatSupport::ShaderSample | FormatSupport::Texture2D))) + else if (FORMAT_FEATURES_ARE_NOT_SUPPORTED(GPUDevice::Instance->GetFormatFeatures(PixelFormat::R16G16B16A16_Float).Support, FormatSupport::RenderTarget | FormatSupport::ShaderSample | FormatSupport::Texture2D)) format = PixelFormat::R16G16B16A16_Float; else format = PixelFormat::R32G32B32A32_Float; @@ -285,7 +285,7 @@ void MotionBlurPass::Render(RenderContext& renderContext, GPUTexture*& input, GP PROFILE_GPU_CPU("Motion Blur"); // Setup shader inputs - const int32 maxBlurSize = (int32)((float)motionVectorsHeight * 0.05f); + const int32 maxBlurSize = Math::Max((int32)((float)motionVectorsHeight * 0.05f), 1); const int32 tileSize = Math::AlignUp(maxBlurSize, 8); const float timeScale = renderContext.Task->View.IsOfflinePass ? 1.0f : 1.0f / Time::Draw.UnscaledDeltaTime.GetTotalSeconds() / 60.0f; // 60fps as a reference Data data; @@ -337,8 +337,8 @@ void MotionBlurPass::Render(RenderContext& renderContext, GPUTexture*& input, GP RenderTargetPool::Release(vMaxBuffer4); // Downscale motion vectors texture down to tileSize/tileSize (with max velocity calculation NxN kernel) - rtDesc.Width = motionVectorsWidth / tileSize; - rtDesc.Height = motionVectorsHeight / tileSize; + rtDesc.Width = Math::Max(motionVectorsWidth / tileSize, 1); + rtDesc.Height = Math::Max(motionVectorsHeight / tileSize, 1); auto vMaxBuffer = RenderTargetPool::Get(rtDesc); context->ResetRenderTarget(); context->SetRenderTarget(vMaxBuffer->View()); diff --git a/Source/Engine/Renderer/Renderer.cpp b/Source/Engine/Renderer/Renderer.cpp index c2a18f519..4d3241375 100644 --- a/Source/Engine/Renderer/Renderer.cpp +++ b/Source/Engine/Renderer/Renderer.cpp @@ -199,9 +199,8 @@ void Renderer::Render(SceneRenderTask* task) #endif // Perform the actual rendering + task->OnPreRender(context, renderContext); RenderInner(task, renderContext); - - // Custom additional rendering task->OnPostRender(context, renderContext); #if USE_EDITOR @@ -306,7 +305,6 @@ void RenderInner(SceneRenderTask* task, RenderContext& renderContext) #endif renderContext.List->Settings.AntiAliasing.Mode = aaMode; - // Prepare renderContext.View.Prepare(renderContext); renderContext.Buffers->Prepare(); diff --git a/Source/Engine/Renderer/ShadowsPass.cpp b/Source/Engine/Renderer/ShadowsPass.cpp index 5ec9d6446..11812a45c 100644 --- a/Source/Engine/Renderer/ShadowsPass.cpp +++ b/Source/Engine/Renderer/ShadowsPass.cpp @@ -6,6 +6,7 @@ #include "Engine/Graphics/Graphics.h" #include "Engine/Graphics/RenderBuffers.h" #include "Engine/Content/Content.h" +#include "Engine/Graphics/PixelFormatExtensions.h" #if USE_EDITOR #include "Engine/Renderer/Lightmaps.h" #endif @@ -77,6 +78,19 @@ bool ShadowsPass::Init() _shader.Get()->OnReloading.Bind(this); #endif + // If GPU doesn't support linear sampling for the shadow map then fallback to the single sample on lowest quality + const auto formatTexture = PixelFormatExtensions::FindShaderResourceFormat(SHADOW_MAPS_FORMAT, false); + const auto formatFeaturesDepth = GPUDevice::Instance->GetFormatFeatures(SHADOW_MAPS_FORMAT); + const auto formatFeaturesTexture = GPUDevice::Instance->GetFormatFeatures(formatTexture); + _supportsShadows = FORMAT_FEATURES_ARE_SUPPORTED(formatFeaturesDepth.Support, FormatSupport::DepthStencil | FormatSupport::Texture2D) + && FORMAT_FEATURES_ARE_SUPPORTED(formatFeaturesTexture.Support, FormatSupport::ShaderSample | FormatSupport::ShaderSampleComparison); + if (!_supportsShadows) + { + LOG(Warning, "GPU doesn't support shadows rendering"); + LOG(Warning, "Format: {0} features support: {1}", (int32)SHADOW_MAPS_FORMAT, (uint32)formatFeaturesDepth.Support); + LOG(Warning, "Format: {0} features support: {1}", (int32)formatTexture, (uint32)formatFeaturesTexture.Support); + } + return false; } @@ -130,24 +144,27 @@ void ShadowsPass::updateShadowMapSize() // Select new size _currentShadowMapsQuality = Graphics::ShadowMapsQuality; - switch (_currentShadowMapsQuality) + if (_supportsShadows) { - case Quality::Ultra: - newSizeCSM = 2048; - newSizeCube = 1024; - break; - case Quality::High: - newSizeCSM = 1024; - newSizeCube = 1024; - break; - case Quality::Medium: - newSizeCSM = 1024; - newSizeCube = 512; - break; - case Quality::Low: - newSizeCSM = 512; - newSizeCube = 256; - break; + switch (_currentShadowMapsQuality) + { + case Quality::Ultra: + newSizeCSM = 2048; + newSizeCube = 1024; + break; + case Quality::High: + newSizeCSM = 1024; + newSizeCube = 1024; + break; + case Quality::Medium: + newSizeCSM = 1024; + newSizeCube = 512; + break; + case Quality::Low: + newSizeCSM = 512; + newSizeCube = 256; + break; + } } // Check if size will change @@ -194,7 +211,7 @@ bool ShadowsPass::CanRenderShadow(RenderContext& renderContext, const RendererPo const float fadeDistance = Math::Max(light.ShadowsFadeDistance, 0.1f); const float fade = 1 - Math::Saturate((dstLightToView - light.Radius - light.ShadowsDistance + fadeDistance) / fadeDistance); - return fade > ZeroTolerance; + return fade > ZeroTolerance && _supportsShadows; } bool ShadowsPass::CanRenderShadow(RenderContext& renderContext, const RendererSpotLightData& light) @@ -206,12 +223,12 @@ bool ShadowsPass::CanRenderShadow(RenderContext& renderContext, const RendererSp const float fadeDistance = Math::Max(light.ShadowsFadeDistance, 0.1f); const float fade = 1 - Math::Saturate((dstLightToView - light.Radius - light.ShadowsDistance + fadeDistance) / fadeDistance); - return fade > ZeroTolerance; + return fade > ZeroTolerance && _supportsShadows; } bool ShadowsPass::CanRenderShadow(RenderContext& renderContext, const RendererDirectionalLightData& light) { - return true; + return _supportsShadows; } void ShadowsPass::Prepare(RenderContext& renderContext, GPUContext* context) diff --git a/Source/Engine/Renderer/ShadowsPass.h b/Source/Engine/Renderer/ShadowsPass.h index 8deef8258..3d3190670 100644 --- a/Source/Engine/Renderer/ShadowsPass.h +++ b/Source/Engine/Renderer/ShadowsPass.h @@ -25,6 +25,7 @@ private: GPUPipelineStatePermutationsPs(Quality::MAX) * 2 * 2> _psShadowDir; GPUPipelineStatePermutationsPs(Quality::MAX) * 2> _psShadowPoint; GPUPipelineStatePermutationsPs(Quality::MAX) * 2> _psShadowSpot; + bool _supportsShadows; // Shadow maps stuff int32 _shadowMapsSizeCSM; diff --git a/Source/Engine/Scripting/Attributes/Editor/EditorOrderAttribute.cs b/Source/Engine/Scripting/Attributes/Editor/EditorOrderAttribute.cs index 506666def..5b783b514 100644 --- a/Source/Engine/Scripting/Attributes/Editor/EditorOrderAttribute.cs +++ b/Source/Engine/Scripting/Attributes/Editor/EditorOrderAttribute.cs @@ -27,7 +27,7 @@ namespace FlaxEngine /// /// Current order is resolved runtime, and can change if custom editor class has changed. /// - /// The order order. + /// The order. public EditorOrderAttribute(int order) { Order = order; diff --git a/Source/Engine/Scripting/Attributes/SerializeAttribute.cs b/Source/Engine/Scripting/Attributes/SerializeAttribute.cs index 3cda97473..d120756ea 100644 --- a/Source/Engine/Scripting/Attributes/SerializeAttribute.cs +++ b/Source/Engine/Scripting/Attributes/SerializeAttribute.cs @@ -5,7 +5,7 @@ using System; namespace FlaxEngine { /// - /// Indicates that a field or a property of a serializable class should be be serialized. This class cannot be inherited. + /// Indicates that a field or a property of a serializable class should be serialized. This class cannot be inherited. /// [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)] public sealed class SerializeAttribute : Attribute diff --git a/Source/Engine/Scripting/ManagedCLR/MAssembly.Mono.cpp b/Source/Engine/Scripting/ManagedCLR/MAssembly.Mono.cpp index 50a8b689e..e24b71eff 100644 --- a/Source/Engine/Scripting/ManagedCLR/MAssembly.Mono.cpp +++ b/Source/Engine/Scripting/ManagedCLR/MAssembly.Mono.cpp @@ -303,7 +303,7 @@ bool MAssembly::LoadWithImage(const String& assemblyPath) const auto assembly = mono_assembly_load_from_full(assemblyImage, name.Substring(0, name.Length() - 3).Get(), &status, false); if (status != MONO_IMAGE_OK || assembly == nullptr) { - // Close image if error occured + // Close image if error occurred mono_image_close(assemblyImage); Log::CLRInnerException(TEXT("Mono assembly image is corrupted at ") + assemblyPath); diff --git a/Source/Engine/Scripting/ManagedCLR/MAssembly.h b/Source/Engine/Scripting/ManagedCLR/MAssembly.h index ff8d5e8db..17cb8b624 100644 --- a/Source/Engine/Scripting/ManagedCLR/MAssembly.h +++ b/Source/Engine/Scripting/ManagedCLR/MAssembly.h @@ -234,7 +234,7 @@ public: #endif /// - /// Gets the classes lookup cache. Performs ful initialization if not cached. The result cache contains all classes from the assembly. + /// Gets the classes lookup cache. Performs full initialization if not cached. The result cache contains all classes from the assembly. /// /// The cache. const ClassesDictionary& GetClasses() const; diff --git a/Source/Engine/Scripting/ManagedCLR/MClass.h b/Source/Engine/Scripting/ManagedCLR/MClass.h index bfa813fb4..a8d083d7b 100644 --- a/Source/Engine/Scripting/ManagedCLR/MClass.h +++ b/Source/Engine/Scripting/ManagedCLR/MClass.h @@ -193,7 +193,7 @@ public: /// /// Returns an object referencing a method with the specified name and number of parameters. /// - /// If the the type contains more than one method of the given name and parameters count the returned value can be non-deterministic (one of the matching methods). + /// If the type contains more than one method of the given name and parameters count the returned value can be non-deterministic (one of the matching methods). /// The method name. /// The method parameters count. /// The method or null if failed to get it. diff --git a/Source/Engine/Scripting/Scripting.cpp b/Source/Engine/Scripting/Scripting.cpp index 6f32f8fd1..c8c8184bd 100644 --- a/Source/Engine/Scripting/Scripting.cpp +++ b/Source/Engine/Scripting/Scripting.cpp @@ -56,7 +56,45 @@ namespace MDomain* _monoRootDomain = nullptr; MDomain* _monoScriptsDomain = nullptr; CriticalSection _objectsLocker; +#define USE_OBJECTS_DISPOSE_CRASHES_DEBUGGING 0 +#if USE_OBJECTS_DISPOSE_CRASHES_DEBUGGING + struct ScriptingObjectData + { + ScriptingObject* Ptr; + StringAnsi TypeName; + + ScriptingObjectData() + { + Ptr = nullptr; + } + + ScriptingObjectData(ScriptingObject* ptr) + { + Ptr = ptr; + if (ptr && ptr->GetTypeHandle() && ptr->GetTypeHandle().TypeIndex < ptr->GetTypeHandle().Module->Types.Count()) + TypeName = ptr->GetType().Fullname; + } + + ScriptingObject* operator->() const + { + return Ptr; + } + + explicit operator ScriptingObject*() + { + return Ptr; + } + + operator ScriptingObject*() const + { + return Ptr; + } + }; + + Dictionary _objectsDictionary(1024 * 16); +#else Dictionary _objectsDictionary(1024 * 16); +#endif bool _isEngineAssemblyLoaded = false; bool _hasGameModulesLoaded = false; MMethod* _method_Update = nullptr; @@ -456,6 +494,9 @@ void Scripting::Release() for (auto i = _objectsDictionary.Begin(); i.IsNotEnd(); ++i) { auto obj = i->Value; +#if USE_OBJECTS_DISPOSE_CRASHES_DEBUGGING + LOG(Info, "[OnScriptingDispose] obj = 0x{0:x}, {1}", (uint64)obj.Ptr, String(obj.TypeName)); +#endif obj->OnScriptingDispose(); } } @@ -498,7 +539,9 @@ void Scripting::Release() } } +#if !USE_SINGLE_DOMAIN MCore::Instance()->UnloadDomain("Scripts Domain"); +#endif } #if USE_EDITOR @@ -546,6 +589,24 @@ void Scripting::Reload(bool canTriggerSceneReload) MCore::GC::Collect(); MCore::GC::WaitForPendingFinalizers(); + // Destroy objects from game assemblies (eg. not released objects that might crash if persist in memory after reload) + _objectsLocker.Lock(); + { + const auto flaxModule = GetBinaryModuleFlaxEngine(); + for (auto i = _objectsDictionary.Begin(); i.IsNotEnd(); ++i) + { + auto obj = i->Value; + if (obj->GetTypeHandle().Module == flaxModule) + continue; + +#if USE_OBJECTS_DISPOSE_CRASHES_DEBUGGING + LOG(Info, "[OnScriptingDispose] obj = 0x{0:x}, {1}", (uint64)obj.Ptr, String(obj.TypeName)); +#endif + obj->OnScriptingDispose(); + } + } + _objectsLocker.Unlock(); + // Unload all game modules LOG(Info, "Unloading game binary modules"); auto modules = BinaryModule::GetModules(); @@ -683,10 +744,18 @@ ScriptingObject* Scripting::FindObject(Guid id, MClass* type) } // Try to find it +#if USE_OBJECTS_DISPOSE_CRASHES_DEBUGGING + ScriptingObjectData data; + _objectsLocker.Lock(); + _objectsDictionary.TryGet(id, data); + _objectsLocker.Unlock(); + auto result = data.Ptr; +#else ScriptingObject* result = nullptr; _objectsLocker.Lock(); _objectsDictionary.TryGet(id, result); _objectsLocker.Unlock(); +#endif if (result) { // Check type @@ -718,11 +787,20 @@ ScriptingObject* Scripting::TryFindObject(Guid id, MClass* type) } // Try to find it +#if USE_OBJECTS_DISPOSE_CRASHES_DEBUGGING + ScriptingObjectData data; + _objectsLocker.Lock(); + _objectsDictionary.TryGet(id, data); + _objectsLocker.Unlock(); + auto result = data.Ptr; +#else ScriptingObject* result = nullptr; _objectsLocker.Lock(); _objectsDictionary.TryGet(id, result); _objectsLocker.Unlock(); +#endif + // Check type if (result && !result->Is(type)) { result = nullptr; @@ -753,28 +831,23 @@ ScriptingObject* Scripting::FindObject(const MonoObject* managedInstance) void Scripting::OnManagedInstanceDeleted(ScriptingObject* obj) { + PROFILE_CPU_NAMED("OnManagedInstanceDeleted"); ASSERT(obj); - PROFILE_CPU_NAMED("OnManagedInstanceDeleted"); - - // This is sometimes crashing, probably rawPtr field is not cleared in some cases - // TODO: use faster callback without crashing - //obj->OnManagedInstanceDeleted(); - // Validate if object still exists - bool isValid; - { - ScopeLock lock(_objectsLocker); - isValid = _objectsDictionary.ContainsValue(obj); - } - if (isValid) + _objectsLocker.Lock(); + if (_objectsDictionary.ContainsValue(obj)) { +#if USE_OBJECTS_DISPOSE_CRASHES_DEBUGGING + LOG(Info, "[OnManagedInstanceDeleted] obj = 0x{0:x}, {1}", (uint64)obj, String(ScriptingObjectData(obj).TypeName)); +#endif obj->OnManagedInstanceDeleted(); } else { //LOG(Warning, "Object finalization called for already removed object (address={0:x})", (uint64)obj); } + _objectsLocker.Unlock(); } bool Scripting::HasGameModulesLoaded() @@ -805,18 +878,25 @@ void Scripting::RegisterObject(ScriptingObject* obj) //ASSERT(!_objectsDictionary.ContainsValue(obj)); #if ENABLE_ASSERTION - ScriptingObject* other = nullptr; +#if USE_OBJECTS_DISPOSE_CRASHES_DEBUGGING + ScriptingObjectData other; if (_objectsDictionary.TryGet(obj->GetID(), other)) +#else + ScriptingObject* other; + if (_objectsDictionary.TryGet(obj->GetID(), other)) +#endif { // Something went wrong... - LOG(Error, "Objects registry already contains object with ID={0}! Trying to register object {1} (type '{2}').", obj->GetID(), obj->ToString(), String(obj->GetClass()->GetFullName())); - + LOG(Error, "Objects registry already contains object with ID={0} (type '{3}')! Trying to register object {1} (type '{2}').", obj->GetID(), obj->ToString(), String(obj->GetClass()->GetFullName()), String(other->GetClass()->GetFullName())); _objectsDictionary.Remove(obj->GetID()); } #else ASSERT(!_objectsDictionary.ContainsKey(obj->_id)); #endif +#if USE_OBJECTS_DISPOSE_CRASHES_DEBUGGING + LOG(Info, "[RegisterObject] obj = 0x{0:x}, {1}", (uint64)obj, String(ScriptingObjectData(obj).TypeName)); +#endif _objectsDictionary.Add(obj->GetID(), obj); } @@ -826,6 +906,9 @@ void Scripting::UnregisterObject(ScriptingObject* obj) //ASSERT(!obj->_id.IsValid() || _objectsDictionary.ContainsValue(obj)); +#if USE_OBJECTS_DISPOSE_CRASHES_DEBUGGING + LOG(Info, "[UnregisterObject] obj = 0x{0:x}, {1}", (uint64)obj, String(ScriptingObjectData(obj).TypeName)); +#endif _objectsDictionary.Remove(obj->GetID()); } diff --git a/Source/Engine/Scripting/ScriptingObject.cpp b/Source/Engine/Scripting/ScriptingObject.cpp index 11d6e43e0..5cdea3ab4 100644 --- a/Source/Engine/Scripting/ScriptingObject.cpp +++ b/Source/Engine/Scripting/ScriptingObject.cpp @@ -11,7 +11,6 @@ #include "ManagedCLR/MClass.h" #include "ManagedCLR/MUtils.h" #include "ManagedCLR/MField.h" -#include "ManagedCLR/MUtils.h" #if PLATFORM_LINUX #include "ManagedCLR/MCore.h" #endif @@ -501,11 +500,6 @@ public: obj->RegisterObject(); } - static void ManagedInstanceDeleted(ScriptingObject* obj) - { - Scripting::OnManagedInstanceDeleted(obj); - } - static void Destroy(ManagedScriptingObject* obj, float timeLeft) { // Use scaled game time for removing actors/scripts by the user (maybe expose it to the api?) @@ -544,7 +538,7 @@ public: ADD_INTERNAL_CALL("FlaxEngine.Object::Internal_Create1", &Create1); ADD_INTERNAL_CALL("FlaxEngine.Object::Internal_Create2", &Create2); ADD_INTERNAL_CALL("FlaxEngine.Object::Internal_ManagedInstanceCreated", &ManagedInstanceCreated); - ADD_INTERNAL_CALL("FlaxEngine.Object::Internal_ManagedInstanceDeleted", &ManagedInstanceDeleted); + ADD_INTERNAL_CALL("FlaxEngine.Object::Internal_ManagedInstanceDeleted", &Scripting::OnManagedInstanceDeleted); ADD_INTERNAL_CALL("FlaxEngine.Object::Internal_Destroy", &Destroy); ADD_INTERNAL_CALL("FlaxEngine.Object::Internal_GetTypeName", &GetTypeName); ADD_INTERNAL_CALL("FlaxEngine.Object::Internal_FindObject", &FindObject); diff --git a/Source/Engine/Scripting/ScriptingType.h b/Source/Engine/Scripting/ScriptingType.h index 40ca2ed10..942edcd1a 100644 --- a/Source/Engine/Scripting/ScriptingType.h +++ b/Source/Engine/Scripting/ScriptingType.h @@ -190,7 +190,7 @@ struct FLAXENGINE_API ScriptingType SetupScriptObjectVTableHandler SetupScriptObjectVTable; /// - /// The default instance of the scripting type. Used by serialization system for comparision to save only modified properties of the object. + /// The default instance of the scripting type. Used by serialization system for comparison to save only modified properties of the object. /// mutable ScriptingObject* DefaultInstance; } Class; @@ -255,7 +255,7 @@ struct FLAXENGINE_API ScriptingType } /// - /// Gets the default instance of the scripting type. Used by serialization system for comparision to save only modified properties of the object. + /// Gets the default instance of the scripting type. Used by serialization system for comparison to save only modified properties of the object. /// ScriptingObject* GetDefaultInstance() const; diff --git a/Source/Engine/Serialization/JsonWriter.cpp b/Source/Engine/Serialization/JsonWriter.cpp index d3f04a684..de90b49b4 100644 --- a/Source/Engine/Serialization/JsonWriter.cpp +++ b/Source/Engine/Serialization/JsonWriter.cpp @@ -195,7 +195,7 @@ void JsonWriter::SceneObject(::SceneObject* obj) auto prefab = Content::Load(obj->GetPrefabID()); if (prefab) { - // Request the prefab to be deserialized to the default instance (used for comparision to generate a diff) + // Request the prefab to be deserialized to the default instance (used for comparison to generate a diff) prefab->GetDefaultInstance(); // Get prefab object instance from the prefab diff --git a/Source/Engine/Serialization/Stream.cpp b/Source/Engine/Serialization/Stream.cpp index e10ae9fd6..269d49562 100644 --- a/Source/Engine/Serialization/Stream.cpp +++ b/Source/Engine/Serialization/Stream.cpp @@ -4,8 +4,9 @@ #include "WriteStream.h" #include "Engine/Core/Types/CommonValue.h" #include "Engine/Core/Types/Variant.h" -#include "Engine/Content/Asset.h" #include "Engine/Core/Collections/Dictionary.h" +#include "Engine/Content/Asset.h" +#include "Engine/Debug/DebugLog.h" #include "Engine/Scripting/ScriptingObject.h" #include "Engine/Scripting/ScriptingObjectReference.h" @@ -227,6 +228,8 @@ void ReadStream::ReadVariantType(VariantType* data) if (typeNameLength == MAX_int32) { ReadInt32(&typeNameLength); + if (typeNameLength == 0) + return; data->TypeName = static_cast(Allocator::Allocate(typeNameLength + 1)); char* ptr = data->TypeName; Read(ptr, typeNameLength); @@ -330,8 +333,19 @@ void ReadStream::ReadVariant(Variant* data) { int32 length; ReadInt32(&length); - ASSERT(data->AsBlob.Length == length); - ReadBytes(data->AsBlob.Data, length); + if (data->AsBlob.Length == length) + { + ReadBytes(data->AsBlob.Data, length); + } + else + { + LOG(Error, "Invalid Variant {2} data length {0}. Expected {1} bytes from stream.", data->AsBlob.Length, length, data->Type.ToString()); + + // Skip those bytes + void* ptr = Allocator::Allocate(length); + ReadBytes(ptr, length); + Allocator::Free(ptr); + } break; } case VariantType::Blob: @@ -590,13 +604,21 @@ void WriteStream::WriteVariant(const Variant& data) break; case VariantType::Structure: case VariantType::Blob: - case VariantType::BoundingBox: - case VariantType::Transform: - case VariantType::Ray: - case VariantType::Matrix: WriteInt32(data.AsBlob.Length); WriteBytes(data.AsBlob.Data, data.AsBlob.Length); break; + case VariantType::BoundingBox: + WriteBytes(data.AsBlob.Data, sizeof(BoundingBox)); + break; + case VariantType::Transform: + WriteBytes(data.AsBlob.Data, sizeof(Transform)); + break; + case VariantType::Ray: + WriteBytes(data.AsBlob.Data, sizeof(Ray)); + break; + case VariantType::Matrix: + WriteBytes(data.AsBlob.Data, sizeof(Matrix)); + break; case VariantType::Asset: id = data.AsAsset ? data.AsAsset->GetID() : Guid::Empty; Write(&id); diff --git a/Source/Engine/Serialization/Stream.h b/Source/Engine/Serialization/Stream.h index de7911fb4..d988a24df 100644 --- a/Source/Engine/Serialization/Stream.h +++ b/Source/Engine/Serialization/Stream.h @@ -37,9 +37,9 @@ public: public: /// - /// Returns true if error occured during reading/writing to the stream + /// Returns true if error occurred during reading/writing to the stream /// - /// True if error occured during reading/writing to the stream + /// True if error occurred during reading/writing to the stream virtual bool HasError() const { return _hasError; diff --git a/Source/Engine/Streaming/StreamingManager.cpp b/Source/Engine/Streaming/StreamingManager.cpp index d82a3b5ab..3b557e85f 100644 --- a/Source/Engine/Streaming/StreamingManager.cpp +++ b/Source/Engine/Streaming/StreamingManager.cpp @@ -92,7 +92,7 @@ void UpdateResource(StreamableResource* resource, DateTime now) } } - // Calculate residency level to stream in (resources may want to incease/decrease it's quality in steps rather than at once) + // Calculate residency level to stream in (resources may want to increase/decrease it's quality in steps rather than at once) int32 requestedResidency = handler->CalculateRequestedResidency(resource, targetResidency); // Create streaming task (resource type specific) diff --git a/Source/Engine/Terrain/Terrain.cpp b/Source/Engine/Terrain/Terrain.cpp index d15dd4cfa..3b00818c2 100644 --- a/Source/Engine/Terrain/Terrain.cpp +++ b/Source/Engine/Terrain/Terrain.cpp @@ -36,6 +36,7 @@ Terrain::~Terrain() void Terrain::UpdateBounds() { + PROFILE_CPU(); _box = BoundingBox(_transform.Translation, _transform.Translation); for (int32 i = 0; i < _patches.Count(); i++) { @@ -48,6 +49,7 @@ void Terrain::UpdateBounds() void Terrain::CacheNeighbors() { + PROFILE_CPU(); for (int32 pathIndex = 0; pathIndex < _patches.Count(); pathIndex++) { const auto patch = _patches[pathIndex]; @@ -489,7 +491,7 @@ void Terrain::RemovePatch(const Int2& patchCoord) const auto patch = GetPatch(patchCoord); if (patch == nullptr) { - LOG(Warning, "Cannot remvoe patch at {0}x{1}. It does not exist.", patchCoord.X, patchCoord.Y); + LOG(Warning, "Cannot remove patch at {0}x{1}. It does not exist.", patchCoord.X, patchCoord.Y); return; } diff --git a/Source/Engine/Terrain/TerrainChunk.cpp b/Source/Engine/Terrain/TerrainChunk.cpp index 1241935e5..fbb80c27d 100644 --- a/Source/Engine/Terrain/TerrainChunk.cpp +++ b/Source/Engine/Terrain/TerrainChunk.cpp @@ -192,7 +192,7 @@ bool TerrainChunk::Intersects(const Ray& ray, float& distance) void TerrainChunk::UpdateBounds() { const Vector3 boundsExtent = _patch->_terrain->_boundsExtent; - const float size = _patch->_terrain->_chunkSize * TERRAIN_UNITS_PER_VERTEX; + const float size = (float)_patch->_terrain->_chunkSize * TERRAIN_UNITS_PER_VERTEX; Transform terrainTransform = _patch->_terrain->_transform; Transform localTransform; diff --git a/Source/Engine/Terrain/TerrainPatch.cpp b/Source/Engine/Terrain/TerrainPatch.cpp index d0050d455..552238e9e 100644 --- a/Source/Engine/Terrain/TerrainPatch.cpp +++ b/Source/Engine/Terrain/TerrainPatch.cpp @@ -5,7 +5,6 @@ #include "Engine/Serialization/Serialization.h" #include "Engine/Graphics/RenderView.h" #include "Engine/Core/Math/Color32.h" -#include "Engine/Core/Math/VectorInt.h" #include "Engine/Profiler/ProfilerCPU.h" #include "Engine/Physics/Utilities.h" #include "Engine/Physics/Physics.h" @@ -952,7 +951,7 @@ bool TerrainPatch::SetupHeightMap(int32 heightMapLength, const float* heightMap, chunk._yHeight = chunkHeights[chunkIndex]; chunk.UpdateTransform(); } - UpdateBounds(); + _terrain->UpdateBounds(); UpdateCollision(); #if TERRAIN_UPDATING @@ -1432,7 +1431,7 @@ bool TerrainPatch::ModifyHeightMap(const float* samples, const Int2& modifiedOff chunk._yHeight = chunkHeights[chunkIndex]; chunk.UpdateTransform(); } - UpdateBounds(); + _terrain->UpdateBounds(); return UpdateHeightData(info, modifiedOffset, modifiedSize, wasHeightRangeChanged); } @@ -2109,9 +2108,8 @@ void TerrainPatch::UpdatePostManualDeserialization() { auto& chunk = Chunks[chunkIndex]; chunk.UpdateTransform(); - chunk.UpdateBounds(); } - UpdateBounds(); + _terrain->UpdateBounds(); ScopeLock lock(_collisionLocker); diff --git a/Source/Engine/Terrain/TerrainPatch.h b/Source/Engine/Terrain/TerrainPatch.h index fcd97f4ec..b09848d61 100644 --- a/Source/Engine/Terrain/TerrainPatch.h +++ b/Source/Engine/Terrain/TerrainPatch.h @@ -5,7 +5,7 @@ #include "Terrain.h" #include "TerrainChunk.h" #include "Engine/Core/Math/Color32.h" -#include "Engine/Core/Math/VectorInt.h" +#include "Engine/Core/Math/Int2.h" #include "Engine/Physics/Types.h" #include "Engine/Level/Scene/Lightmap.h" #include "Engine/Content/Assets/RawDataAsset.h" diff --git a/Source/Engine/Threading/ConcurrentBuffer.h b/Source/Engine/Threading/ConcurrentBuffer.h index 99159e206..99ebe5e86 100644 --- a/Source/Engine/Threading/ConcurrentBuffer.h +++ b/Source/Engine/Threading/ConcurrentBuffer.h @@ -382,7 +382,7 @@ public: /// Searches for the specified object and returns the zero-based index of the first occurrence within the entire collection. /// /// The item. - /// The zero-based index of the first occurrence of itm within the entire collection, if found; otherwise, INVALID_INDEX. + /// The zero-based index of the first occurrence of item within the entire collection, if found; otherwise, INVALID_INDEX. int32 IndexOf(const T& item) const { for (int32 i = 0; i < _count; i++) diff --git a/Source/Engine/Threading/IRunnable.h b/Source/Engine/Threading/IRunnable.h index 7dc42f73a..83a2af305 100644 --- a/Source/Engine/Threading/IRunnable.h +++ b/Source/Engine/Threading/IRunnable.h @@ -41,7 +41,7 @@ public: } // Called when thread ends work (via Kill or normally) - // @param wasKilled True if thead has been killed + // @param wasKilled True if thread has been killed virtual void AfterWork(bool wasKilled) { } diff --git a/Source/Engine/Threading/ThreadPool.cpp b/Source/Engine/Threading/ThreadPool.cpp index 56d6f6289..ffd80d655 100644 --- a/Source/Engine/Threading/ThreadPool.cpp +++ b/Source/Engine/Threading/ThreadPool.cpp @@ -52,7 +52,7 @@ bool ThreadPoolService::Init() // Create tread auto runnable = New(true); runnable->OnWork.Bind(ThreadPool::ThreadProc); - auto thread = Thread::Create(runnable, String::Format(TEXT("Therad Pool {0}"), i)); + auto thread = Thread::Create(runnable, String::Format(TEXT("Thread Pool {0}"), i)); if (thread == nullptr) { LOG(Error, "Failed to spawn {0} thread in the Thread Pool", i + 1); diff --git a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Material.cpp b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Material.cpp index 998c3f3a3..c1f250824 100644 --- a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Material.cpp +++ b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Material.cpp @@ -274,8 +274,10 @@ void MaterialGenerator::ProcessGroupMaterial(Box* box, Node* node, Value& value) // Compute depth difference auto depthDiff = writeLocal(VariantType::Float, String::Format(TEXT("{0} * ViewFar - {1}"), sceneDepth.Value, posVS.Value), node); + auto fadeDistance = tryGetValue(node->GetBox(0), node->Values[0]).AsFloat(); + // Apply smoothing factor and clamp the result - value = writeLocal(VariantType::Float, String::Format(TEXT("saturate({0} / {1})"), depthDiff.Value, node->Values[0].AsFloat), node); + value = writeLocal(VariantType::Float, String::Format(TEXT("saturate({0} / {1})"), depthDiff.Value, fadeDistance.Value), node); break; } // Material Function @@ -337,6 +339,125 @@ void MaterialGenerator::ProcessGroupMaterial(Box* box, Node* node, Value& value) case 25: value = Value(VariantType::Vector3, TEXT("GetObjectSize(input)")); break; + // Blend Normals + case 26: + { + const auto baseNormal = tryGetValue(node->GetBox(0), getNormalZero).AsVector3(); + const auto additionalNormal = tryGetValue(node->GetBox(1), getNormalZero).AsVector3(); + + const String text1 = String::Format(TEXT("(float2({0}.xy) + float2({1}.xy) * 2.0)"), baseNormal.Value, additionalNormal.Value); + const auto appendXY = writeLocal(ValueType::Vector2, text1, node); + + const String text2 = String::Format(TEXT("float3({0}, sqrt(saturate(1.0 - dot({0}.xy, {0}.xy))))"), appendXY.Value); + value = writeLocal(ValueType::Vector3, text2, node); + break; + } + // Rotator + case 27: + { + const auto uv = tryGetValue(node->GetBox(0), getUVs).AsVector2(); + const auto center = tryGetValue(node->GetBox(1), Value::Zero).AsVector2(); + const auto rotationAngle = tryGetValue(node->GetBox(2), Value::Zero).AsFloat(); + + auto x1 = writeLocal(ValueType::Vector2, String::Format(TEXT("({0} * -1) + {1}"), center.Value, uv.Value), node); + auto raCosSin = writeLocal(ValueType::Vector2, String::Format(TEXT("float2(cos({0}), sin({0}))"), rotationAngle.Value), node); + + auto dotB1 = writeLocal(ValueType::Vector2, String::Format(TEXT("float2({0}.x, {0}.y * -1)"), raCosSin.Value), node); + auto dotB2 = writeLocal(ValueType::Vector2, String::Format(TEXT("float2({0}.y, {0}.x)"), raCosSin.Value), node); + + value = writeLocal(ValueType::Vector2, String::Format(TEXT("{3} + float2(dot({0},{1}), dot({0},{2}))"), x1.Value, dotB1.Value, dotB2.Value, center.Value), node); + break; + } + // Sphere Mask + case 28: + { + const auto a = tryGetValue(node->GetBox(0), 0, Value::Zero); + const auto b = tryGetValue(node->GetBox(1), 1, Value::Zero).Cast(a.Type); + const auto radius = tryGetValue(node->GetBox(2), node->Values[0]).AsFloat(); + const auto hardness = tryGetValue(node->GetBox(3), node->Values[1]).AsFloat(); + const auto invert = tryGetValue(node->GetBox(4), node->Values[2]).AsBool(); + + // Get distance and apply radius + auto x1 = writeLocal(ValueType::Float, String::Format(TEXT("distance({0},{1}) * (1 / {2})"), a.Value, b.Value, radius.Value), node); + + // Apply hardness, use 0.991 as max since any value above will result in harsh aliasing + auto x2 = writeLocal(ValueType::Float, String::Format(TEXT("saturate((1 - {0}) * (1 / (1 - clamp({1}, 0, 0.991f))))"), x1.Value, hardness.Value), node); + + value = writeLocal(ValueType::Float, String::Format(TEXT("{0} ? (1 - {1}) : {1}"), invert.Value, x2.Value), node); + break; + } + // Tiling & Offset + case 29: + { + const auto uv = tryGetValue(node->GetBox(0), getUVs).AsVector2(); + const auto tiling = tryGetValue(node->GetBox(1), node->Values[0]).AsVector2(); + const auto offset = tryGetValue(node->GetBox(2), node->Values[1]).AsVector2(); + + value = writeLocal(ValueType::Vector2, String::Format(TEXT("{0} * {1} + {2}"), uv.Value, tiling.Value, offset.Value), node); + break; + } + // DDX + case 30: + { + const auto inValue = tryGetValue(node->GetBox(0), 0, Value::Zero); + + value = writeLocal(inValue.Type, String::Format(TEXT("ddx({0})"), inValue.Value), node); + break; + } + // DDY + case 31: + { + const auto inValue = tryGetValue(node->GetBox(0), 0, Value::Zero); + + value = writeLocal(inValue.Type, String::Format(TEXT("ddy({0})"), inValue.Value), node); + break; + } + // Sign + case 32: + { + const auto inValue = tryGetValue(node->GetBox(0), 0, Value::Zero); + + value = writeLocal(ValueType::Float, String::Format(TEXT("sign({0})"), inValue.Value), node); + break; + } + // Any + case 33: + { + const auto inValue = tryGetValue(node->GetBox(0), 0, Value::Zero); + + value = writeLocal(ValueType::Bool, String::Format(TEXT("any({0})"), inValue.Value), node); + break; + } + // All + case 34: + { + const auto inValue = tryGetValue(node->GetBox(0), 0, Value::Zero); + + value = writeLocal(ValueType::Bool, String::Format(TEXT("all({0})"), inValue.Value), node); + break; + } + // Blackbody + case 35: + { + // Reference: Mitchell Charity, http://www.vendian.org/mncharity/dir3/blackbody/ + + const auto temperature = tryGetValue(node->GetBox(0), node->Values[0]).AsFloat(); + + // Value X + auto x = writeLocal(ValueType::Float, String::Format(TEXT("56100000.0f * pow({0}, -1) + 148.0f"), temperature.Value), node); + + // Value Y + auto y = writeLocal(ValueType::Float, String::Format(TEXT("{0} > 6500.0f ? 35200000.0f * pow({0}, -1) + 184.0f : 100.04f * log({0}) - 623.6f"), temperature.Value), node); + + // Value Z + auto z = writeLocal(ValueType::Float, String::Format(TEXT("194.18f * log({0}) - 1448.6f"), temperature.Value), node); + + // Final color + auto color = writeLocal(ValueType::Vector3, String::Format(TEXT("float3({0}, {1}, {2})"), x.Value, y.Value, z.Value), node); + color = writeLocal(ValueType::Vector3, String::Format(TEXT("clamp({0}, 0.0f, 255.0f) / 255.0f"), color.Value), node); + value = writeLocal(ValueType::Vector3, String::Format(TEXT("{1} < 1000.0f ? {0} * {1}/1000.0f : {0}"), color.Value, temperature.Value), node); + break; + } default: break; } diff --git a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.cpp b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.cpp index 7abd29af9..57f669f7f 100644 --- a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.cpp +++ b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.cpp @@ -27,6 +27,7 @@ enum MaterialTemplateInputsMapping MaterialValue MaterialGenerator::getUVs(VariantType::Vector2, TEXT("input.TexCoord")); MaterialValue MaterialGenerator::getTime(VariantType::Float, TEXT("TimeParam")); MaterialValue MaterialGenerator::getNormal(VariantType::Vector3, TEXT("input.TBN[2]")); +MaterialValue MaterialGenerator::getNormalZero(VariantType::Vector3, TEXT("float3(0, 0, 1)")); MaterialValue MaterialGenerator::getVertexColor(VariantType::Vector4, TEXT("GetVertexColor(input)")); MaterialGenerator::MaterialGenerator() diff --git a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.h b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.h index 005b9c58c..804955618 100644 --- a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.h +++ b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.h @@ -205,6 +205,7 @@ public: static MaterialValue getUVs; static MaterialValue getTime; static MaterialValue getNormal; + static MaterialValue getNormalZero; static MaterialValue getVertexColor; static MaterialGraphBoxesMapping MaterialGraphBoxesMappings[]; diff --git a/Source/Engine/Tools/MaterialGenerator/MaterialLayer.cpp b/Source/Engine/Tools/MaterialGenerator/MaterialLayer.cpp index c7f940574..41679a6a3 100644 --- a/Source/Engine/Tools/MaterialGenerator/MaterialLayer.cpp +++ b/Source/Engine/Tools/MaterialGenerator/MaterialLayer.cpp @@ -64,7 +64,7 @@ void MaterialLayer::Prepare() Guid MaterialLayer::GetMappedParamId(const Guid& id) { - // TODO: test ParamIdsMappings using Dictionary. will performance change? mamybe we don't wont to allocate too much memory + // TODO: test ParamIdsMappings using Dictionary. will performance change? maybe we don't wont to allocate too much memory for (int32 i = 0; i < ParamIdsMappings.Count(); i++) { diff --git a/Source/Engine/Tools/ModelTool/ModelTool.Assimp.cpp b/Source/Engine/Tools/ModelTool/ModelTool.Assimp.cpp index fef8d7f3c..50914ea2e 100644 --- a/Source/Engine/Tools/ModelTool/ModelTool.Assimp.cpp +++ b/Source/Engine/Tools/ModelTool/ModelTool.Assimp.cpp @@ -355,7 +355,7 @@ bool ProcessMesh(AssimpImporterData& data, const aiMesh* aMesh, MeshData& mesh, mesh.BlendIndices.SetAll(Int4::Zero); mesh.BlendWeights.SetAll(Vector4::Zero); - // Build skinning clusters and fill controls points data stutcture + // Build skinning clusters and fill controls points data structure for (unsigned boneId = 0; boneId < aMesh->mNumBones; boneId++) { const auto aBone = aMesh->mBones[boneId]; diff --git a/Source/Engine/Tools/ModelTool/ModelTool.OpenFBX.cpp b/Source/Engine/Tools/ModelTool/ModelTool.OpenFBX.cpp index 4c2876125..757dfa6ea 100644 --- a/Source/Engine/Tools/ModelTool/ModelTool.OpenFBX.cpp +++ b/Source/Engine/Tools/ModelTool/ModelTool.OpenFBX.cpp @@ -744,7 +744,7 @@ bool ProcessMesh(OpenFbxImporterData& data, const ofbx::Mesh* aMesh, MeshData& m auto length = delta.Length(); if (length > ZeroTolerance) delta /= length;*/ - auto delta = Vector3::Zero; // TODO: blend shape normals deltas fix when importing from ofbx + auto delta = Vector3::Zero; // TODO: blend shape normals deltas fix when importing from fbx blendShapeData.Vertices[i].NormalDelta = delta; } } diff --git a/Source/Engine/Tools/ModelTool/ModelTool.cpp b/Source/Engine/Tools/ModelTool/ModelTool.cpp index 3d6c74482..05a69141f 100644 --- a/Source/Engine/Tools/ModelTool/ModelTool.cpp +++ b/Source/Engine/Tools/ModelTool/ModelTool.cpp @@ -15,7 +15,8 @@ #include "Engine/Tools/TextureTool/TextureTool.h" #include "Engine/ContentImporters/AssetsImportingManager.h" #include "Engine/ContentImporters/CreateMaterial.h" -#include "ThirdParty/meshoptimizer/meshoptimizer.h" +#include "Editor/Utilities/EditorUtilities.h" +#include void RemoveNamespace(String& name) { @@ -486,6 +487,11 @@ bool ModelTool::ImportModel(const String& path, ModelData& meshData, Options opt if (autoImportOutput.IsEmpty() || (data.Types & ImportDataTypes::Textures) == 0 || texture.FilePath.IsEmpty()) continue; auto filename = StringUtils::GetFileNameWithoutExtension(texture.FilePath); + for (int32 j = filename.Length() - 1; j >= 0; j--) + { + if (EditorUtilities::IsInvalidPathChar(filename[j])) + filename[j] = ' '; + } if (importedFileNames.Contains(filename)) { int32 counter = 1; @@ -526,6 +532,11 @@ bool ModelTool::ImportModel(const String& path, ModelData& meshData, Options opt if (autoImportOutput.IsEmpty() || (data.Types & ImportDataTypes::Materials) == 0 || !material.UsesProperties()) continue; auto filename = material.Name; + for (int32 j = filename.Length() - 1; j >= 0; j--) + { + if (EditorUtilities::IsInvalidPathChar(filename[j])) + filename[j] = ' '; + } if (importedFileNames.Contains(filename)) { int32 counter = 1; @@ -1131,7 +1142,7 @@ bool ModelTool::ImportModel(const String& path, ModelData& meshData, Options opt dstLod.Meshes.RemoveAt(i--); } - LOG(Info, "Generated LOD{0}: triangles: {1} ({2}% of base LOD), verteces: {3} ({4}% of base LOD)", + LOG(Info, "Generated LOD{0}: triangles: {1} ({2}% of base LOD), verticies: {3} ({4}% of base LOD)", lodIndex, lodTriangleCount, (int32)(lodTriangleCount * 100 / baseLodTriangleCount), lodVertexCount, (int32)(lodVertexCount * 100 / baseLodVertexCount)); diff --git a/Source/Engine/Tools/TextureTool/TextureTool.DirectXTex.cpp b/Source/Engine/Tools/TextureTool/TextureTool.DirectXTex.cpp index dbf51e965..0c423fed1 100644 --- a/Source/Engine/Tools/TextureTool/TextureTool.DirectXTex.cpp +++ b/Source/Engine/Tools/TextureTool/TextureTool.DirectXTex.cpp @@ -190,6 +190,16 @@ bool TextureTool::ExportTextureDirectXTex(ImageType type, const StringView& path } img = tmp.GetImage(0, 0, 0); } + else if (image.format == DXGI_FORMAT_R10G10B10A2_UNORM || image.format == DXGI_FORMAT_R11G11B10_FLOAT) + { + result = DirectX::Convert(image, DXGI_FORMAT_R8G8B8A8_UNORM, DirectX::TEX_FILTER_DEFAULT, DirectX::TEX_THRESHOLD_DEFAULT, tmp); + if (FAILED(result)) + { + LOG(Error, "Cannot convert texture, error: {0:x}", static_cast(result)); + return true; + } + img = tmp.GetImage(0, 0, 0); + } DirectX::WICCodecs codec; switch (type) diff --git a/Source/Engine/Tools/TextureTool/TextureTool.cpp b/Source/Engine/Tools/TextureTool/TextureTool.cpp index a8af48764..67e4d7679 100644 --- a/Source/Engine/Tools/TextureTool/TextureTool.cpp +++ b/Source/Engine/Tools/TextureTool/TextureTool.cpp @@ -7,7 +7,7 @@ #include "Engine/Core/Types/DateTime.h" #include "Engine/Core/Math/Packed.h" #include "Engine/Core/Math/Color32.h" -#include "Engine/Core/Math/VectorInt.h" +#include "Engine/Core/Math/Int2.h" #include "Engine/Platform/FileSystem.h" #include "Engine/Serialization/JsonWriter.h" #include "Engine/Serialization/JsonTools.h" @@ -316,7 +316,7 @@ bool TextureTool::Convert(TextureData& dst, const TextureData& src, const PixelF } if (src.Format == dstFormat) { - LOG(Warning, "Soure data and destination format are the same. Cannot perform conversion."); + LOG(Warning, "Source data and destination format are the same. Cannot perform conversion."); return true; } if (src.Depth != 1) @@ -343,7 +343,7 @@ bool TextureTool::Resize(TextureData& dst, const TextureData& src, int32 dstWidt } if (src.Width == dstWidth && src.Height == dstHeight) { - LOG(Warning, "Soure data and destination dimensions are the same. Cannot perform resizing."); + LOG(Warning, "Source data and destination dimensions are the same. Cannot perform resizing."); return true; } if (src.Depth != 1) @@ -489,11 +489,11 @@ TextureTool::PixelFormatSampler PixelFormatSamplers[] = sizeof(Half), [](const void* ptr) { - return Color(ConvertHalfToFloat(*(Half*)ptr), 0, 0, 1); + return Color(Float16Compressor::Decompress(*(Half*)ptr), 0, 0, 1); }, [](const void* ptr, const Color& color) { - *(Half*)ptr = ConvertFloatToHalf(color.R); + *(Half*)ptr = Float16Compressor::Compress(color.R); }, }, { diff --git a/Source/Engine/UI/GUI/Common/Dropdown.cs b/Source/Engine/UI/GUI/Common/Dropdown.cs index e78300e1a..917e51f6e 100644 --- a/Source/Engine/UI/GUI/Common/Dropdown.cs +++ b/Source/Engine/UI/GUI/Common/Dropdown.cs @@ -26,7 +26,7 @@ namespace FlaxEngine.GUI public Action ItemClicked; /// - /// Occurs when popup losts focus. + /// Occurs when popup lost focus. /// public Action LostFocus; diff --git a/Source/Engine/UI/GUI/Common/TextBoxBase.cs b/Source/Engine/UI/GUI/Common/TextBoxBase.cs index 107da6f40..ccf346ed6 100644 --- a/Source/Engine/UI/GUI/Common/TextBoxBase.cs +++ b/Source/Engine/UI/GUI/Common/TextBoxBase.cs @@ -942,6 +942,8 @@ namespace FlaxEngine.GUI { base.OnLostFocus(); + if (IsReadOnly) + return; OnEditEnd(); } diff --git a/Source/Engine/UI/GUI/ContainerControl.cs b/Source/Engine/UI/GUI/ContainerControl.cs index 44f8c782a..fb11f2eff 100644 --- a/Source/Engine/UI/GUI/ContainerControl.cs +++ b/Source/Engine/UI/GUI/ContainerControl.cs @@ -469,7 +469,7 @@ namespace FlaxEngine.GUI } /// - /// Checks if given point in thi container control space intersects with the child control content. + /// Checks if given point in this container control space intersects with the child control content. /// Also calculates result location in child control space which can be used to feed control with event at that point. /// /// The child control to check. @@ -636,7 +636,7 @@ namespace FlaxEngine.GUI } /// - /// Draws the children. Can be overriden to provide some customizations. Draw is performed with applied clipping mask for the client area. + /// Draws the children. Can be overridden to provide some customizations. Draw is performed with applied clipping mask for the client area. /// protected virtual void DrawChildren() { diff --git a/Source/Engine/UI/GUI/Control.cs b/Source/Engine/UI/GUI/Control.cs index 74041610c..c2ba34f05 100644 --- a/Source/Engine/UI/GUI/Control.cs +++ b/Source/Engine/UI/GUI/Control.cs @@ -1006,10 +1006,9 @@ namespace FlaxEngine.GUI c = c.Parent; if (c == parent) - return location; + break; } - - throw new ArgumentException(); + return location; } /// @@ -1211,7 +1210,7 @@ namespace FlaxEngine.GUI } /// - /// Action fred when parent control gets changed. + /// Action fired when parent control gets changed. /// protected virtual void OnParentChangedInternal() { @@ -1252,9 +1251,9 @@ namespace FlaxEngine.GUI } /// - /// Helper utility function to sets the update callback to the root. Does nothing if value has not been modified. Handles if control ahs no root or parent. + /// Helper utility function to sets the update callback to the root. Does nothing if value has not been modified. Handles if control has no root or parent. /// - /// The cached update callback delegate (field in teh custom control implementation). + /// The cached update callback delegate (field in the custom control implementation). /// The value to assign. protected void SetUpdate(ref UpdateDelegate onUpdate, UpdateDelegate value) { @@ -1268,7 +1267,7 @@ namespace FlaxEngine.GUI } /// - /// Action fred when parent control gets resized (also when control gets non-null parent). + /// Action fired when parent control gets resized (also when control gets non-null parent). /// public virtual void OnParentResized() { diff --git a/Source/Engine/UI/GUI/Panels/BlurPanel.cs b/Source/Engine/UI/GUI/Panels/BlurPanel.cs index e6d699876..a894ea71d 100644 --- a/Source/Engine/UI/GUI/Panels/BlurPanel.cs +++ b/Source/Engine/UI/GUI/Panels/BlurPanel.cs @@ -11,7 +11,7 @@ namespace FlaxEngine.GUI /// /// Gets or sets the blur strength. Defines how blurry the background is. Larger numbers increase blur, resulting in a larger runtime cost on the GPU. /// - [EditorOrder(0), Limit(0, 100, 0.1f), Tooltip("Blur strength defines how blurry the background is. Larger numbers increase blur, resulting in a larger runtime cost on the GPU.")] + [EditorOrder(0), Limit(0, 100, 0.0f), Tooltip("Blur strength defines how blurry the background is. Larger numbers increase blur, resulting in a larger runtime cost on the GPU.")] public float BlurStrength { get; set; } /// diff --git a/Source/Engine/UI/GUI/Panels/HorizontalPanel.cs b/Source/Engine/UI/GUI/Panels/HorizontalPanel.cs index 8f8e7f994..d7b54a835 100644 --- a/Source/Engine/UI/GUI/Panels/HorizontalPanel.cs +++ b/Source/Engine/UI/GUI/Panels/HorizontalPanel.cs @@ -42,10 +42,10 @@ namespace FlaxEngine.GUI for (int i = 0; i < _children.Count; i++) { Control c = _children[i]; - if (c.Visible) + if (c.Visible && Mathf.IsZero(c.AnchorMax.X)) { var w = c.Width; - c.Bounds = new Rectangle(x + _offset.X, _margin.Top + _offset.Y, h, w); + c.Bounds = new Rectangle(x + _offset.X, _margin.Top + _offset.Y, w, h); x = c.Right + _spacing; hasAnyItem = true; } diff --git a/Source/Engine/UI/GUI/Panels/SplitPanel.cs b/Source/Engine/UI/GUI/Panels/SplitPanel.cs index 733e0b3a1..04160db25 100644 --- a/Source/Engine/UI/GUI/Panels/SplitPanel.cs +++ b/Source/Engine/UI/GUI/Panels/SplitPanel.cs @@ -12,12 +12,12 @@ namespace FlaxEngine.GUI /// /// The splitter size (in pixels). /// - public const int SpliterSize = 4; + public const int SplitterSize = 4; /// /// The splitter half size (in pixels). /// - private const int SpliterSizeHalf = SpliterSize / 2; + private const int SplitterSizeHalf = SplitterSize / 2; private Orientation _orientation; private float _splitterValue; @@ -105,12 +105,12 @@ namespace FlaxEngine.GUI if (_orientation == Orientation.Horizontal) { var split = Mathf.RoundToInt(_splitterValue * Width); - _splitterRect = new Rectangle(Mathf.Clamp(split - SpliterSizeHalf, 0.0f, Width), 0, SpliterSize, Height); + _splitterRect = new Rectangle(Mathf.Clamp(split - SplitterSizeHalf, 0.0f, Width), 0, SplitterSize, Height); } else { var split = Mathf.RoundToInt(_splitterValue * Height); - _splitterRect = new Rectangle(0, Mathf.Clamp(split - SpliterSizeHalf, 0.0f, Height), Width, SpliterSize); + _splitterRect = new Rectangle(0, Mathf.Clamp(split - SplitterSizeHalf, 0.0f, Height), Width, SplitterSize); } } @@ -226,14 +226,14 @@ namespace FlaxEngine.GUI if (_orientation == Orientation.Horizontal) { var split = Mathf.RoundToInt(_splitterValue * Width); - Panel1.Bounds = new Rectangle(0, 0, split - SpliterSizeHalf, Height); - Panel2.Bounds = new Rectangle(split + SpliterSizeHalf, 0, Width - split - SpliterSizeHalf, Height); + Panel1.Bounds = new Rectangle(0, 0, split - SplitterSizeHalf, Height); + Panel2.Bounds = new Rectangle(split + SplitterSizeHalf, 0, Width - split - SplitterSizeHalf, Height); } else { var split = Mathf.RoundToInt(_splitterValue * Height); - Panel1.Bounds = new Rectangle(0, 0, Width, split - SpliterSizeHalf); - Panel2.Bounds = new Rectangle(0, split + SpliterSizeHalf, Width, Height - split - SpliterSizeHalf); + Panel1.Bounds = new Rectangle(0, 0, Width, split - SplitterSizeHalf); + Panel2.Bounds = new Rectangle(0, split + SplitterSizeHalf, Width, Height - split - SplitterSizeHalf); } } } diff --git a/Source/Engine/UI/GUI/Panels/UniformGridPanel.cs b/Source/Engine/UI/GUI/Panels/UniformGridPanel.cs index 089907901..62c6a9744 100644 --- a/Source/Engine/UI/GUI/Panels/UniformGridPanel.cs +++ b/Source/Engine/UI/GUI/Panels/UniformGridPanel.cs @@ -14,7 +14,7 @@ namespace FlaxEngine.GUI /// /// Gets or sets the padding given to each slot. /// - [EditorOrder(0), Tooltip("The padding margin appied to each item slot.")] + [EditorOrder(0), Tooltip("The padding margin applied to each item slot.")] public Margin SlotPadding { get => _slotPadding; diff --git a/Source/Engine/UI/GUI/Panels/VerticalPanel.cs b/Source/Engine/UI/GUI/Panels/VerticalPanel.cs index f811755ef..2c821bd3f 100644 --- a/Source/Engine/UI/GUI/Panels/VerticalPanel.cs +++ b/Source/Engine/UI/GUI/Panels/VerticalPanel.cs @@ -42,7 +42,7 @@ namespace FlaxEngine.GUI for (int i = 0; i < _children.Count; i++) { Control c = _children[i]; - if (c.Visible) + if (c.Visible && Mathf.IsZero(c.AnchorMax.Y)) { var h = c.Height; c.Bounds = new Rectangle(_margin.Left + _offset.X, y + _offset.Y, w, h); diff --git a/Source/Engine/UI/GUI/WindowRootControl.cs b/Source/Engine/UI/GUI/WindowRootControl.cs index 03a994aa6..02c996b68 100644 --- a/Source/Engine/UI/GUI/WindowRootControl.cs +++ b/Source/Engine/UI/GUI/WindowRootControl.cs @@ -151,7 +151,7 @@ namespace FlaxEngine.GUI } /// - public override Vector2 TrackingMouseOffset => _window.TrackingMouseOffset; + public override Vector2 TrackingMouseOffset => _window.TrackingMouseOffset / _window._dpiScale; /// public override WindowRootControl RootWindow => this; @@ -262,14 +262,14 @@ namespace FlaxEngine.GUI return false; // Change focused control - Control prevous = _focusedControl; + Control previous = _focusedControl; _focusedControl = c; // Fire events - if (prevous != null) + if (previous != null) { - prevous.OnLostFocus(); - Assert.IsFalse(prevous.IsFocused); + previous.OnLostFocus(); + Assert.IsFalse(previous.IsFocused); } if (_focusedControl != null) { diff --git a/Source/Engine/UI/UICanvas.cpp b/Source/Engine/UI/UICanvas.cpp index 67dbaaa5e..49d6ac420 100644 --- a/Source/Engine/UI/UICanvas.cpp +++ b/Source/Engine/UI/UICanvas.cpp @@ -17,13 +17,17 @@ MMethod* UICanvas_OnDisable = nullptr; MMethod* UICanvas_EndPlay = nullptr; #define UICANVAS_INVOKE(event) \ - MonoObject* exception = nullptr; \ - UICanvas_##event->Invoke(GetManagedInstance(), nullptr, &exception); \ - if (exception) \ - { \ - MException ex(exception); \ - ex.Log(LogType::Error, TEXT("UICanvas::" #event)); \ - } + auto instance = GetManagedInstance(); \ + if (instance) \ + { \ + MonoObject* exception = nullptr; \ + UICanvas_##event->Invoke(instance, nullptr, &exception); \ + if (exception) \ + { \ + MException ex(exception); \ + ex.Log(LogType::Error, TEXT("UICanvas::" #event)); \ + } \ + } UICanvas::UICanvas(const SpawnParams& params) : Actor(params) diff --git a/Source/Engine/UI/UICanvas.cs b/Source/Engine/UI/UICanvas.cs index 9ccbef214..426441493 100644 --- a/Source/Engine/UI/UICanvas.cs +++ b/Source/Engine/UI/UICanvas.cs @@ -1,5 +1,6 @@ // Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. +using System; using System.Globalization; using System.IO; using System.Text; @@ -186,14 +187,14 @@ namespace FlaxEngine public CanvasRootControl GUI => _guiRoot; /// - /// Delegate schema for the callback used to perform custom canvas intersection test. Can be used to implement a canvas tha has a holes or non-rectangular shape. + /// Delegate schema for the callback used to perform custom canvas intersection test. Can be used to implement a canvas that has a holes or non-rectangular shape. /// /// The location of the point to test in coordinates of the canvas root control (see ). /// True if canvas was hit, otherwise false. public delegate bool TestCanvasIntersectionDelegate(ref Vector2 location); /// - /// The callback used to perform custom canvas intersection test. Can be used to implement a canvas tha has a holes or non-rectangular shape. + /// The callback used to perform custom canvas intersection test. Can be used to implement a canvas that has a holes or non-rectangular shape. /// [HideInEditor] public TestCanvasIntersectionDelegate TestCanvasIntersection; @@ -228,7 +229,10 @@ namespace FlaxEngine } } - private UICanvas() + /// + /// Initializes a new instance of the class. + /// + public UICanvas() { _guiRoot = new CanvasRootControl(this); _guiRoot.IsLayoutLocked = false; @@ -276,16 +280,41 @@ namespace FlaxEngine else if (_renderMode == CanvasRenderMode.CameraSpace) { Matrix tmp1, tmp2; + Vector3 viewPos, viewUp, viewForward, pos; + Quaternion viewRot; // Use default camera is not specified var camera = RenderCamera ?? Camera.MainCamera; +#if FLAX_EDITOR + if (_editorTask) + { + // Use editor viewport task to override Camera Space placement + var view = _editorTask.View; + var frustum = view.Frustum; + if (!frustum.IsOrthographic) + _guiRoot.Size = new Vector2(frustum.GetWidthAtDepth(Distance), frustum.GetHeightAtDepth(Distance)); + else + _guiRoot.Size = _editorTask.Viewport.Size; + Matrix.Translation(_guiRoot.Width / -2.0f, _guiRoot.Height / -2.0f, 0, out world); + Matrix.RotationYawPitchRoll(Mathf.Pi, Mathf.Pi, 0, out tmp2); + Matrix.Multiply(ref world, ref tmp2, out tmp1); + viewPos = view.Position; + viewRot = view.Direction != Vector3.Up ? Quaternion.LookRotation(view.Direction, Vector3.Up) : Quaternion.LookRotation(view.Direction, Vector3.Right); + viewUp = Vector3.Up * viewRot; + viewForward = view.Direction; + pos = view.Position + view.Direction * Distance; + Matrix.Billboard(ref pos, ref viewPos, ref viewUp, ref viewForward, out tmp2); + Matrix.Multiply(ref tmp1, ref tmp2, out world); + return; + } +#endif + // Adjust GUI size to the viewport size at the given distance form the camera var viewport = camera.Viewport; if (camera.UsePerspective) { - Matrix tmp3; - camera.GetMatrices(out tmp1, out tmp3, ref viewport); + camera.GetMatrices(out tmp1, out var tmp3, ref viewport); Matrix.Multiply(ref tmp1, ref tmp3, out tmp2); var frustum = new BoundingFrustum(tmp2); _guiRoot.Size = new Vector2(frustum.GetWidthAtDepth(Distance), frustum.GetHeightAtDepth(Distance)); @@ -301,11 +330,11 @@ namespace FlaxEngine Matrix.Multiply(ref world, ref tmp2, out tmp1); // In front of the camera - var viewPos = camera.Position; - var viewRot = camera.Orientation; - var viewUp = Vector3.Up * viewRot; - var viewForward = Vector3.Forward * viewRot; - var pos = viewPos + viewForward * Distance; + viewPos = camera.Position; + viewRot = camera.Orientation; + viewUp = Vector3.Up * viewRot; + viewForward = Vector3.Forward * viewRot; + pos = viewPos + viewForward * Distance; Matrix.Billboard(ref pos, ref viewPos, ref viewUp, ref viewForward, out tmp2); Matrix.Multiply(ref tmp1, ref tmp2, out world); @@ -331,11 +360,18 @@ namespace FlaxEngine _guiRoot.Offsets = Margin.Zero; if (_renderer) { +#if FLAX_EDITOR + _editorTask?.CustomPostFx.Remove(_renderer); +#endif SceneRenderTask.GlobalCustomPostFx.Remove(_renderer); _renderer.Canvas = null; Destroy(_renderer); _renderer = null; } +#if FLAX_EDITOR + if (_editorRoot != null) + _guiRoot.Parent = _editorRoot; +#endif break; } case CanvasRenderMode.CameraSpace: @@ -343,12 +379,31 @@ namespace FlaxEngine { // Render canvas manually _guiRoot.AnchorPreset = AnchorPresets.TopLeft; +#if FLAX_EDITOR + if (_editorRoot != null && _guiRoot != null) + _guiRoot.Parent = null; +#endif if (_renderer == null) { _renderer = New(); _renderer.Canvas = this; if (IsActiveInHierarchy && Scene) + { +#if FLAX_EDITOR + if (_editorTask != null) + { + _editorTask.CustomPostFx.Add(_renderer); + break; + } +#endif SceneRenderTask.GlobalCustomPostFx.Add(_renderer); + } +#if FLAX_EDITOR + else if (_editorTask != null && IsActiveInHierarchy) + { + _editorTask.CustomPostFx.Add(_renderer); + } +#endif } break; } @@ -487,10 +542,21 @@ namespace FlaxEngine internal void OnEnable() { +#if FLAX_EDITOR + _guiRoot.Parent = _editorRoot ?? RootControl.CanvasRoot; +#else _guiRoot.Parent = RootControl.CanvasRoot; +#endif if (_renderer) { +#if FLAX_EDITOR + if (_editorTask != null) + { + _editorTask.CustomPostFx.Add(_renderer); + return; + } +#endif SceneRenderTask.GlobalCustomPostFx.Add(_renderer); } } @@ -515,5 +581,25 @@ namespace FlaxEngine _renderer = null; } } + +#if FLAX_EDITOR + private SceneRenderTask _editorTask; + private ContainerControl _editorRoot; + + internal void EditorOverride(SceneRenderTask task, ContainerControl root) + { + if (_editorTask != null && _renderer != null) + _editorTask.CustomPostFx.Remove(_renderer); + if (_editorRoot != null && _guiRoot != null) + _guiRoot.Parent = null; + + _editorTask = task; + _editorRoot = root; + Setup(); + + if (RenderMode == CanvasRenderMode.ScreenSpace && _editorRoot != null && _guiRoot != null) + _guiRoot.Parent = _editorRoot; + } +#endif } } diff --git a/Source/Engine/UI/UIControl.cs b/Source/Engine/UI/UIControl.cs index e0f15a106..b31d9cab0 100644 --- a/Source/Engine/UI/UIControl.cs +++ b/Source/Engine/UI/UIControl.cs @@ -13,6 +13,7 @@ namespace FlaxEngine partial class UIControl { private Control _control; + private static bool _blockEvents; // Used to ignore internal events from C++ UIControl impl when performing state sync with C# UI /// /// Gets or sets the GUI control used by this actor. @@ -30,10 +31,11 @@ namespace FlaxEngine return; // Cleanup previous - if (_control != null) + var prevControl = _control; + if (prevControl != null) { - _control.LocationChanged -= OnControlLocationChanged; - _control.Dispose(); + prevControl.LocationChanged -= OnControlLocationChanged; + prevControl.Dispose(); } // Set value @@ -42,16 +44,18 @@ namespace FlaxEngine // Link the new one (events and parent) if (_control != null) { + // Setup control + _blockEvents = true; var containerControl = _control as ContainerControl; if (containerControl != null) containerControl.UnlockChildrenRecursive(); - _control.Parent = GetParent(); _control.IndexInParent = OrderInParent; _control.Location = new Vector2(LocalPosition); // TODO: sync control order in parent with actor order in parent (think about special cases like Panel with scroll bars used as internal controls) _control.LocationChanged += OnControlLocationChanged; + // Link children UI controls if (containerControl != null && IsActiveInHierarchy) { var children = ChildrenCount; @@ -64,6 +68,13 @@ namespace FlaxEngine } } } + + // Refresh + _blockEvents = false; + if (prevControl == null && _control.Parent != null) + _control.Parent.PerformLayout(); + else + _control.PerformLayout(); } } } @@ -170,7 +181,9 @@ namespace FlaxEngine private void OnControlLocationChanged(Control control) { + _blockEvents = true; LocalPosition = new Vector3(control.Location, LocalPosition.Z); + _blockEvents = false; } /// @@ -285,7 +298,7 @@ namespace FlaxEngine internal void ParentChanged() { - if (_control != null) + if (_control != null && !_blockEvents) { _control.Parent = GetParent(); _control.IndexInParent = OrderInParent; @@ -294,13 +307,15 @@ namespace FlaxEngine internal void TransformChanged() { - if (_control != null) + if (_control != null && !_blockEvents) + { _control.Location = new Vector2(LocalPosition); + } } internal void ActiveInTreeChanged() { - if (_control != null) + if (_control != null && !_blockEvents) { // Link or unlink control (won't modify Enable/Visible state) _control.Parent = GetParent(); @@ -310,8 +325,10 @@ namespace FlaxEngine internal void OrderInParentChanged() { - if (_control != null) + if (_control != null && !_blockEvents) + { _control.IndexInParent = OrderInParent; + } } internal void BeginPlay() diff --git a/Source/Engine/Utilities/RectPack.h b/Source/Engine/Utilities/RectPack.h index f020b0481..a8ffb0b7e 100644 --- a/Source/Engine/Utilities/RectPack.h +++ b/Source/Engine/Utilities/RectPack.h @@ -25,7 +25,7 @@ struct RectPack SizeType Width; SizeType Height; - // The reaming space amount inside this slot (updated on every insertion, initial it equal to width*height). + // The remaining space amount inside this slot (updated on every insertion, initial it equal to width*height). SizeType SpaceLeft; // True, if slot has been allocated, otherwise it's free. diff --git a/Source/Engine/Utilities/StateMachine.h b/Source/Engine/Utilities/StateMachine.h index 292a67978..38e69a06b 100644 --- a/Source/Engine/Utilities/StateMachine.h +++ b/Source/Engine/Utilities/StateMachine.h @@ -50,7 +50,7 @@ public: /// /// Checks if can exit from that state /// - /// Next state to ener after exit from the current state + /// Next state to enter after exit from the current state /// True if can exit from that state, otherwise false virtual bool CanExit(State* nextState) const { diff --git a/Source/Engine/Visject/ShaderGraph.cpp b/Source/Engine/Visject/ShaderGraph.cpp index 61c609307..c8a8782e9 100644 --- a/Source/Engine/Visject/ShaderGraph.cpp +++ b/Source/Engine/Visject/ShaderGraph.cpp @@ -380,6 +380,15 @@ void ShaderGenerator::ProcessGroupMath(Box* box, Node* node, Value& value) Value v2 = tryGetValue(node->GetBox(1), Value::Zero); value = writeFunction2(node, v1, v2, TEXT("atan2")); break; + } + // Near Equal + case 42: + { + Value v1 = tryGetValue(node->GetBox(0), Value::Zero); + Value v2 = tryGetValue(node->GetBox(1), Value::Zero).Cast(v1.Type); + Value epsilon = tryGetValue(node->GetBox(2), 2, Value::Zero); + value = writeLocal(ValueType::Bool, String::Format(TEXT("distance({0},{1}) < {2}"), v1.Value, v2.Value, epsilon.Value), node); + break; } // Degrees case 43: @@ -392,6 +401,18 @@ void ShaderGenerator::ProcessGroupMath(Box* box, Node* node, Value& value) { value = writeFunction1(node, tryGetValue(node->GetBox(0), Value::Zero), TEXT("radians")); break; + } + // Remap + case 48: + { + const auto inVal = tryGetValue(node->GetBox(0), node->Values[0].AsFloat); + const auto rangeA = tryGetValue(node->GetBox(1), node->Values[1].AsVector2()); + const auto rangeB = tryGetValue(node->GetBox(2), node->Values[2].AsVector2()); + const auto clamp = tryGetValue(node->GetBox(3), node->Values[3]).AsBool(); + + const auto mapFunc = String::Format(TEXT("{2}.x + ({0} - {1}.x) * ({2}.y - {2}.x) / ({1}.y - {1}.x)"), inVal.Value, rangeA.Value, rangeB.Value); + value = writeLocal(ValueType::Float, String::Format(TEXT("{2} ? clamp({0}, {1}.x, {1}.y) : {0}"), mapFunc, rangeB.Value, clamp.Value), node); + break; } default: break; @@ -911,7 +932,7 @@ void ShaderGenerator::ProcessGroupComparisons(Box* box, Node* node, Value& value const Value condition = tryGetValue(node->GetBox(0), Value::False).AsBool(); const Value onTrue = tryGetValue(node->GetBox(2), 1, Value::Zero); const Value onFalse = tryGetValue(node->GetBox(1), 0, Value::Zero).Cast(onTrue.Type); - value = writeLocal(onTrue.Type, String::Format(TEXT("({0}) ? ({1}) : ({2})"), condition.Value, onTrue.Value, onFalse.Value), node); + value = writeLocal(onTrue.Type, String::Format(TEXT("{0} ? {1} : {2}"), condition.Value, onTrue.Value, onFalse.Value), node); break; } } diff --git a/Source/Engine/Visject/VisjectGraph.cpp b/Source/Engine/Visject/VisjectGraph.cpp index eb2b8bf61..939013ebd 100644 --- a/Source/Engine/Visject/VisjectGraph.cpp +++ b/Source/Engine/Visject/VisjectGraph.cpp @@ -371,6 +371,20 @@ void VisjectExecutor::ProcessGroupMath(Box* box, Node* node, Value& value) if (value.Type.Type == VariantType::Enum) value.AsUint64 = value.AsUint64 | (uint64)tryGetValue(node->GetBox(1), Value::Zero); break; + // Remap + case 48: + { + const float inVal = tryGetValue(node->GetBox(0), node->Values[0]).AsFloat; + const Vector2 rangeA = tryGetValue(node->GetBox(1), node->Values[1]).AsVector2(); + const Vector2 rangeB = tryGetValue(node->GetBox(2), node->Values[2]).AsVector2(); + const bool clamp = tryGetValue(node->GetBox(3), node->Values[3]).AsBool; + + auto mapFunc = rangeB.X + (inVal - rangeA.X) * (rangeB.Y - rangeB.X) / (rangeA.Y - rangeA.X); + + // Clamp value? + value = clamp ? Math::Clamp(mapFunc, rangeB.X, rangeB.Y) : mapFunc; + break; + } default: break; } diff --git a/Source/FlaxEngine.Gen.cs b/Source/FlaxEngine.Gen.cs index ceab5ff53..04aef0556 100644 --- a/Source/FlaxEngine.Gen.cs +++ b/Source/FlaxEngine.Gen.cs @@ -13,5 +13,5 @@ using System.Runtime.InteropServices; [assembly: AssemblyCulture("")] [assembly: ComVisible(false)] [assembly: Guid("095aaaed-cc57-6182-57cc-82617b3c2889")] -[assembly: AssemblyVersion("1.0.6215")] -[assembly: AssemblyFileVersion("1.0.6215")] +[assembly: AssemblyVersion("1.0.6216")] +[assembly: AssemblyFileVersion("1.0.6216")] diff --git a/Source/FlaxEngine.Gen.h b/Source/FlaxEngine.Gen.h index 4ceda354d..53a18990c 100644 --- a/Source/FlaxEngine.Gen.h +++ b/Source/FlaxEngine.Gen.h @@ -5,11 +5,11 @@ #include "Engine/Core/Compiler.h" #define FLAXENGINE_NAME "FlaxEngine" -#define FLAXENGINE_VERSION Version(1, 0, 6215) -#define FLAXENGINE_VERSION_TEXT "1.0.6215" +#define FLAXENGINE_VERSION Version(1, 0, 6216) +#define FLAXENGINE_VERSION_TEXT "1.0.6216" #define FLAXENGINE_VERSION_MAJOR 1 #define FLAXENGINE_VERSION_MINOR 0 -#define FLAXENGINE_VERSION_BUILD 6215 +#define FLAXENGINE_VERSION_BUILD 6216 #define FLAXENGINE_COMPANY "Flax" #define FLAXENGINE_COPYRIGHT "Copyright (c) 2012-2021 Wojciech Figat. All rights reserved." diff --git a/Source/Platforms/Windows/Binaries/ThirdParty/x64/MonoPosixHelper.dll b/Source/Platforms/Windows/Binaries/ThirdParty/x64/MonoPosixHelper.dll index 65c280e99..dd082f208 100644 --- a/Source/Platforms/Windows/Binaries/ThirdParty/x64/MonoPosixHelper.dll +++ b/Source/Platforms/Windows/Binaries/ThirdParty/x64/MonoPosixHelper.dll @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9ad92c41f484482e217f791b5ae7963f5d55c2799bc5578125a88f2d40f33262 +oid sha256:35a252fcb61a805c85558646d073e32f7eb54666931a73380b4dea2879c72584 size 157696 diff --git a/Source/Platforms/Windows/Binaries/ThirdParty/x64/libgcmonosgen.pdb b/Source/Platforms/Windows/Binaries/ThirdParty/x64/libgcmonosgen.pdb index 1a80321ba..166a8df92 100644 --- a/Source/Platforms/Windows/Binaries/ThirdParty/x64/libgcmonosgen.pdb +++ b/Source/Platforms/Windows/Binaries/ThirdParty/x64/libgcmonosgen.pdb @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8bd411ac3aa425db683905a5fa2445d7d770e5d04821757fcdcaa42e0d0ac986 +oid sha256:d165b1a28ce64c030b4d242b86b813292ce038e0155ad0d6025ed7472e036ce4 size 348160 diff --git a/Source/Platforms/Windows/Binaries/ThirdParty/x64/libmini.pdb b/Source/Platforms/Windows/Binaries/ThirdParty/x64/libmini.pdb index 7c24a2502..719362b2d 100644 --- a/Source/Platforms/Windows/Binaries/ThirdParty/x64/libmini.pdb +++ b/Source/Platforms/Windows/Binaries/ThirdParty/x64/libmini.pdb @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:184d1b18bb84c35fa43d878cedb60f53f203207208882ce6518a2d7a35308332 +oid sha256:9cc98ae831f784a7dd6f15a1dbb083c63e6c50100a201501a7825a6d85baf53e size 864256 diff --git a/Source/Platforms/Windows/Binaries/ThirdParty/x64/libmono-static.lib b/Source/Platforms/Windows/Binaries/ThirdParty/x64/libmono-static.lib index 366a28594..1cc28852d 100644 --- a/Source/Platforms/Windows/Binaries/ThirdParty/x64/libmono-static.lib +++ b/Source/Platforms/Windows/Binaries/ThirdParty/x64/libmono-static.lib @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f633de4fbae8e4d65aec49eaaa06f37745bfc28328f4193020434dab398235cc -size 28245086 +oid sha256:2eac1fd52b8d87a0fe4dce3c02e68da8b35bfaa44cb1689d5d8935f2ba09531b +size 28241218 diff --git a/Source/Shaders/BakeLightmap.shader b/Source/Shaders/BakeLightmap.shader index 4f206469e..14f99b4f4 100644 --- a/Source/Shaders/BakeLightmap.shader +++ b/Source/Shaders/BakeLightmap.shader @@ -328,7 +328,7 @@ void CS_BlurEmpty(uint3 GroupID : SV_GroupID, uint3 GroupThreadID : SV_GroupThre const int2 location = int2(GroupID.x, GroupID.y); const uint texelAdress = (location.y * AtlasSize + location.x) * NUM_SH_TARGETS; - // TODO: use more therads to sample lightmap and final therad make it blur + // TODO: use more threads to sample lightmap and final therad make it blur // Simple box filter (using only valid samples) const int blurRadius = 2; diff --git a/Source/Shaders/BitonicSort.shader b/Source/Shaders/BitonicSort.shader index b75f553b1..8a53802d0 100644 --- a/Source/Shaders/BitonicSort.shader +++ b/Source/Shaders/BitonicSort.shader @@ -32,7 +32,7 @@ uint InsertOneBit(uint value, uint oneBitMask) // Determines if two sort keys should be swapped in the list. KeySign is // either 1 or -1. Multiplication with the KeySign will either invert the sign -// (effectively a negation) or leave the value alone. When the the KeySign is +// (effectively a negation) or leave the value alone. When the KeySign is // 1, we are sorting descending, so when A < B, they should swap. For an // ascending sort, -A < -B should swap. bool ShouldSwap(Item a, Item b) diff --git a/Source/Shaders/Lighting.hlsl b/Source/Shaders/Lighting.hlsl index 9838cca52..b5021b711 100644 --- a/Source/Shaders/Lighting.hlsl +++ b/Source/Shaders/Lighting.hlsl @@ -4,7 +4,7 @@ #define __LIGHTING__ #if !defined(USE_GBUFFER_CUSTOM_DATA) -#error "Canot calculate lighting without custom data in GBuffer. Define USE_GBUFFER_CUSTOM_DATA." +#error "Cannot calculate lighting without custom data in GBuffer. Define USE_GBUFFER_CUSTOM_DATA." #endif #include "./Flax/LightingCommon.hlsl" diff --git a/Source/ThirdParty/OpenFBX/ofbx.cpp b/Source/ThirdParty/OpenFBX/ofbx.cpp index dc7c92718..36f942cf8 100644 --- a/Source/ThirdParty/OpenFBX/ofbx.cpp +++ b/Source/ThirdParty/OpenFBX/ofbx.cpp @@ -2894,7 +2894,7 @@ static float getFramerateFromTimeMode(FrameRate time_mode, float custom_frame_ra { switch (time_mode) { - case FrameRate_DEFAULT: return 1; + case FrameRate_DEFAULT: return 14; case FrameRate_120: return 120; case FrameRate_100: return 100; case FrameRate_60: return 60; diff --git a/Source/ThirdParty/rapidjson/document.h b/Source/ThirdParty/rapidjson/document.h index d9472ec1e..2691a6386 100644 --- a/Source/ThirdParty/rapidjson/document.h +++ b/Source/ThirdParty/rapidjson/document.h @@ -1679,7 +1679,18 @@ public: //@{ const Ch* GetString() const { RAPIDJSON_ASSERT(IsString()); return (data_.f.flags & kInlineStrFlag) ? data_.ss.str : GetStringPointer(); } - ::String GetText() const { RAPIDJSON_ASSERT(IsString()); return ::String(GetString(), GetStringLength()); } + ::String GetText() const + { + ::String result; + if (IsString()) + { + if (data_.f.flags & kInlineStrFlag) + result.Set(data_.ss.str, data_.ss.GetLength()); + else + result.Set(GetStringPointer(), data_.s.length); + } + return result; + } //! Get the length of string. /*! Since rapidjson permits "\\u0000" in the json string, strlen(v.GetString()) may not equal to v.GetStringLength(). diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs index d51d7b754..15d8825a8 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs @@ -488,10 +488,7 @@ namespace Flax.Build.Bindings { if (comment.Contains("/// ")) continue; - var c = comment.Replace("::", "."); - contents.Append(indent); - contents.Append(c); - contents.AppendLine(); + contents.Append(indent).Append(comment.Replace("::", ".")).AppendLine(); } GenerateCSharpAttributes(buildData, contents, indent, eventInfo, true); @@ -586,11 +583,7 @@ namespace Flax.Build.Bindings { if (comment.Contains("/// ")) continue; - - var c = comment.Replace("::", "."); - contents.Append(indent); - contents.Append(c); - contents.AppendLine(); + contents.Append(indent).Append(comment.Replace("::", ".")).AppendLine(); } GenerateCSharpAttributes(buildData, contents, indent, fieldInfo, true); @@ -636,14 +629,7 @@ namespace Flax.Build.Bindings { if (comment.Contains("/// ") || comment.Contains(" a, List b) + public static bool Equals(List a, List b) { if (a == null && b == null) return true; diff --git a/Source/Tools/Flax.Build/Build/NativeCpp/CompileEnvironment.cs b/Source/Tools/Flax.Build/Build/NativeCpp/CompileEnvironment.cs index b1159db1c..9939cea53 100644 --- a/Source/Tools/Flax.Build/Build/NativeCpp/CompileEnvironment.cs +++ b/Source/Tools/Flax.Build/Build/NativeCpp/CompileEnvironment.cs @@ -56,6 +56,11 @@ namespace Flax.Build.NativeCpp /// public bool Optimization = false; + /// + /// Enables the whole program optimization. + /// + public bool WholeProgramOptimization = false; + /// /// Enables functions level linking support. /// @@ -131,6 +136,7 @@ namespace Flax.Build.NativeCpp RuntimeTypeInfo = RuntimeTypeInfo, Inlining = Inlining, Optimization = Optimization, + WholeProgramOptimization = WholeProgramOptimization, FunctionLevelLinking = FunctionLevelLinking, DebugInformation = DebugInformation, UseDebugCRT = UseDebugCRT, diff --git a/Source/Tools/Flax.Build/Build/Plugins/VisualScriptingPlugin.cs b/Source/Tools/Flax.Build/Build/Plugins/VisualScriptingPlugin.cs index 8ebe13518..ecfd97d88 100644 --- a/Source/Tools/Flax.Build/Build/Plugins/VisualScriptingPlugin.cs +++ b/Source/Tools/Flax.Build/Build/Plugins/VisualScriptingPlugin.cs @@ -22,7 +22,7 @@ namespace Flax.Build.Plugins private void OnGenerateCppScriptWrapperFunction(Builder.BuildData buildData, ClassInfo classInfo, FunctionInfo functionInfo, int scriptVTableSize, int scriptVTableIndex, StringBuilder contents) { - // Generate C++ wrapper function to invoke Visual Script instead of overriden native function (with support for base method callback) + // Generate C++ wrapper function to invoke Visual Script instead of overridden native function (with support for base method callback) BindingsGenerator.CppIncludeFiles.Add("Engine/Content/Assets/VisualScript.h"); diff --git a/Source/Tools/Flax.Build/Build/Target.cs b/Source/Tools/Flax.Build/Build/Target.cs index b7a6b2fbe..239578d51 100644 --- a/Source/Tools/Flax.Build/Build/Target.cs +++ b/Source/Tools/Flax.Build/Build/Target.cs @@ -225,7 +225,7 @@ namespace Flax.Build } /// - /// Setups the target building environment (native C++). Allows to modify compiler and linker options. Options applied here are used by all modules included into this target (can be overriden per module). + /// Setups the target building environment (native C++). Allows to modify compiler and linker options. Options applied here are used by all modules included into this target (can be overridden per module). /// /// The build options. public virtual void SetupTargetEnvironment(BuildOptions options) @@ -256,6 +256,7 @@ namespace Flax.Build options.CompileEnv.IntrinsicFunctions = false; options.CompileEnv.BufferSecurityCheck = true; options.CompileEnv.Inlining = false; + options.CompileEnv.WholeProgramOptimization = false; options.LinkEnv.DebugInformation = true; options.LinkEnv.LinkTimeCodeGeneration = false; @@ -273,11 +274,11 @@ namespace Flax.Build options.CompileEnv.IntrinsicFunctions = true; options.CompileEnv.BufferSecurityCheck = true; options.CompileEnv.Inlining = true; - //options.CompileEnv.WholeProgramOptimization = true; + options.CompileEnv.WholeProgramOptimization = false; options.LinkEnv.DebugInformation = true; - options.LinkEnv.LinkTimeCodeGeneration = true; - options.LinkEnv.UseIncrementalLinking = false; + options.LinkEnv.LinkTimeCodeGeneration = false; + options.LinkEnv.UseIncrementalLinking = true; options.LinkEnv.Optimization = true; break; case TargetConfiguration.Release: @@ -291,7 +292,7 @@ namespace Flax.Build options.CompileEnv.IntrinsicFunctions = true; options.CompileEnv.BufferSecurityCheck = false; options.CompileEnv.Inlining = true; - //options.CompileEnv.WholeProgramOptimization = true; + options.CompileEnv.WholeProgramOptimization = true; options.LinkEnv.DebugInformation = false; options.LinkEnv.LinkTimeCodeGeneration = true; diff --git a/Source/Tools/Flax.Build/Deps/Dependencies/mono.cs b/Source/Tools/Flax.Build/Deps/Dependencies/mono.cs index ca23d81ea..92c9bdec9 100644 --- a/Source/Tools/Flax.Build/Deps/Dependencies/mono.cs +++ b/Source/Tools/Flax.Build/Deps/Dependencies/mono.cs @@ -258,9 +258,8 @@ namespace Flax.Deps.Dependencies "mono_type_normalize", }; - private void BuildMsvc(BuildOptions options, TargetPlatform platform, TargetArchitecture architecture) + private void BuildMsvc(BuildOptions options, TargetPlatform platform, TargetArchitecture architecture, string configuration = "Release") { - var configuration = "Release"; string buildPlatform; switch (architecture) { @@ -491,12 +490,13 @@ namespace Flax.Deps.Dependencies { case TargetPlatform.Windows: { - BuildMsvc(options, platform, TargetArchitecture.x64); + var configuration = "Release"; + BuildMsvc(options, platform, TargetArchitecture.x64, configuration); //BuildBcl(options, platform); // Export header files - Deploy.VCEnvironment.BuildSolution(Path.Combine(root, "msvc", "libmono-dynamic.vcxproj"), "Release", "x64"); - Deploy.VCEnvironment.BuildSolution(Path.Combine(root, "msvc", "build-install.vcxproj"), "Release", "x64"); + Deploy.VCEnvironment.BuildSolution(Path.Combine(root, "msvc", "libmono-dynamic.vcxproj"), configuration, "x64"); + Deploy.VCEnvironment.BuildSolution(Path.Combine(root, "msvc", "build-install.vcxproj"), configuration, "x64"); // Get exported mono methods to forward them in engine module (on Win32 platforms) GetMonoExports(options); diff --git a/Source/Tools/Flax.Build/Platforms/Linux/LinuxPlatform.cs b/Source/Tools/Flax.Build/Platforms/Linux/LinuxPlatform.cs index b64877dc3..f17e6768e 100644 --- a/Source/Tools/Flax.Build/Platforms/Linux/LinuxPlatform.cs +++ b/Source/Tools/Flax.Build/Platforms/Linux/LinuxPlatform.cs @@ -79,7 +79,7 @@ namespace Flax.Build.Platforms { switch (platform) { - case TargetPlatform.Linux: return true; + case TargetPlatform.Linux: return HasRequiredSDKsInstalled; case TargetPlatform.Android: return AndroidSdk.Instance.IsValid && AndroidNdk.Instance.IsValid; default: return false; } diff --git a/Source/Tools/Flax.Build/Platforms/Windows/WindowsPlatformBase.cs b/Source/Tools/Flax.Build/Platforms/Windows/WindowsPlatformBase.cs index 413435a62..b7ddf0605 100644 --- a/Source/Tools/Flax.Build/Platforms/Windows/WindowsPlatformBase.cs +++ b/Source/Tools/Flax.Build/Platforms/Windows/WindowsPlatformBase.cs @@ -314,6 +314,10 @@ namespace Flax.Build.Platforms return _sdks; _sdks = new Dictionary(); + // Skip if running on non-Windows system + if (BuildTargetPlatform != TargetPlatform.Windows) + return _sdks; + // Check Windows 8.1 SDK if (TryReadInstallDirRegistryKey32("Microsoft\\Microsoft SDKs\\Windows\\v8.1", "InstallationFolder", out var sdk81)) { diff --git a/Source/Tools/Flax.Build/Platforms/Windows/WindowsToolchainBase.cs b/Source/Tools/Flax.Build/Platforms/Windows/WindowsToolchainBase.cs index 0917b5027..4d3289a86 100644 --- a/Source/Tools/Flax.Build/Platforms/Windows/WindowsToolchainBase.cs +++ b/Source/Tools/Flax.Build/Platforms/Windows/WindowsToolchainBase.cs @@ -467,8 +467,11 @@ namespace Flax.Build.Platforms // Frame-Pointer Omission commonArgs.Add("/Oy"); - // Whole Program Optimization - commonArgs.Add("/GL"); + if (compileEnvironment.WholeProgramOptimization) + { + // Whole Program Optimization + commonArgs.Add("/GL"); + } } else { @@ -721,7 +724,7 @@ namespace Flax.Build.Platforms args.Add("/PDBALTPATH:%_PDB%"); // Optimize - if (linkEnvironment.Optimization) + if (linkEnvironment.Optimization && !linkEnvironment.UseIncrementalLinking) { // Generate an EXE checksum args.Add("/RELEASE"); diff --git a/Source/Tools/Flax.Stats/CodeFrameNode.cs b/Source/Tools/Flax.Stats/CodeFrameNode.cs index 25d326ff4..e42bdcfef 100644 --- a/Source/Tools/Flax.Stats/CodeFrameNode.cs +++ b/Source/Tools/Flax.Stats/CodeFrameNode.cs @@ -75,14 +75,14 @@ namespace Flax.Stats /// /// Gets total amount of memory used by that node and all child nodes /// - public long TotaSizeOnDisk + public long TotalSizeOnDisk { get { long result = SizeOnDisk; for (int i = 0; i < Children.Length; i++) { - result += Children[i].TotaSizeOnDisk; + result += Children[i].TotalSizeOnDisk; } return result; } @@ -153,15 +153,15 @@ namespace Flax.Stats /// /// Gets total amount of lines of code per language /// - /// Language + /// Language /// Result amount of lines - public long GetTotalLinesOfCode(Languages languge) + public long GetTotalLinesOfCode(Languages language) { long result = 0; - result += LinesOfCode[(int)languge]; + result += LinesOfCode[(int)language]; for (int i = 0; i < Children.Length; i++) { - result += Children[i].GetTotalLinesOfCode(languge); + result += Children[i].GetTotalLinesOfCode(language); } return result; } @@ -270,9 +270,9 @@ namespace Flax.Stats public void CleanupDirectories() { - var chld = Children.ToList(); - chld.RemoveAll(e => ignoredFolders.Contains(e.ShortName.ToLower())); - Children = chld.ToArray(); + var child = Children.ToList(); + child.RemoveAll(e => ignoredFolders.Contains(e.ShortName.ToLower())); + Children = child.ToArray(); foreach (var a in Children) { diff --git a/Source/Tools/Flax.Stats/Tools.cs b/Source/Tools/Flax.Stats/Tools.cs index d947ae758..1e3ba1f74 100644 --- a/Source/Tools/Flax.Stats/Tools.cs +++ b/Source/Tools/Flax.Stats/Tools.cs @@ -103,7 +103,7 @@ namespace Flax.Stats } /// - /// Write string in UTF-8 encoding to the stream and ofset data + /// Write string in UTF-8 encoding to the stream and offset data /// /// File stream /// Data to write @@ -292,7 +292,7 @@ namespace Flax.Stats } /// - /// Write arry of Guids to the stream + /// Write array of Guids to the stream /// /// File stream /// Value to write diff --git a/Source/flax.natvis b/Source/flax.natvis index fb3ef1228..550d8a4d4 100644 --- a/Source/flax.natvis +++ b/Source/flax.natvis @@ -52,7 +52,7 @@ _elementsCount - + _table[i] i++ @@ -72,7 +72,7 @@ _elementsCount - + _table[i] i++ @@ -206,4 +206,13 @@ + + + Null + Type={Module->Types._allocation._data[TypeIndex].Fullname} + + Module->Types._allocation._data[TypeIndex] + + +