diff --git a/Content/Editor/Gizmo/Axis.flax b/Content/Editor/Gizmo/Axis.flax deleted file mode 100644 index c2b0a30f0..000000000 --- a/Content/Editor/Gizmo/Axis.flax +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:f32f2401c2c6eb05d1674f98950f4d9ff0a9508d42fa25413eba3a49a0be9363 -size 11800 diff --git a/Content/Editor/Gizmo/MaterialSphere.flax b/Content/Editor/Gizmo/MaterialSphere.flax new file mode 100644 index 000000000..bf110acfb --- /dev/null +++ b/Content/Editor/Gizmo/MaterialSphere.flax @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bf88f48c5de6b8fb8c7effbbdded9e6f115b1a3ac262d60785987c8f24107dfb +size 567 diff --git a/Content/Editor/Gizmo/RotationAxis.flax b/Content/Editor/Gizmo/RotationAxis.flax new file mode 100644 index 000000000..a743e30a6 --- /dev/null +++ b/Content/Editor/Gizmo/RotationAxis.flax @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8d8f319dcd39842cb991a4e8e67e4823c7159a493a1aec757232077e5d56ad9f +size 41685 diff --git a/Content/Editor/Gizmo/ScaleAxis.flax b/Content/Editor/Gizmo/ScaleAxis.flax index 150b1e1dd..8f774c883 100644 --- a/Content/Editor/Gizmo/ScaleAxis.flax +++ b/Content/Editor/Gizmo/ScaleAxis.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:feb61724fc4fd620de5e3cedf6f74485d38aea4f3a88bf44ecdc186424d14b04 -size 3933 +oid sha256:8743967dc27d5839b88dfa1e599cbc9b290427afd6b65b2424ffa5218b330beb +size 40754 diff --git a/Content/Editor/Gizmo/TranslateAxis.flax b/Content/Editor/Gizmo/TranslateAxis.flax deleted file mode 100644 index a97bbc780..000000000 --- a/Content/Editor/Gizmo/TranslateAxis.flax +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:011c8be02f11017c28b300601b19f12c8750b256d0aabe060c519794c4578d41 -size 7757 diff --git a/Content/Editor/Gizmo/TranslationAxis.flax b/Content/Editor/Gizmo/TranslationAxis.flax new file mode 100644 index 000000000..f91e2b18a --- /dev/null +++ b/Content/Editor/Gizmo/TranslationAxis.flax @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a6096ef866920a861ec91f0660999a9ea07b2fbd26bcf52ba2c0112ba5e02336 +size 8915 diff --git a/Content/Editor/Gizmo/WireCircle.flax b/Content/Editor/Gizmo/WireCircle.flax deleted file mode 100644 index 1378ea4f6..000000000 --- a/Content/Editor/Gizmo/WireCircle.flax +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:901ae5ba301bf22697e4f7ced101f97dfe6ef38771f940c0c38e43125157d080 -size 139269 diff --git a/Content/Shaders/GI/DDGI.flax b/Content/Shaders/GI/DDGI.flax index 6b387f70b..106d6d772 100644 --- a/Content/Shaders/GI/DDGI.flax +++ b/Content/Shaders/GI/DDGI.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d851e919211c073ac83ba5e9e2c75529164c588ea1ace8eca858995643f5744a -size 23686 +oid sha256:74e29de6dbadea44168350c430d7f75719448987bc2f1d0b063dbfb73aaa0780 +size 23690 diff --git a/Content/Shaders/GlobalSignDistanceField.flax b/Content/Shaders/GlobalSignDistanceField.flax index e013618f3..18f653f86 100644 --- a/Content/Shaders/GlobalSignDistanceField.flax +++ b/Content/Shaders/GlobalSignDistanceField.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3e12440d4fcc78a0d7e07da6584efcc95750046db500d0aff2dc9a41360aac36 -size 11798 +oid sha256:2c8aa181a814d69b15ffec6493a71a6f42ae816ce04f7803cff2d5073b4b3c4f +size 11790 diff --git a/Source/Editor/CustomEditors/Editors/TagEditor.cs b/Source/Editor/CustomEditors/Editors/TagEditor.cs index 8a4333a9b..948663892 100644 --- a/Source/Editor/CustomEditors/Editors/TagEditor.cs +++ b/Source/Editor/CustomEditors/Editors/TagEditor.cs @@ -174,9 +174,7 @@ namespace FlaxEditor.CustomEditors.Editors for (int i = 0; i < subInputs.Length; i++) { if (string.IsNullOrEmpty(subInputs[i])) - { continue; - } // Check all entered subtags and create any that dont exist for (int j = 0; j <= i; j++) @@ -296,6 +294,7 @@ namespace FlaxEditor.CustomEditors.Editors settingsObj.Tags.Add(tagName); settingsObj.Tags.Sort(); settingsAsset.SetInstance(settingsObj); + settingsAsset.Save(); // Reload editor window to reflect new tag assetWindow?.RefreshAsset(); @@ -417,7 +416,6 @@ namespace FlaxEditor.CustomEditors.Editors { if (!uniqueText) return; - OnAddTagButtonClicked(nameTextBox.Text, tree, nameTextBox, addTagDropPanel, pickerData); }; diff --git a/Source/Editor/GUI/ContextMenu/ContextMenuBase.cs b/Source/Editor/GUI/ContextMenu/ContextMenuBase.cs index f93e31957..27d47345d 100644 --- a/Source/Editor/GUI/ContextMenu/ContextMenuBase.cs +++ b/Source/Editor/GUI/ContextMenu/ContextMenuBase.cs @@ -1,3 +1,7 @@ +#if PLATFORM_WINDOWS +#define USE_IS_FOREGROUND +#else +#endif // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. using System.Collections.Generic; @@ -326,6 +330,7 @@ namespace FlaxEditor.GUI.ContextMenu // Nothing to do } +#if USE_IS_FOREGROUND /// /// Returns true if context menu is in foreground (eg. context window or any child window has user focus or user opened additional popup within this context). /// @@ -385,6 +390,57 @@ namespace FlaxEditor.GUI.ContextMenu } } } +#else + private void OnWindowGotFocus() + { + } + + private void OnWindowLostFocus() + { + // Skip for parent menus (child should handle lost of focus) + if (_childCM != null) + return; + + if (_parentCM != null) + { + if (IsMouseOver) + return; + + // Check if any external popup is focused + foreach (var externalPopup in ExternalPopups) + { + if (externalPopup && externalPopup.IsFocused) + return; + } + + // Check if mouse is over any of the parents + ContextMenuBase focusCM = null; + var cm = _parentCM; + while (cm != null) + { + if (cm.IsMouseOver) + focusCM = cm; + cm = cm._parentCM; + } + + if (focusCM != null) + { + // Focus on the clicked parent and hide any open sub-menus + focusCM.HideChild(); + focusCM._window?.Focus(); + } + else + { + // User clicked outside the context menus, hide the whole context menu tree + TopmostCM.Hide(); + } + } + else if (!IsMouseOver) + { + Hide(); + } + } +#endif /// public override bool IsMouseOver @@ -405,6 +461,7 @@ namespace FlaxEditor.GUI.ContextMenu } } +#if USE_IS_FOREGROUND /// public override void Update(float deltaTime) { @@ -416,6 +473,7 @@ namespace FlaxEditor.GUI.ContextMenu Hide(); } } +#endif /// public override void Draw() diff --git a/Source/Editor/GUI/ContextMenu/ContextMenuChildMenu.cs b/Source/Editor/GUI/ContextMenu/ContextMenuChildMenu.cs index 0256d2bee..7af36fae0 100644 --- a/Source/Editor/GUI/ContextMenu/ContextMenuChildMenu.cs +++ b/Source/Editor/GUI/ContextMenu/ContextMenuChildMenu.cs @@ -55,8 +55,6 @@ namespace FlaxEditor.GUI.ContextMenu /// public override void OnMouseEnter(Float2 location) { - base.OnMouseEnter(location); - // Skip if has no children if (ContextMenu.HasChildren == false) return; @@ -66,8 +64,28 @@ namespace FlaxEditor.GUI.ContextMenu if (parentContextMenu == ContextMenu) return; + if (ContextMenu.IsOpened) + return; + + base.OnMouseEnter(location); + // Hide parent CM popups and set itself as child parentContextMenu.ShowChild(ContextMenu, PointToParent(ParentContextMenu, new Float2(Width, 0))); } + + /// + public override bool OnMouseUp(Float2 location, MouseButton button) + { + // Skip if already shown + var parentContextMenu = ParentContextMenu; + if (parentContextMenu == ContextMenu) + return true; + if (ContextMenu.IsOpened) + return true; + + // Hide parent CM popups and set itself as child + parentContextMenu.ShowChild(ContextMenu, PointToParent(ParentContextMenu, new Float2(Width, 0))); + return base.OnMouseUp(location, button); + } } } diff --git a/Source/Editor/GUI/Tree/TreeNode.cs b/Source/Editor/GUI/Tree/TreeNode.cs index 54aeedb9a..f2c8e2d6a 100644 --- a/Source/Editor/GUI/Tree/TreeNode.cs +++ b/Source/Editor/GUI/Tree/TreeNode.cs @@ -700,6 +700,8 @@ namespace FlaxEditor.GUI.Tree /// public override bool OnMouseDown(Float2 location, MouseButton button) { + UpdateMouseOverFlags(location); + // Check if mouse hits bar and node isn't a root if (_mouseOverHeader) { @@ -728,6 +730,8 @@ namespace FlaxEditor.GUI.Tree /// public override bool OnMouseUp(Float2 location, MouseButton button) { + UpdateMouseOverFlags(location); + // Clear flag for left button if (button == MouseButton.Left) { @@ -815,21 +819,7 @@ namespace FlaxEditor.GUI.Tree /// public override void OnMouseMove(Float2 location) { - // Cache flags - _mouseOverArrow = HasAnyVisibleChild && ArrowRect.Contains(location); - _mouseOverHeader = new Rectangle(0, 0, Width, _headerHeight - 1).Contains(location); - if (_mouseOverHeader) - { - // Allow non-scrollable controls to stay on top of the header and override the mouse behaviour - for (int i = 0; i < Children.Count; i++) - { - if (!Children[i].IsScrollable && IntersectsChildContent(Children[i], location, out _)) - { - _mouseOverHeader = false; - break; - } - } - } + UpdateMouseOverFlags(location); // Check if start drag and drop if (_isMouseDown && Float2.Distance(_mouseDownPos, location) > 10.0f) @@ -852,6 +842,25 @@ namespace FlaxEditor.GUI.Tree } } + private void UpdateMouseOverFlags(Vector2 location) + { + // Cache flags + _mouseOverArrow = HasAnyVisibleChild && ArrowRect.Contains(location); + _mouseOverHeader = new Rectangle(0, 0, Width, _headerHeight - 1).Contains(location); + if (_mouseOverHeader) + { + // Allow non-scrollable controls to stay on top of the header and override the mouse behaviour + for (int i = 0; i < Children.Count; i++) + { + if (!Children[i].IsScrollable && IntersectsChildContent(Children[i], location, out _)) + { + _mouseOverHeader = false; + break; + } + } + } + } + /// public override void OnMouseLeave() { diff --git a/Source/Editor/Gizmo/TransformGizmoBase.Draw.cs b/Source/Editor/Gizmo/TransformGizmoBase.Draw.cs index 77a9cb017..0d421a17e 100644 --- a/Source/Editor/Gizmo/TransformGizmoBase.Draw.cs +++ b/Source/Editor/Gizmo/TransformGizmoBase.Draw.cs @@ -6,44 +6,48 @@ namespace FlaxEditor.Gizmo { public partial class TransformGizmoBase { - private Model _modelTranslateAxis; + // Models + private Model _modelTranslationAxis; private Model _modelScaleAxis; - private Model _modelBox; - private Model _modelCircle; + private Model _modelRotationAxis; + private Model _modelSphere; + private Model _modelCube; + + // Materials private MaterialInstance _materialAxisX; private MaterialInstance _materialAxisY; private MaterialInstance _materialAxisZ; private MaterialInstance _materialAxisFocus; - private MaterialBase _materialWire; - private MaterialBase _materialWireFocus; + private MaterialBase _materialSphere; private void InitDrawing() { - // Load content (but async - don't wait and don't block execution) - _modelTranslateAxis = FlaxEngine.Content.LoadAsyncInternal("Editor/Gizmo/TranslateAxis"); + // Axis Models + _modelTranslationAxis = FlaxEngine.Content.LoadAsyncInternal("Editor/Gizmo/TranslationAxis"); _modelScaleAxis = FlaxEngine.Content.LoadAsyncInternal("Editor/Gizmo/ScaleAxis"); - _modelBox = FlaxEngine.Content.LoadAsyncInternal("Editor/Gizmo/WireBox"); - _modelCircle = FlaxEngine.Content.LoadAsyncInternal("Editor/Gizmo/WireCircle"); + _modelRotationAxis = FlaxEngine.Content.LoadAsyncInternal("Editor/Gizmo/RotationAxis"); + _modelSphere = FlaxEngine.Content.LoadAsyncInternal("Editor/Primitives/Sphere"); + _modelCube = FlaxEngine.Content.LoadAsyncInternal("Editor/Primitives/Cube"); + + // Axis Materials _materialAxisX = FlaxEngine.Content.LoadAsyncInternal("Editor/Gizmo/MaterialAxisX"); _materialAxisY = FlaxEngine.Content.LoadAsyncInternal("Editor/Gizmo/MaterialAxisY"); _materialAxisZ = FlaxEngine.Content.LoadAsyncInternal("Editor/Gizmo/MaterialAxisZ"); _materialAxisFocus = FlaxEngine.Content.LoadAsyncInternal("Editor/Gizmo/MaterialAxisFocus"); - _materialWire = FlaxEngine.Content.LoadAsyncInternal("Editor/Gizmo/MaterialWire"); - _materialWireFocus = FlaxEngine.Content.LoadAsyncInternal("Editor/Gizmo/MaterialWireFocus"); + _materialSphere = FlaxEngine.Content.LoadAsyncInternal("Editor/Gizmo/MaterialSphere"); // Ensure that every asset was loaded - if (_modelTranslateAxis == null || + if (_modelTranslationAxis == null || _modelScaleAxis == null || - _modelBox == null || - _modelCircle == null || + _modelRotationAxis == null || + _modelSphere == null || + _modelCube == null || _materialAxisX == null || _materialAxisY == null || _materialAxisZ == null || _materialAxisFocus == null || - _materialWire == null || - _materialWireFocus == null) + _materialSphere == null) { - // Error Platform.Fatal("Failed to load transform gizmo resources."); } } @@ -58,6 +62,8 @@ namespace FlaxEditor.Gizmo // https://github.com/FlaxEngine/FlaxEngine/issues/680 Matrix m1, m2, m3, mx1; + float boxScale = 300f; + float boxSize = 0.085f; bool isXAxis = _activeAxis == Axis.X || _activeAxis == Axis.XY || _activeAxis == Axis.ZX; bool isYAxis = _activeAxis == Axis.Y || _activeAxis == Axis.XY || _activeAxis == Axis.YZ; bool isZAxis = _activeAxis == Axis.Z || _activeAxis == Axis.YZ || _activeAxis == Axis.ZX; @@ -65,114 +71,137 @@ namespace FlaxEditor.Gizmo renderContext.View.GetWorldMatrix(ref _gizmoWorld, out Matrix world); const float gizmoModelsScale2RealGizmoSize = 0.075f; + Mesh sphereMesh, cubeMesh; switch (_activeMode) { case Mode.Translate: { - if (!_modelTranslateAxis || !_modelTranslateAxis.IsLoaded || !_modelBox || !_modelBox.IsLoaded) + if (!_modelTranslationAxis || !_modelTranslationAxis.IsLoaded || !_modelCube || !_modelCube.IsLoaded || !_modelSphere || !_modelSphere.IsLoaded) break; + var transAxisMesh = _modelTranslationAxis.LODs[0].Meshes[0]; + cubeMesh = _modelCube.LODs[0].Meshes[0]; + sphereMesh = _modelSphere.LODs[0].Meshes[0]; Matrix.Scaling(gizmoModelsScale2RealGizmoSize, out m3); Matrix.Multiply(ref m3, ref world, out m1); mx1 = m1; mx1.M41 += 0.05f; - var axisMesh = _modelTranslateAxis.LODs[0].Meshes[0]; - var boxMesh = _modelBox.LODs[0].Meshes[0]; - var boxSize = 10.0f; - - // XY plane - m2 = Matrix.Transformation(new Vector3(boxSize, 1.0f, boxSize), Quaternion.RotationX(Mathf.PiOverTwo), new Vector3(boxSize * 0.5f, boxSize * 0.5f, 0.0f)); - Matrix.Multiply(ref m2, ref m1, out m3); - boxMesh.Draw(ref renderContext, _activeAxis == Axis.XY ? _materialWireFocus : _materialWire, ref m3); - - // ZX plane - m2 = Matrix.Transformation(new Vector3(boxSize, 1.0f, boxSize), Quaternion.Identity, new Vector3(boxSize * 0.5f, 0.0f, boxSize * 0.5f)); - Matrix.Multiply(ref m2, ref m1, out m3); - boxMesh.Draw(ref renderContext, _activeAxis == Axis.ZX ? _materialWireFocus : _materialWire, ref m3); - - // YZ plane - m2 = Matrix.Transformation(new Vector3(boxSize, 1.0f, boxSize), Quaternion.RotationZ(Mathf.PiOverTwo), new Vector3(0.0f, boxSize * 0.5f, boxSize * 0.5f)); - Matrix.Multiply(ref m2, ref m1, out m3); - boxMesh.Draw(ref renderContext, _activeAxis == Axis.YZ ? _materialWireFocus : _materialWire, ref m3); // X axis - axisMesh.Draw(ref renderContext, isXAxis ? _materialAxisFocus : _materialAxisX, ref mx1); - - // Y axis - Matrix.RotationZ(Mathf.PiOverTwo, out m2); - Matrix.Multiply(ref m2, ref m1, out m3); - axisMesh.Draw(ref renderContext, isYAxis ? _materialAxisFocus : _materialAxisY, ref m3); - - // Z axis Matrix.RotationY(-Mathf.PiOverTwo, out m2); Matrix.Multiply(ref m2, ref m1, out m3); - axisMesh.Draw(ref renderContext, isZAxis ? _materialAxisFocus : _materialAxisZ, ref m3); + transAxisMesh.Draw(ref renderContext, isXAxis ? _materialAxisFocus : _materialAxisX, ref m3); + + // Y axis + Matrix.RotationX(Mathf.PiOverTwo, out m2); + Matrix.Multiply(ref m2, ref m1, out m3); + transAxisMesh.Draw(ref renderContext, isYAxis ? _materialAxisFocus : _materialAxisY, ref m3); + + // Z axis + Matrix.RotationX(Mathf.Pi, out m2); + Matrix.Multiply(ref m2, ref m1, out m3); + transAxisMesh.Draw(ref renderContext, isZAxis ? _materialAxisFocus : _materialAxisZ, ref m3); + + // XY plane + m2 = Matrix.Transformation(new Vector3(boxSize, boxSize * 0.1f, boxSize), Quaternion.RotationX(Mathf.PiOverTwo), new Vector3(boxSize * boxScale, boxSize * boxScale, 0.0f)); + Matrix.Multiply(ref m2, ref m1, out m3); + cubeMesh.Draw(ref renderContext, _activeAxis == Axis.XY ? _materialAxisFocus : _materialAxisX, ref m3); + + // ZX plane + m2 = Matrix.Transformation(new Vector3(boxSize, boxSize * 0.1f, boxSize), Quaternion.Identity, new Vector3(boxSize * boxScale, 0.0f, boxSize * boxScale)); + Matrix.Multiply(ref m2, ref m1, out m3); + cubeMesh.Draw(ref renderContext, _activeAxis == Axis.ZX ? _materialAxisFocus : _materialAxisZ, ref m3); + + // YZ plane + m2 = Matrix.Transformation(new Vector3(boxSize, boxSize * 0.1f, boxSize), Quaternion.RotationZ(Mathf.PiOverTwo), new Vector3(0.0f, boxSize * boxScale, boxSize * boxScale)); + Matrix.Multiply(ref m2, ref m1, out m3); + cubeMesh.Draw(ref renderContext, _activeAxis == Axis.YZ ? _materialAxisFocus : _materialAxisY, ref m3); + + // Center sphere + Matrix.Scaling(gizmoModelsScale2RealGizmoSize, out m2); + Matrix.Multiply(ref m2, ref m1, out m3); + sphereMesh.Draw(ref renderContext, isCenter ? _materialAxisFocus : _materialSphere, ref m3); break; } case Mode.Rotate: { - if (!_modelCircle || !_modelCircle.IsLoaded || !_modelBox || !_modelBox.IsLoaded) + if (!_modelRotationAxis || !_modelRotationAxis.IsLoaded || !_modelSphere || !_modelSphere.IsLoaded) break; - var circleMesh = _modelCircle.LODs[0].Meshes[0]; - var boxMesh = _modelBox.LODs[0].Meshes[0]; - Matrix.Scaling(8.0f, out m3); + var rotationAxisMesh = _modelRotationAxis.LODs[0].Meshes[0]; + sphereMesh = _modelSphere.LODs[0].Meshes[0]; + Matrix.Scaling(gizmoModelsScale2RealGizmoSize, out m3); Matrix.Multiply(ref m3, ref world, out m1); + mx1 = m1; + mx1.M41 += 0.05f; // X axis Matrix.RotationZ(Mathf.PiOverTwo, out m2); Matrix.Multiply(ref m2, ref m1, out m3); - circleMesh.Draw(ref renderContext, isXAxis ? _materialAxisFocus : _materialAxisX, ref m3); + rotationAxisMesh.Draw(ref renderContext, isXAxis ? _materialAxisFocus : _materialAxisX, ref m3); // Y axis - circleMesh.Draw(ref renderContext, isYAxis ? _materialAxisFocus : _materialAxisY, ref m1); + rotationAxisMesh.Draw(ref renderContext, isYAxis ? _materialAxisFocus : _materialAxisY, ref m1); // Z axis Matrix.RotationX(-Mathf.PiOverTwo, out m2); Matrix.Multiply(ref m2, ref m1, out m3); - circleMesh.Draw(ref renderContext, isZAxis ? _materialAxisFocus : _materialAxisZ, ref m3); + rotationAxisMesh.Draw(ref renderContext, isZAxis ? _materialAxisFocus : _materialAxisZ, ref m3); // Center box - Matrix.Scaling(gizmoModelsScale2RealGizmoSize, out m3); - Matrix.Multiply(ref m3, ref world, out m1); - Matrix.Scaling(1.0f, out m2); + Matrix.Scaling(gizmoModelsScale2RealGizmoSize, out m2); Matrix.Multiply(ref m2, ref m1, out m3); - boxMesh.Draw(ref renderContext, isCenter ? _materialWireFocus : _materialWire, ref m3); + sphereMesh.Draw(ref renderContext, isCenter ? _materialAxisFocus : _materialSphere, ref m3); break; } case Mode.Scale: { - if (!_modelScaleAxis || !_modelScaleAxis.IsLoaded || !_modelBox || !_modelBox.IsLoaded) + if (!_modelScaleAxis || !_modelScaleAxis.IsLoaded || !_modelCube || !_modelCube.IsLoaded || !_modelSphere || !_modelSphere.IsLoaded) break; + var scaleAxisMesh = _modelScaleAxis.LODs[0].Meshes[0]; + cubeMesh = _modelCube.LODs[0].Meshes[0]; + sphereMesh = _modelSphere.LODs[0].Meshes[0]; Matrix.Scaling(gizmoModelsScale2RealGizmoSize, out m3); Matrix.Multiply(ref m3, ref world, out m1); mx1 = m1; mx1.M41 -= 0.05f; - var axisMesh = _modelScaleAxis.LODs[0].Meshes[0]; - var boxMesh = _modelBox.LODs[0].Meshes[0]; - // X axis Matrix.RotationY(-Mathf.PiOverTwo, out m2); Matrix.Multiply(ref m2, ref mx1, out m3); - axisMesh.Draw(ref renderContext, isXAxis ? _materialAxisFocus : _materialAxisX, ref m3); + scaleAxisMesh.Draw(ref renderContext, isXAxis ? _materialAxisFocus : _materialAxisX, ref m3); // Y axis Matrix.RotationX(Mathf.PiOverTwo, out m2); Matrix.Multiply(ref m2, ref m1, out m3); - axisMesh.Draw(ref renderContext, isYAxis ? _materialAxisFocus : _materialAxisY, ref m3); + scaleAxisMesh.Draw(ref renderContext, isYAxis ? _materialAxisFocus : _materialAxisY, ref m3); // Z axis Matrix.RotationX(Mathf.Pi, out m2); Matrix.Multiply(ref m2, ref m1, out m3); - axisMesh.Draw(ref renderContext, isZAxis ? _materialAxisFocus : _materialAxisZ, ref m3); + scaleAxisMesh.Draw(ref renderContext, isZAxis ? _materialAxisFocus : _materialAxisZ, ref m3); + + // XY plane + m2 = Matrix.Transformation(new Vector3(boxSize, boxSize * 0.1f, boxSize), Quaternion.RotationX(Mathf.PiOverTwo), new Vector3(boxSize * boxScale, boxSize * boxScale, 0.0f)); + Matrix.Multiply(ref m2, ref m1, out m3); + cubeMesh.Draw(ref renderContext, _activeAxis == Axis.XY ? _materialAxisFocus : _materialAxisX, ref m3); + + // ZX plane + m2 = Matrix.Transformation(new Vector3(boxSize, boxSize * 0.1f, boxSize), Quaternion.Identity, new Vector3(boxSize * boxScale, 0.0f, boxSize * boxScale)); + Matrix.Multiply(ref m2, ref m1, out m3); + cubeMesh.Draw(ref renderContext, _activeAxis == Axis.ZX ? _materialAxisFocus : _materialAxisZ, ref m3); + + // YZ plane + m2 = Matrix.Transformation(new Vector3(boxSize, boxSize * 0.1f, boxSize), Quaternion.RotationZ(Mathf.PiOverTwo), new Vector3(0.0f, boxSize * boxScale, boxSize * boxScale)); + Matrix.Multiply(ref m2, ref m1, out m3); + cubeMesh.Draw(ref renderContext, _activeAxis == Axis.YZ ? _materialAxisFocus : _materialAxisY, ref m3); // Center box - Matrix.Scaling(10.0f, out m2); + Matrix.Scaling(gizmoModelsScale2RealGizmoSize, out m2); Matrix.Multiply(ref m2, ref m1, out m3); - boxMesh.Draw(ref renderContext, isCenter ? _materialWireFocus : _materialWire, ref m3); + sphereMesh.Draw(ref renderContext, isCenter ? _materialAxisFocus : _materialSphere, ref m3); break; } diff --git a/Source/Editor/Gizmo/TransformGizmoBase.Selection.cs b/Source/Editor/Gizmo/TransformGizmoBase.Selection.cs index 513178f40..86c0a6afe 100644 --- a/Source/Editor/Gizmo/TransformGizmoBase.Selection.cs +++ b/Source/Editor/Gizmo/TransformGizmoBase.Selection.cs @@ -102,9 +102,15 @@ namespace FlaxEditor.Gizmo closestIntersection = intersection; } + /*// Center + if (CenterBoxRaw.Intersects(ref localRay, out intersection) && intersection > closestIntersection) + { + _activeAxis = Axis.Center; + closestIntersection = intersection; + }*/ + break; } - case Mode.Rotate: { // Circles @@ -124,41 +130,53 @@ namespace FlaxEditor.Gizmo closestIntersection = intersection; } - // Center - /*if (CenterSphere.Intersects(ref ray, out intersection) && intersection < closestintersection) - { - _activeAxis = Axis.Center; - closestintersection = intersection; - }*/ - break; } - case Mode.Scale: { - // Spheres collision - if (ScaleXSphere.Intersects(ref ray, out intersection) && intersection < closestIntersection) + // Boxes collision + if (XAxisBox.Intersects(ref localRay, out intersection) && intersection < closestIntersection) { _activeAxis = Axis.X; closestIntersection = intersection; } - if (ScaleYSphere.Intersects(ref ray, out intersection) && intersection < closestIntersection) + if (YAxisBox.Intersects(ref localRay, out intersection) && intersection < closestIntersection) { _activeAxis = Axis.Y; closestIntersection = intersection; } - if (ScaleZSphere.Intersects(ref ray, out intersection) && intersection < closestIntersection) + if (ZAxisBox.Intersects(ref localRay, out intersection) && intersection < closestIntersection) { _activeAxis = Axis.Z; closestIntersection = intersection; } - // Center - if (CenterBox.Intersects(ref ray, out intersection) && intersection < closestIntersection) + // Quad planes collision + if (closestIntersection >= float.MaxValue) + closestIntersection = float.MinValue; + + if (XYBox.Intersects(ref localRay, out intersection) && intersection > closestIntersection) + { + _activeAxis = Axis.XY; + closestIntersection = intersection; + } + if (XZBox.Intersects(ref localRay, out intersection) && intersection > closestIntersection) + { + _activeAxis = Axis.ZX; + closestIntersection = intersection; + } + if (YZBox.Intersects(ref localRay, out intersection) && intersection > closestIntersection) + { + _activeAxis = Axis.YZ; + closestIntersection = intersection; + } + + /*// Center + if (CenterBoxRaw.Intersects(ref localRay, out intersection) && intersection < closestIntersection) { _activeAxis = Axis.Center; closestIntersection = intersection; - } + }*/ break; } diff --git a/Source/Editor/Gizmo/TransformGizmoBase.Settings.cs b/Source/Editor/Gizmo/TransformGizmoBase.Settings.cs index ec9423475..8415beac3 100644 --- a/Source/Editor/Gizmo/TransformGizmoBase.Settings.cs +++ b/Source/Editor/Gizmo/TransformGizmoBase.Settings.cs @@ -7,56 +7,53 @@ namespace FlaxEditor.Gizmo { public partial class TransformGizmoBase { - private const float GizmoScaleFactor = 18; - private const float LineLength = 3.0f; - private const float LineOffset = 1.0f; - private const float MultiAxisThickness = 0.05f; - private const float SingleAxisThickness = 0.3f; - private const float ScaleSpheresRadius = 0.7f; - private const float CenterBoxSize = 0.8f; - private const float CenterSphereRadius = 0.1f; - private const float HalfLineOffset = LineOffset / 2; + /// + /// Scale of the gizmo itself + /// + private const float GizmoScaleFactor = 24; - private readonly Vector3[] _translationLineVertices = - { - // -- X Axis -- // index 0 - 5 - new Vector3(HalfLineOffset, 0, 0), - new Vector3(LineLength, 0, 0), - new Vector3(LineOffset, 0, 0), - new Vector3(LineOffset, LineOffset, 0), - new Vector3(LineOffset, 0, 0), - new Vector3(LineOffset, 0, LineOffset), + /// + /// The length of each axis (outwards) + /// + private const float AxisLength = 3.5f; - // -- Y Axis -- // index 6 - 11 - new Vector3(0, HalfLineOffset, 0), - new Vector3(0, LineLength, 0), - new Vector3(0, LineOffset, 0), - new Vector3(LineOffset, LineOffset, 0), - new Vector3(0, LineOffset, 0), - new Vector3(0, LineOffset, LineOffset), + /// + /// Offset to move axis away from center + /// + private const float AxisOffset = 0.8f; - // -- Z Axis -- // index 12 - 17 - new Vector3(0, 0, HalfLineOffset), - new Vector3(0, 0, LineLength), - new Vector3(0, 0, LineOffset), - new Vector3(LineOffset, 0, LineOffset), - new Vector3(0, 0, LineOffset), - new Vector3(0, LineOffset, LineOffset) - }; + /// + /// How thick the axis should be + /// + private const float AxisThickness = 0.3f; - private BoundingBox XAxisBox = new BoundingBox(new Vector3(LineOffset, -SingleAxisThickness, -SingleAxisThickness), new Vector3(LineOffset + LineLength, SingleAxisThickness, SingleAxisThickness)); - private BoundingBox YAxisBox = new BoundingBox(new Vector3(-SingleAxisThickness, LineOffset, -SingleAxisThickness), new Vector3(SingleAxisThickness, LineOffset + LineLength, SingleAxisThickness)); - private BoundingBox ZAxisBox = new BoundingBox(new Vector3(-SingleAxisThickness, -SingleAxisThickness, LineOffset), new Vector3(SingleAxisThickness, SingleAxisThickness, LineOffset + LineLength)); - private BoundingBox XZBox = new BoundingBox(Vector3.Zero, new Vector3(LineOffset, MultiAxisThickness, LineOffset)); - private BoundingBox XYBox = new BoundingBox(Vector3.Zero, new Vector3(LineOffset, LineOffset, MultiAxisThickness)); - private BoundingBox YZBox = new BoundingBox(Vector3.Zero, new Vector3(MultiAxisThickness, LineOffset, LineOffset)); - private BoundingBox CenterBoxRaw = new BoundingBox(new Vector3(-0.5f * CenterBoxSize), new Vector3(0.5f * CenterBoxSize)); - private float RotateRadiusRaw = 4.0f; + /// + /// Center box scale + /// + private const float CenterBoxScale = 0.8f; - private BoundingSphere ScaleXSphere => new BoundingSphere(Vector3.Transform(_translationLineVertices[1], _gizmoWorld), ScaleSpheresRadius * _screenScale); - private BoundingSphere ScaleYSphere => new BoundingSphere(Vector3.Transform(_translationLineVertices[7], _gizmoWorld), ScaleSpheresRadius * _screenScale); - private BoundingSphere ScaleZSphere => new BoundingSphere(Vector3.Transform(_translationLineVertices[13], _gizmoWorld), ScaleSpheresRadius * _screenScale); + /// + /// The inner minimum of the multiscale + /// + private const float InnerExtend = AxisOffset + 0.5f; + + /// + /// The outer maximum of the multiscale + /// + private const float OuterExtend = AxisOffset * 3.5f; + + // Cube with the size AxisThickness, then moves it along the axis (AxisThickness) and finally makes it really long (AxisLength) + private BoundingBox XAxisBox = new BoundingBox(new Vector3(-AxisThickness), new Vector3(AxisThickness)).MakeOffsetted(AxisOffset * Vector3.UnitX).Merge(AxisLength * Vector3.UnitX); + private BoundingBox YAxisBox = new BoundingBox(new Vector3(-AxisThickness), new Vector3(AxisThickness)).MakeOffsetted(AxisOffset * Vector3.UnitY).Merge(AxisLength * Vector3.UnitY); + private BoundingBox ZAxisBox = new BoundingBox(new Vector3(-AxisThickness), new Vector3(AxisThickness)).MakeOffsetted(AxisOffset * Vector3.UnitZ).Merge(AxisLength * Vector3.UnitZ); + + private BoundingBox XZBox = new BoundingBox(new Vector3(InnerExtend, 0, InnerExtend), new Vector3(OuterExtend, 0, OuterExtend)); + private BoundingBox XYBox = new BoundingBox(new Vector3(InnerExtend, InnerExtend, 0), new Vector3(OuterExtend, OuterExtend, 0)); + private BoundingBox YZBox = new BoundingBox(new Vector3(0, InnerExtend, InnerExtend), new Vector3(0, OuterExtend, OuterExtend)); + + private BoundingBox CenterBoxRaw = new BoundingBox(new Vector3(-0.5f * CenterBoxScale), new Vector3(0.5f * CenterBoxScale)); private OrientedBoundingBox CenterBox => new OrientedBoundingBox(CenterBoxRaw) * _gizmoWorld; + private const float RotateRadiusRaw = 4.0f; private Mode _activeMode = Mode.Translate; private Axis _activeAxis = Axis.None; diff --git a/Source/Editor/Scripting/CodeEditors/RiderCodeEditor.cpp b/Source/Editor/Scripting/CodeEditors/RiderCodeEditor.cpp index 8ad95eb0b..d887d7861 100644 --- a/Source/Editor/Scripting/CodeEditors/RiderCodeEditor.cpp +++ b/Source/Editor/Scripting/CodeEditors/RiderCodeEditor.cpp @@ -36,7 +36,7 @@ namespace // Load product info Array productInfoData; const String productInfoPath = directory / TEXT("product-info.json"); - if (File::ReadAllBytes(productInfoPath, productInfoData)) + if (!FileSystem::FileExists(productInfoPath) || File::ReadAllBytes(productInfoPath, productInfoData)) return; rapidjson_flax::Document document; document.Parse((char*)productInfoData.Get(), productInfoData.Count()); @@ -193,6 +193,7 @@ void RiderCodeEditor::FindEditors(Array* output) // TODO: detect Snap installations // TODO: detect by reading the jetbrains-rider.desktop file from ~/.local/share/applications and /usr/share/applications? + SearchDirectory(&installations, TEXT("/usr/share/rider/")); FileSystem::GetChildDirectories(subDirectories, TEXT("/usr/share/rider")); // Default suggested location for standalone installations diff --git a/Source/Editor/Utilities/EditorUtilities.cpp b/Source/Editor/Utilities/EditorUtilities.cpp index 7e7aad25c..3fba14859 100644 --- a/Source/Editor/Utilities/EditorUtilities.cpp +++ b/Source/Editor/Utilities/EditorUtilities.cpp @@ -543,19 +543,22 @@ bool EditorUtilities::GetTexture(const Guid& textureId, TextureData& textureData } else { - // TODO: disable streaming for a texture or set max quality override - int32 waits = 1000; - const auto targetResidency = texture->StreamingTexture()->GetMaxResidency(); - ASSERT(targetResidency > 0); - while (targetResidency != texture->StreamingTexture()->GetCurrentResidency() && waits-- > 0) + const bool useGPU = texture->IsVirtual(); + if (useGPU) { - Platform::Sleep(10); + int32 waits = 1000; + const auto targetResidency = texture->StreamingTexture()->GetMaxResidency(); + ASSERT(targetResidency > 0); + while (targetResidency != texture->StreamingTexture()->GetCurrentResidency() && waits-- > 0) + { + Platform::Sleep(10); + } + + // Get texture data from GPU + if (!texture->GetTexture()->DownloadData(textureData)) + return false; } - // Get texture data from GPU - if (!texture->GetTexture()->DownloadData(textureData)) - return false; - // Get texture data from asset if (!texture->GetTextureData(textureData)) return false; diff --git a/Source/Engine/Content/JsonAsset.cpp b/Source/Engine/Content/JsonAsset.cpp index 8dfc01438..5811f1a15 100644 --- a/Source/Engine/Content/JsonAsset.cpp +++ b/Source/Engine/Content/JsonAsset.cpp @@ -37,12 +37,8 @@ String JsonAssetBase::GetData() const if (Data == nullptr) return String::Empty; PROFILE_CPU_NAMED("JsonAsset.GetData"); - - // Get serialized data rapidjson_flax::StringBuffer buffer; - rapidjson_flax::Writer writer(buffer); - Data->Accept(writer); - + OnGetData(buffer); return String((const char*)buffer.GetString(), (int32)buffer.GetSize()); } @@ -83,6 +79,12 @@ bool JsonAssetBase::Init(const StringView& dataTypeName, const StringAnsiView& d return loadAsset() != LoadResult::Ok; } +void JsonAssetBase::OnGetData(rapidjson_flax::StringBuffer& buffer) const +{ + PrettyJsonWriter writerObj(buffer); + Data->Accept(writerObj.GetWriter()); +} + const String& JsonAssetBase::GetPath() const { #if USE_EDITOR @@ -144,6 +146,53 @@ void JsonAssetBase::GetReferences(const StringAnsiView& json, Array& outpu FindIds(document, output); } +bool JsonAssetBase::Save(const StringView& path) +{ + // Validate state + if (WaitForLoaded()) + { + LOG(Error, "Asset loading failed. Cannot save it."); + return true; + } + if (IsVirtual() && path.IsEmpty()) + { + LOG(Error, "To save virtual asset asset you need to specify the target asset path location."); + return true; + } + ScopeLock lock(Locker); + + // Serialize to json file + rapidjson_flax::StringBuffer buffer; + PrettyJsonWriter writerObj(buffer); + JsonWriter& writer = writerObj; + writer.StartObject(); + { + // Json resource header + writer.JKEY("ID"); + writer.Guid(GetID()); + writer.JKEY("TypeName"); + writer.String(DataTypeName); + writer.JKEY("EngineBuild"); + writer.Int(FLAXENGINE_VERSION_BUILD); + + // Json resource data + rapidjson_flax::StringBuffer dataBuffer; + OnGetData(dataBuffer); + writer.JKEY("Data"); + writer.RawValue(dataBuffer.GetString(), (int32)dataBuffer.GetSize()); + } + writer.EndObject(); + + // Save json to file + if (File::WriteAllBytes(path.HasChars() ? path : StringView(GetPath()), (byte*)buffer.GetString(), (int32)buffer.GetSize())) + { + LOG(Error, "Cannot save \'{0}\'", ToString()); + return true; + } + + return false; +} + void JsonAssetBase::GetReferences(Array& output) const { if (Data == nullptr) diff --git a/Source/Engine/Content/JsonAsset.h b/Source/Engine/Content/JsonAsset.h index 9fca675ee..ca52ac86b 100644 --- a/Source/Engine/Content/JsonAsset.h +++ b/Source/Engine/Content/JsonAsset.h @@ -72,8 +72,19 @@ public: /// The Json string. /// The output list of object IDs references by the asset (appended, not cleared). API_FUNCTION() static void GetReferences(const StringAnsiView& json, API_PARAM(Out) Array& output); + + /// + /// Saves this asset to the file. Supported only in Editor. + /// + /// The custom asset path to use for the saving. Use empty value to save this asset to its own storage location. Can be used to duplicate asset. Must be specified when saving virtual asset. + /// True if cannot save data, otherwise false. + API_FUNCTION() bool Save(const StringView& path = StringView::Empty); #endif +protected: + // Gets the serialized Json data (from runtime state). + virtual void OnGetData(rapidjson_flax::StringBuffer& buffer) const; + public: // [Asset] const String& GetPath() const override; diff --git a/Source/Engine/Content/Storage/FlaxStorage.cpp b/Source/Engine/Content/Storage/FlaxStorage.cpp index fd964daaf..8608a34f6 100644 --- a/Source/Engine/Content/Storage/FlaxStorage.cpp +++ b/Source/Engine/Content/Storage/FlaxStorage.cpp @@ -560,12 +560,8 @@ bool FlaxStorage::Load() bool FlaxStorage::Reload() { - // Check if wasn't already loaded if (!IsLoaded()) - { - LOG(Warning, "{0} isn't loaded.", ToString()); return false; - } OnReloading(this); diff --git a/Source/Engine/Core/Config/GameSettings.cpp b/Source/Engine/Core/Config/GameSettings.cpp index 4d135ebb5..c64fc0d4e 100644 --- a/Source/Engine/Core/Config/GameSettings.cpp +++ b/Source/Engine/Core/Config/GameSettings.cpp @@ -22,7 +22,7 @@ #include "Engine/Engine/Globals.h" #include "Engine/Profiler/ProfilerCPU.h" #include "Engine/Streaming/StreamingSettings.h" -#if FLAX_TESTS +#if FLAX_TESTS || USE_EDITOR #include "Engine/Platform/FileSystem.h" #endif @@ -91,6 +91,19 @@ GameSettings* GameSettings::Get() // Silence missing GameSettings during test run before Editor creates it (not important) if (!FileSystem::FileExists(assetPath)) return nullptr; +#endif +#if USE_EDITOR + // Log once missing GameSettings in Editor + if (!FileSystem::FileExists(assetPath)) + { + static bool LogOnce = true; + if (LogOnce) + { + LogOnce = false; + LOG(Error, "Missing file game settings asset ({0})", assetPath); + } + return nullptr; + } #endif GameSettingsAsset = Content::LoadAsync(assetPath); if (GameSettingsAsset == nullptr) diff --git a/Source/Engine/Core/Delegate.h b/Source/Engine/Core/Delegate.h index ffb78dfeb..21a4374ea 100644 --- a/Source/Engine/Core/Delegate.h +++ b/Source/Engine/Core/Delegate.h @@ -617,12 +617,11 @@ public: FunctionType* bindings = (FunctionType*)Platform::AtomicRead((intptr volatile*)&_ptr); for (intptr i = 0; i < size; i++) { - auto function = (StubSignature)Platform::AtomicRead((intptr volatile*)&bindings[i]._function); - if (function != nullptr) - { - auto callee = (void*)Platform::AtomicRead((intptr volatile*)&bindings[i]._callee); + auto function = (StubSignature)Platform::AtomicRead((intptr volatile*)&bindings->_function); + auto callee = (void*)Platform::AtomicRead((intptr volatile*)&bindings->_callee); + if (function != nullptr && function == (StubSignature)Platform::AtomicRead((intptr volatile*)&bindings->_function)) function(callee, Forward(params)...); - } + ++bindings; } } }; diff --git a/Source/Engine/Core/Math/BoundingBox.cs b/Source/Engine/Core/Math/BoundingBox.cs index da3a2c863..51225d775 100644 --- a/Source/Engine/Core/Math/BoundingBox.cs +++ b/Source/Engine/Core/Math/BoundingBox.cs @@ -419,6 +419,31 @@ namespace FlaxEngine return box; } + /// + /// Constructs a that is as large as the box and point. + /// + /// The box to merge. + /// The point to merge. + /// When the method completes, contains the newly constructed bounding box. + public static void Merge(ref BoundingBox value1, ref Vector3 value2, out BoundingBox result) + { + Vector3.Min(ref value1.Minimum, ref value2, out result.Minimum); + Vector3.Max(ref value1.Maximum, ref value2, out result.Maximum); + } + + /// + /// Constructs a that is as large as the box and point. + /// + /// The point to merge. + /// The newly constructed bounding box. + public BoundingBox Merge(Vector3 value2) + { + BoundingBox result; + Vector3.Min(ref Minimum, ref value2, out result.Minimum); + Vector3.Max(ref Maximum, ref value2, out result.Maximum); + return result; + } + /// /// Transforms bounding box using the given transformation matrix. /// @@ -498,6 +523,19 @@ namespace FlaxEngine result = new BoundingBox(min, max); } + /// + /// Creates the bounding box that is offseted by the given vector. Adds the offset value to minimum and maximum points. + /// + /// The bounds offset. + /// The offsetted bounds. + public BoundingBox MakeOffsetted(Vector3 offset) + { + BoundingBox result; + Vector3.Add(ref Minimum, ref offset, out result.Minimum); + Vector3.Add(ref Maximum, ref offset, out result.Maximum); + return result; + } + /// /// Creates the bounding box that is offseted by the given vector. Adds the offset value to minimum and maximum points. /// diff --git a/Source/Engine/Graphics/GPUBuffer.cpp b/Source/Engine/Graphics/GPUBuffer.cpp index a38c88dd0..6aaa939b3 100644 --- a/Source/Engine/Graphics/GPUBuffer.cpp +++ b/Source/Engine/Graphics/GPUBuffer.cpp @@ -377,12 +377,9 @@ void GPUBuffer::SetData(const void* data, uint32 size) Log::ArgumentOutOfRangeException(TEXT("Buffer.SetData")); return; } - void* mapped = Map(GPUResourceMapMode::Write); if (!mapped) - { return; - } Platform::MemoryCopy(mapped, data, size); Unmap(); } diff --git a/Source/Engine/Graphics/GPUBuffer.h b/Source/Engine/Graphics/GPUBuffer.h index 8a5418570..4d14124b3 100644 --- a/Source/Engine/Graphics/GPUBuffer.h +++ b/Source/Engine/Graphics/GPUBuffer.h @@ -190,6 +190,7 @@ public: /// /// Gets a CPU pointer to the resource by mapping its contents. Denies the GPU access to that resource. /// + /// Always call Unmap if the returned pointer is valid to release resources. /// The map operation mode. /// 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/Materials/MaterialShader.cpp b/Source/Engine/Graphics/Materials/MaterialShader.cpp index e0d3d610c..0d428c706 100644 --- a/Source/Engine/Graphics/Materials/MaterialShader.cpp +++ b/Source/Engine/Graphics/Materials/MaterialShader.cpp @@ -231,6 +231,7 @@ bool MaterialShader::Load(MemoryReadStream& shaderCacheStream, const MaterialInf _cb = nullptr; } _cbData.Resize(cbSize, false); + Platform::MemoryClear(_cbData.Get(), cbSize); } // Initialize the material based on type (create pipeline states and setup) diff --git a/Source/Engine/Graphics/Mesh.cs b/Source/Engine/Graphics/Mesh.cs index d4f693e77..0a986d44d 100644 --- a/Source/Engine/Graphics/Mesh.cs +++ b/Source/Engine/Graphics/Mesh.cs @@ -527,9 +527,8 @@ namespace FlaxEngine /// The gathered data. public Vertex0[] DownloadVertexBuffer0(bool forceGpu = false) { - var vertices = VertexCount; - var result = new Vertex0[vertices]; - if (Internal_DownloadBuffer(__unmanagedPtr, forceGpu, result, (int)InternalBufferType.VB0)) + var result = (Vertex0[])Internal_DownloadBuffer(__unmanagedPtr, forceGpu, typeof(Vertex0), (int)InternalBufferType.VB0); + if (result == null) throw new Exception("Failed to download mesh data."); return result; } @@ -541,9 +540,8 @@ namespace FlaxEngine /// The gathered data. public Vertex1[] DownloadVertexBuffer1(bool forceGpu = false) { - var vertices = VertexCount; - var result = new Vertex1[vertices]; - if (Internal_DownloadBuffer(__unmanagedPtr, forceGpu, result, (int)InternalBufferType.VB1)) + var result = (Vertex1[])Internal_DownloadBuffer(__unmanagedPtr, forceGpu, typeof(Vertex1), (int)InternalBufferType.VB1); + if (result == null) throw new Exception("Failed to download mesh data."); return result; } @@ -560,10 +558,8 @@ namespace FlaxEngine { if (!HasVertexColors) return null; - - var vertices = VertexCount; - var result = new Vertex2[vertices]; - if (Internal_DownloadBuffer(__unmanagedPtr, forceGpu, result, (int)InternalBufferType.VB2)) + var result = (Vertex2[])Internal_DownloadBuffer(__unmanagedPtr, forceGpu, typeof(Vertex2), (int)InternalBufferType.VB2); + if (result == null) throw new Exception("Failed to download mesh data."); return result; } @@ -582,7 +578,7 @@ namespace FlaxEngine var vb1 = DownloadVertexBuffer1(forceGpu); var vb2 = DownloadVertexBuffer2(forceGpu); - var vertices = VertexCount; + var vertices = vb0.Length; var result = new Vertex[vertices]; for (int i = 0; i < vertices; i++) { @@ -618,9 +614,8 @@ namespace FlaxEngine /// The gathered data. public uint[] DownloadIndexBuffer(bool forceGpu = false) { - var triangles = TriangleCount; - var result = new uint[triangles * 3]; - if (Internal_DownloadBuffer(__unmanagedPtr, forceGpu, result, (int)InternalBufferType.IB32)) + var result = (uint[])Internal_DownloadBuffer(__unmanagedPtr, forceGpu, typeof(uint), (int)InternalBufferType.IB32); + if (result == null) throw new Exception("Failed to download mesh data."); return result; } @@ -633,9 +628,8 @@ namespace FlaxEngine /// The gathered data. public ushort[] DownloadIndexBufferUShort(bool forceGpu = false) { - var triangles = TriangleCount; - var result = new ushort[triangles * 3]; - if (Internal_DownloadBuffer(__unmanagedPtr, forceGpu, result, (int)InternalBufferType.IB16)) + var result = (ushort[])Internal_DownloadBuffer(__unmanagedPtr, forceGpu, typeof(ushort), (int)InternalBufferType.IB16); + if (result == null) throw new Exception("Failed to download mesh data."); return result; } diff --git a/Source/Engine/Graphics/Models/Mesh.cpp b/Source/Engine/Graphics/Models/Mesh.cpp index 7b0130874..335b4cb69 100644 --- a/Source/Engine/Graphics/Models/Mesh.cpp +++ b/Source/Engine/Graphics/Models/Mesh.cpp @@ -746,75 +746,11 @@ enum class InternalBufferType IB32 = 4, }; -void ConvertMeshData(Mesh* mesh, InternalBufferType type, MonoArray* resultObj, void* srcData) +MonoArray* Mesh::DownloadBuffer(bool forceGpu, MonoReflectionType* resultType, int32 typeI) { - auto vertices = mesh->GetVertexCount(); - auto triangles = mesh->GetTriangleCount(); - auto indices = triangles * 3; - auto use16BitIndexBuffer = mesh->Use16BitIndexBuffer(); - - void* managedArrayPtr = mono_array_addr_with_size(resultObj, 0, 0); - switch (type) - { - case InternalBufferType::VB0: - { - Platform::MemoryCopy(managedArrayPtr, srcData, sizeof(VB0ElementType) * vertices); - break; - } - case InternalBufferType::VB1: - { - Platform::MemoryCopy(managedArrayPtr, srcData, sizeof(VB1ElementType) * vertices); - break; - } - case InternalBufferType::VB2: - { - Platform::MemoryCopy(managedArrayPtr, srcData, sizeof(VB2ElementType) * vertices); - break; - } - case InternalBufferType::IB16: - { - if (use16BitIndexBuffer) - { - Platform::MemoryCopy(managedArrayPtr, srcData, sizeof(uint16) * indices); - } - else - { - auto dst = (uint16*)managedArrayPtr; - auto src = (uint32*)srcData; - for (int32 i = 0; i < indices; i++) - { - dst[i] = src[i]; - } - } - break; - } - case InternalBufferType::IB32: - { - if (use16BitIndexBuffer) - { - auto dst = (uint32*)managedArrayPtr; - auto src = (uint16*)srcData; - for (int32 i = 0; i < indices; i++) - { - dst[i] = src[i]; - } - } - else - { - Platform::MemoryCopy(managedArrayPtr, srcData, sizeof(uint32) * indices); - } - break; - } - } -} - -bool Mesh::DownloadBuffer(bool forceGpu, MonoArray* resultObj, int32 typeI) -{ - Mesh* mesh = this; - InternalBufferType type = (InternalBufferType)typeI; + auto mesh = this; + auto type = (InternalBufferType)typeI; auto model = mesh->GetModel(); - ASSERT(model && resultObj); - ScopeLock lock(model->Locker); // Virtual assets always fetch from GPU memory @@ -823,23 +759,7 @@ bool Mesh::DownloadBuffer(bool forceGpu, MonoArray* resultObj, int32 typeI) if (!mesh->IsInitialized() && forceGpu) { LOG(Error, "Cannot load mesh data from GPU if it's not loaded."); - return true; - } - if (type == InternalBufferType::IB16 || type == InternalBufferType::IB32) - { - if (mono_array_length(resultObj) != mesh->GetTriangleCount() * 3) - { - LOG(Error, "Invalid buffer size."); - return true; - } - } - else - { - if (mono_array_length(resultObj) != mesh->GetVertexCount()) - { - LOG(Error, "Invalid buffer size."); - return true; - } + return nullptr; } MeshBufferType bufferType; @@ -859,35 +779,96 @@ bool Mesh::DownloadBuffer(bool forceGpu, MonoArray* resultObj, int32 typeI) bufferType = MeshBufferType::Index; break; default: - return true; + return nullptr; } BytesContainer data; + int32 dataCount; if (forceGpu) { // Get data from GPU // TODO: support reusing the input memory buffer to perform a single copy from staging buffer to the input CPU buffer auto task = mesh->DownloadDataGPUAsync(bufferType, data); if (task == nullptr) - return true; + return nullptr; task->Start(); model->Locker.Unlock(); if (task->Wait()) { LOG(Error, "Task failed."); - return true; + return nullptr; } model->Locker.Lock(); + + // Extract elements count from result data + switch (bufferType) + { + case MeshBufferType::Index: + dataCount = data.Length() / (Use16BitIndexBuffer() ? sizeof(uint16) : sizeof(uint32)); + break; + case MeshBufferType::Vertex0: + dataCount = data.Length() / sizeof(VB0ElementType); + break; + case MeshBufferType::Vertex1: + dataCount = data.Length() / sizeof(VB1ElementType); + break; + case MeshBufferType::Vertex2: + dataCount = data.Length() / sizeof(VB2ElementType); + break; + } } else { // Get data from CPU - int32 count; - if (DownloadDataCPU(bufferType, data, count)) - return true; + if (DownloadDataCPU(bufferType, data, dataCount)) + return nullptr; } - ConvertMeshData(mesh, type, resultObj, data.Get()); - return false; + // Convert into managed array + MonoArray* result = mono_array_new(mono_domain_get(), mono_type_get_class(mono_reflection_type_get_type(resultType)), dataCount); + void* managedArrayPtr = mono_array_addr_with_size(result, 0, 0); + const int32 elementSize = data.Length() / dataCount; + switch (type) + { + case InternalBufferType::VB0: + case InternalBufferType::VB1: + case InternalBufferType::VB2: + { + Platform::MemoryCopy(managedArrayPtr, data.Get(), data.Length()); + break; + } + case InternalBufferType::IB16: + { + if (elementSize == sizeof(uint16)) + { + Platform::MemoryCopy(managedArrayPtr, data.Get(), data.Length()); + } + else + { + auto dst = (uint16*)managedArrayPtr; + auto src = (uint32*)data.Get(); + for (int32 i = 0; i < dataCount; i++) + dst[i] = src[i]; + } + break; + } + case InternalBufferType::IB32: + { + if (elementSize == sizeof(uint16)) + { + auto dst = (uint32*)managedArrayPtr; + auto src = (uint16*)data.Get(); + for (int32 i = 0; i < dataCount; i++) + dst[i] = src[i]; + } + else + { + Platform::MemoryCopy(managedArrayPtr, data.Get(), data.Length()); + } + break; + } + } + + return result; } #endif diff --git a/Source/Engine/Graphics/Models/Mesh.h b/Source/Engine/Graphics/Models/Mesh.h index ee16c16e3..9b1ab4846 100644 --- a/Source/Engine/Graphics/Models/Mesh.h +++ b/Source/Engine/Graphics/Models/Mesh.h @@ -321,6 +321,6 @@ private: API_FUNCTION(NoProxy) bool UpdateMeshUShort(int32 vertexCount, int32 triangleCount, MonoArray* verticesObj, MonoArray* trianglesObj, MonoArray* normalsObj, MonoArray* tangentsObj, MonoArray* uvObj, MonoArray* colorsObj); API_FUNCTION(NoProxy) bool UpdateTrianglesUInt(int32 triangleCount, MonoArray* trianglesObj); API_FUNCTION(NoProxy) bool UpdateTrianglesUShort(int32 triangleCount, MonoArray* trianglesObj); - API_FUNCTION(NoProxy) bool DownloadBuffer(bool forceGpu, MonoArray* resultObj, int32 typeI); + API_FUNCTION(NoProxy) MonoArray* DownloadBuffer(bool forceGpu, MonoReflectionType* resultType, int32 typeI); #endif }; diff --git a/Source/Engine/Graphics/Models/SkinnedMesh.cpp b/Source/Engine/Graphics/Models/SkinnedMesh.cpp index 89db8ff8b..f33b8b4fd 100644 --- a/Source/Engine/Graphics/Models/SkinnedMesh.cpp +++ b/Source/Engine/Graphics/Models/SkinnedMesh.cpp @@ -516,64 +516,12 @@ enum class InternalBufferType IB32 = 4, }; -void ConvertMeshData(SkinnedMesh* mesh, InternalBufferType type, MonoArray* resultObj, void* srcData) -{ - auto vertices = mesh->GetVertexCount(); - auto triangles = mesh->GetTriangleCount(); - auto indices = triangles * 3; - auto use16BitIndexBuffer = mesh->Use16BitIndexBuffer(); - - void* managedArrayPtr = mono_array_addr_with_size(resultObj, 0, 0); - switch (type) - { - case InternalBufferType::VB0: - { - Platform::MemoryCopy(managedArrayPtr, srcData, sizeof(VB0SkinnedElementType) * vertices); - break; - } - case InternalBufferType::IB16: - { - if (use16BitIndexBuffer) - { - Platform::MemoryCopy(managedArrayPtr, srcData, sizeof(uint16) * indices); - } - else - { - auto dst = (uint16*)managedArrayPtr; - auto src = (uint32*)srcData; - for (int32 i = 0; i < indices; i++) - { - dst[i] = src[i]; - } - } - break; - } - case InternalBufferType::IB32: - { - if (use16BitIndexBuffer) - { - auto dst = (uint32*)managedArrayPtr; - auto src = (uint16*)srcData; - for (int32 i = 0; i < indices; i++) - { - dst[i] = src[i]; - } - } - else - { - Platform::MemoryCopy(managedArrayPtr, srcData, sizeof(uint32) * indices); - } - break; - } - } -} - -bool SkinnedMesh::DownloadBuffer(bool forceGpu, MonoArray* resultObj, int32 typeI) +MonoArray* SkinnedMesh::DownloadBuffer(bool forceGpu, MonoReflectionType* resultType, int32 typeI) { SkinnedMesh* mesh = this; InternalBufferType type = (InternalBufferType)typeI; auto model = mesh->GetSkinnedModel(); - ASSERT(model && resultObj); + ScopeLock lock(model->Locker); // Virtual assets always fetch from GPU memory forceGpu |= model->IsVirtual(); @@ -581,23 +529,7 @@ bool SkinnedMesh::DownloadBuffer(bool forceGpu, MonoArray* resultObj, int32 type if (!mesh->IsInitialized() && forceGpu) { LOG(Error, "Cannot load mesh data from GPU if it's not loaded."); - return true; - } - if (type == InternalBufferType::IB16 || type == InternalBufferType::IB32) - { - if (mono_array_length(resultObj) != mesh->GetTriangleCount() * 3) - { - LOG(Error, "Invalid buffer size."); - return true; - } - } - else - { - if (mono_array_length(resultObj) != mesh->GetVertexCount()) - { - LOG(Error, "Invalid buffer size."); - return true; - } + return nullptr; } MeshBufferType bufferType; @@ -610,37 +542,89 @@ bool SkinnedMesh::DownloadBuffer(bool forceGpu, MonoArray* resultObj, int32 type case InternalBufferType::IB32: bufferType = MeshBufferType::Index; break; - default: CRASH; - return true; + default: + return nullptr; } BytesContainer data; + int32 dataCount; if (forceGpu) { // Get data from GPU // TODO: support reusing the input memory buffer to perform a single copy from staging buffer to the input CPU buffer auto task = mesh->DownloadDataGPUAsync(bufferType, data); if (task == nullptr) - return true; + return nullptr; task->Start(); model->Locker.Unlock(); if (task->Wait()) { LOG(Error, "Task failed."); - return true; + return nullptr; } model->Locker.Lock(); + + // Extract elements count from result data + switch (bufferType) + { + case MeshBufferType::Index: + dataCount = data.Length() / (Use16BitIndexBuffer() ? sizeof(uint16) : sizeof(uint32)); + break; + case MeshBufferType::Vertex0: + dataCount = data.Length() / sizeof(VB0SkinnedElementType); + break; + } } else { // Get data from CPU - int32 count; - if (DownloadDataCPU(bufferType, data, count)) - return true; + if (DownloadDataCPU(bufferType, data, dataCount)) + return nullptr; } - // Convert into managed memory - ConvertMeshData(mesh, type, resultObj, data.Get()); - return false; + // Convert into managed array + MonoArray* result = mono_array_new(mono_domain_get(), mono_type_get_class(mono_reflection_type_get_type(resultType)), dataCount); + void* managedArrayPtr = mono_array_addr_with_size(result, 0, 0); + const int32 elementSize = data.Length() / dataCount; + switch (type) + { + case InternalBufferType::VB0: + { + Platform::MemoryCopy(managedArrayPtr, data.Get(), data.Length()); + break; + } + case InternalBufferType::IB16: + { + if (elementSize == sizeof(uint16)) + { + Platform::MemoryCopy(managedArrayPtr, data.Get(), data.Length()); + } + else + { + auto dst = (uint16*)managedArrayPtr; + auto src = (uint32*)data.Get(); + for (int32 i = 0; i < dataCount; i++) + dst[i] = src[i]; + } + break; + } + case InternalBufferType::IB32: + { + if (elementSize == sizeof(uint16)) + { + auto dst = (uint32*)managedArrayPtr; + auto src = (uint16*)data.Get(); + for (int32 i = 0; i < dataCount; i++) + dst[i] = src[i]; + } + else + { + Platform::MemoryCopy(managedArrayPtr, data.Get(), data.Length()); + } + break; + } + } + + return result; } #endif diff --git a/Source/Engine/Graphics/Models/SkinnedMesh.h b/Source/Engine/Graphics/Models/SkinnedMesh.h index 8bf957554..57a382cff 100644 --- a/Source/Engine/Graphics/Models/SkinnedMesh.h +++ b/Source/Engine/Graphics/Models/SkinnedMesh.h @@ -199,6 +199,6 @@ private: #if !COMPILE_WITHOUT_CSHARP API_FUNCTION(NoProxy) bool UpdateMeshUInt(MonoArray* verticesObj, MonoArray* trianglesObj, MonoArray* blendIndicesObj, MonoArray* blendWeightsObj, MonoArray* normalsObj, MonoArray* tangentsObj, MonoArray* uvObj); API_FUNCTION(NoProxy) bool UpdateMeshUShort(MonoArray* verticesObj, MonoArray* trianglesObj, MonoArray* blendIndicesObj, MonoArray* blendWeightsObj, MonoArray* normalsObj, MonoArray* tangentsObj, MonoArray* uvObj); - API_FUNCTION(NoProxy) bool DownloadBuffer(bool forceGpu, MonoArray* resultObj, int32 typeI); + API_FUNCTION(NoProxy) MonoArray* DownloadBuffer(bool forceGpu, MonoReflectionType* resultType, int32 typeI); #endif }; diff --git a/Source/Engine/Graphics/PostProcessEffect.h b/Source/Engine/Graphics/PostProcessEffect.h index a8355ed8e..2e444c0ea 100644 --- a/Source/Engine/Graphics/PostProcessEffect.h +++ b/Source/Engine/Graphics/PostProcessEffect.h @@ -17,23 +17,24 @@ struct RenderContext; /// API_CLASS(Abstract) class FLAXENGINE_API PostProcessEffect : public Script { + API_AUTO_SERIALIZATION(); DECLARE_SCRIPTING_TYPE(PostProcessEffect); public: /// /// Effect rendering location within rendering pipeline. /// - API_FIELD() PostProcessEffectLocation Location = PostProcessEffectLocation::Default; + API_FIELD(Attributes="EditorDisplay(\"Post Process Effect\"), ExpandGroups") PostProcessEffectLocation Location = PostProcessEffectLocation::Default; /// /// True whether use a single render target as both input and output. Use this if your effect doesn't need to copy the input buffer to the output but can render directly to the single texture. Can be used to optimize game performance. /// - API_FIELD() bool UseSingleTarget = false; + API_FIELD(Attributes="EditorDisplay(\"Post Process Effect\")") bool UseSingleTarget = false; /// /// Effect rendering order. Post effects are sorted before rendering (from the lowest order to the highest order). /// - API_FIELD() int32 Order = 0; + API_FIELD(Attributes="EditorDisplay(\"Post Process Effect\")") int32 Order = 0; public: /// diff --git a/Source/Engine/Graphics/RenderView.h b/Source/Engine/Graphics/RenderView.h index 52e317a51..5cfe06a8d 100644 --- a/Source/Engine/Graphics/RenderView.h +++ b/Source/Engine/Graphics/RenderView.h @@ -253,6 +253,14 @@ public: } public: + // Ignore deprecation warnings in defaults + PRAGMA_DISABLE_DEPRECATION_WARNINGS + RenderView() = default; + RenderView(const RenderView& other) = default; + RenderView(RenderView&& other) = default; + RenderView& operator=(const RenderView& other) = default; + PRAGMA_ENABLE_DEPRECATION_WARNINGS + // Set up view with custom params // @param viewProjection View * Projection matrix void SetUp(const Matrix& viewProjection); diff --git a/Source/Engine/Graphics/SkinnedMesh.cs b/Source/Engine/Graphics/SkinnedMesh.cs index 5998c6a1e..8fb7a83dc 100644 --- a/Source/Engine/Graphics/SkinnedMesh.cs +++ b/Source/Engine/Graphics/SkinnedMesh.cs @@ -274,9 +274,8 @@ namespace FlaxEngine /// The gathered data. public Vertex0[] DownloadVertexBuffer0(bool forceGpu = false) { - var vertices = VertexCount; - var result = new Vertex0[vertices]; - if (Internal_DownloadBuffer(__unmanagedPtr, forceGpu, result, (int)InternalBufferType.VB0)) + var result = (Vertex0[])Internal_DownloadBuffer(__unmanagedPtr, forceGpu, typeof(Vertex0), (int)InternalBufferType.VB0); + if (result == null) throw new Exception("Failed to download mesh data."); return result; } @@ -292,7 +291,7 @@ namespace FlaxEngine var vb0 = DownloadVertexBuffer0(forceGpu); - var vertices = VertexCount; + var vertices = vb0.Length; var result = new Vertex[vertices]; for (int i = 0; i < vertices; i++) { @@ -319,9 +318,8 @@ namespace FlaxEngine /// The gathered data. public uint[] DownloadIndexBuffer(bool forceGpu = false) { - var triangles = TriangleCount; - var result = new uint[triangles * 3]; - if (Internal_DownloadBuffer(__unmanagedPtr, forceGpu, result, (int)InternalBufferType.IB32)) + var result = (uint[])Internal_DownloadBuffer(__unmanagedPtr, forceGpu, typeof(uint), (int)InternalBufferType.IB32); + if (result == null) throw new Exception("Failed to download mesh data."); return result; } @@ -334,9 +332,8 @@ namespace FlaxEngine /// The gathered data. public ushort[] DownloadIndexBufferUShort(bool forceGpu = false) { - var triangles = TriangleCount; - var result = new ushort[triangles * 3]; - if (Internal_DownloadBuffer(__unmanagedPtr, forceGpu, result, (int)InternalBufferType.IB16)) + var result = (ushort[])Internal_DownloadBuffer(__unmanagedPtr, forceGpu, typeof(ushort), (int)InternalBufferType.IB16); + if (result == null) throw new Exception("Failed to download mesh data."); return result; } diff --git a/Source/Engine/GraphicsDevice/DirectX/DX11/GPUBufferDX11.cpp b/Source/Engine/GraphicsDevice/DirectX/DX11/GPUBufferDX11.cpp index e3ff0befe..b83ab6a08 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX11/GPUBufferDX11.cpp +++ b/Source/Engine/GraphicsDevice/DirectX/DX11/GPUBufferDX11.cpp @@ -19,7 +19,8 @@ GPUBufferView* GPUBufferDX11::View() const void* GPUBufferDX11::Map(GPUResourceMapMode mode) { - if (!IsInMainThread()) + const bool isMainThread = IsInMainThread(); + if (!isMainThread) _device->Locker.Lock(); ASSERT(!_mapped); @@ -31,7 +32,7 @@ void* GPUBufferDX11::Map(GPUResourceMapMode mode) { case GPUResourceMapMode::Read: mapType = D3D11_MAP_READ; - if (_desc.Usage == GPUResourceUsage::StagingReadback) + if (_desc.Usage == GPUResourceUsage::StagingReadback && isMainThread) mapFlags = D3D11_MAP_FLAG_DO_NOT_WAIT; break; case GPUResourceMapMode::Write: @@ -47,18 +48,19 @@ void* GPUBufferDX11::Map(GPUResourceMapMode mode) const HRESULT result = _device->GetIM()->Map(_resource, 0, mapType, mapFlags, &map); if (result != DXGI_ERROR_WAS_STILL_DRAWING) LOG_DIRECTX_RESULT(result); + _mapped = map.pData != nullptr; + if (!_mapped && !isMainThread) + _device->Locker.Unlock(); + return map.pData; } void GPUBufferDX11::Unmap() { - if (_mapped) - { - _mapped = false; - _device->GetIM()->Unmap(_resource, 0); - } - + ASSERT(_mapped); + _mapped = false; + _device->GetIM()->Unmap(_resource, 0); if (!IsInMainThread()) _device->Locker.Unlock(); } diff --git a/Source/Engine/Level/Actor.cpp b/Source/Engine/Level/Actor.cpp index 73ef072b3..1c2f1768e 100644 --- a/Source/Engine/Level/Actor.cpp +++ b/Source/Engine/Level/Actor.cpp @@ -471,6 +471,26 @@ bool Actor::HasTag(const StringView& tag) const return Tags.Contains(tag); } +void Actor::AddTag(const Tag& tag) +{ + Tags.AddUnique(tag); +} + +PRAGMA_DISABLE_DEPRECATION_WARNINGS + +const String& Actor::GetTag() const +{ + return Tags.Count() != 0 ? Tags[0].ToString() : String::Empty; +} + +void Actor::SetTag(const StringView& value) +{ + const Tag tag = Tags::Get(value); + Tags.Set(&tag, 1); +} + +PRAGMA_ENABLE_DEPRECATION_WARNINGS + void Actor::SetLayer(int32 layerIndex) { layerIndex = Math::Clamp(layerIndex, 0, 31); diff --git a/Source/Engine/Level/Actor.h b/Source/Engine/Level/Actor.h index 38bd629a5..c1d038783 100644 --- a/Source/Engine/Level/Actor.h +++ b/Source/Engine/Level/Actor.h @@ -130,6 +130,25 @@ public: /// The tag to check. API_FUNCTION() bool HasTag(const StringView& tag) const; + /// + /// Adds a tag to the actor + /// + /// The tag to add. + API_FUNCTION() void AddTag(const Tag& tag); + + /// + /// Gets the name of the tag. + /// [Deprecated in v1.5] + /// + API_PROPERTY(Attributes="HideInEditor, NoSerialize, NoAnimate") + DEPRECATED const String& GetTag() const; + + /// + /// Sets the name of the tag. + /// [Deprecated in v1.5] + /// + API_PROPERTY() DEPRECATED void SetTag(const StringView& value); + /// /// Gets the actor name. /// diff --git a/Source/Engine/Level/Scene/Lightmap.cpp b/Source/Engine/Level/Scene/Lightmap.cpp index f2d11e0c5..c8b83febf 100644 --- a/Source/Engine/Level/Scene/Lightmap.cpp +++ b/Source/Engine/Level/Scene/Lightmap.cpp @@ -103,7 +103,7 @@ void Lightmap::EnsureSize(int32 size) #if COMPILE_WITH_ASSETS_IMPORTER Guid id = Guid::New(); - LOG(Info, "Cannot load lightmap {0} ({1}:{2}). Creating new one.", id, _index, textureIndex); + LOG(Info, "Cannot load lightmap ({1}:{2}). Creating new one with ID={0}.", id, _index, textureIndex); String assetPath; _manager->GetCachedLightmapPath(&assetPath, _index, textureIndex); diff --git a/Source/Engine/Level/Scene/Lightmap.h b/Source/Engine/Level/Scene/Lightmap.h index ef43428c7..3129dbd25 100644 --- a/Source/Engine/Level/Scene/Lightmap.h +++ b/Source/Engine/Level/Scene/Lightmap.h @@ -48,7 +48,7 @@ public: /// Gets attached texture objects /// /// Lightmaps textures array - void GetTextures(GPUTexture* lightmaps[]) const + void GetTextures(GPUTexture* lightmaps[3]) const { lightmaps[0] = _textures[0] ? _textures[0]->GetTexture() : nullptr; lightmaps[1] = _textures[1] ? _textures[1]->GetTexture() : nullptr; @@ -59,7 +59,7 @@ public: /// Gets attached texture objects /// /// Lightmaps textures array - void GetTextures(Texture* lightmaps[]) const + void GetTextures(Texture* lightmaps[3]) const { lightmaps[0] = _textures[0].Get(); lightmaps[1] = _textures[1].Get(); diff --git a/Source/Engine/Localization/LocalizedStringTable.cpp b/Source/Engine/Localization/LocalizedStringTable.cpp index 63a364e77..74c9b6774 100644 --- a/Source/Engine/Localization/LocalizedStringTable.cpp +++ b/Source/Engine/Localization/LocalizedStringTable.cpp @@ -2,11 +2,10 @@ #include "LocalizedStringTable.h" #include "Engine/Serialization/JsonTools.h" +#include "Engine/Serialization/JsonWriters.h" #include "Engine/Serialization/SerializationFwd.h" #include "Engine/Content/Factories/JsonAssetFactory.h" #if USE_EDITOR -#include "Engine/ContentImporters/CreateJson.h" -#include "Engine/Serialization/JsonWriters.h" #include "Engine/Threading/Threading.h" #include "Engine/Core/Log.h" #endif @@ -55,75 +54,6 @@ String LocalizedStringTable::GetPluralString(const String& id, int32 n) const return String::Format(result.GetText(), n); } -#if USE_EDITOR - -bool LocalizedStringTable::Save(const StringView& path) -{ - // Validate state - if (WaitForLoaded()) - { - LOG(Error, "Asset loading failed. Cannot save it."); - return true; - } - if (IsVirtual() && path.IsEmpty()) - { - LOG(Error, "To save virtual asset asset you need to specify the target asset path location."); - return true; - } - - ScopeLock lock(Locker); - - // Serialize data - rapidjson_flax::StringBuffer outputData; - PrettyJsonWriter writerObj(outputData); - JsonWriter& writer = writerObj; - writer.StartObject(); - { - writer.JKEY("Locale"); - writer.String(Locale); - - if (FallbackTable.GetID().IsValid()) - { - writer.JKEY("FallbackTable"); - writer.Guid(FallbackTable.GetID()); - } - - writer.JKEY("Entries"); - writer.StartObject(); - for (auto& e : Entries) - { - writer.Key(e.Key); - if (e.Value.Count() == 1) - { - writer.String(e.Value[0]); - } - else - { - writer.StartArray(); - for (auto& q : e.Value) - writer.String(q); - writer.EndArray(); - } - } - writer.EndObject(); - } - writer.EndObject(); - - // Save asset -#if COMPILE_WITH_ASSETS_IMPORTER - const bool saveResult = CreateJson::Create(path.HasChars() ? path : StringView(GetPath()), outputData, TypeName); - if (saveResult) -#endif - { - LOG(Error, "Cannot save \'{0}\'", ToString()); - return true; - } - - return false; -} - -#endif - Asset::LoadResult LocalizedStringTable::loadAsset() { // Base @@ -170,3 +100,41 @@ void LocalizedStringTable::unload(bool isReloading) FallbackTable = nullptr; Entries.Clear(); } + +void LocalizedStringTable::OnGetData(rapidjson_flax::StringBuffer& buffer) const +{ + PrettyJsonWriter writerObj(buffer); + JsonWriter& writer = writerObj; + writer.StartObject(); + { + writer.JKEY("Locale"); + writer.String(Locale); + + if (FallbackTable.GetID().IsValid()) + { + writer.JKEY("FallbackTable"); + writer.Guid(FallbackTable.GetID()); + } + + writer.JKEY("Entries"); + writer.StartObject(); + for (auto& e : Entries) + { + writer.Key(e.Key); + if (e.Value.Count() == 1) + { + writer.String(e.Value[0]); + } + else + { + writer.StartArray(); + for (auto& q : e.Value) + writer.String(q); + writer.EndArray(); + } + } + writer.EndObject(); + } + writer.EndObject(); +} + diff --git a/Source/Engine/Localization/LocalizedStringTable.h b/Source/Engine/Localization/LocalizedStringTable.h index e52b5cc3f..4024d5d3a 100644 --- a/Source/Engine/Localization/LocalizedStringTable.h +++ b/Source/Engine/Localization/LocalizedStringTable.h @@ -61,19 +61,9 @@ public: /// The localized text. API_FUNCTION() String GetPluralString(const String& id, int32 n) const; -#if USE_EDITOR - - /// - /// Saves this asset to the file. Supported only in Editor. - /// - /// The custom asset path to use for the saving. Use empty value to save this asset to its own storage location. Can be used to duplicate asset. Must be specified when saving virtual asset. - /// True if cannot save data, otherwise false. - API_FUNCTION() bool Save(const StringView& path = StringView::Empty); - -#endif - protected: // [JsonAssetBase] LoadResult loadAsset() override; void unload(bool isReloading) override; + void OnGetData(rapidjson_flax::StringBuffer& buffer) const override; }; diff --git a/Source/Engine/Networking/NetworkConfig.h b/Source/Engine/Networking/NetworkConfig.h index 794ae74a3..d875c49b4 100644 --- a/Source/Engine/Networking/NetworkConfig.h +++ b/Source/Engine/Networking/NetworkConfig.h @@ -36,7 +36,7 @@ API_STRUCT(Namespace="FlaxEngine.Networking") struct FLAXENGINE_API NetworkConfi /// [Deprecated in v1.3] /// API_FIELD() - DEPRECATED NetworkDriverType NetworkDriverType = NetworkDriverType::ENet; + DEPRECATED NetworkDriverType NetworkDriverType; /// /// The network driver instance (implements INetworkDriver) that will be used to create and manage the peer, send and receive messages. @@ -81,4 +81,12 @@ API_STRUCT(Namespace="FlaxEngine.Networking") struct FLAXENGINE_API NetworkConfi /// API_FIELD() uint16 MessagePoolSize = 2048; + + PRAGMA_DISABLE_DEPRECATION_WARNINGS + /// Ctor. + NetworkConfig() + { + NetworkDriverType = NetworkDriverType::ENet; + } + PRAGMA_ENABLE_DEPRECATION_WARNINGS }; diff --git a/Source/Engine/Particles/ParticlesSimulation.cpp b/Source/Engine/Particles/ParticlesSimulation.cpp index 2d52a7f76..c68862925 100644 --- a/Source/Engine/Particles/ParticlesSimulation.cpp +++ b/Source/Engine/Particles/ParticlesSimulation.cpp @@ -104,15 +104,18 @@ int32 ParticleSystemInstance::GetParticlesCount() const if (GPUParticlesCountReadback && GPUParticlesCountReadback->IsAllocated()) { auto data = static_cast(GPUParticlesCountReadback->Map(GPUResourceMapMode::Read)); - for (const auto& emitter : Emitters) + if (data) { - if (emitter.Buffer && emitter.Buffer->Mode == ParticlesSimulationMode::GPU && emitter.Buffer->GPU.HasValidCount) + for (const auto& emitter : Emitters) { - result += *data; + if (emitter.Buffer && emitter.Buffer->Mode == ParticlesSimulationMode::GPU && emitter.Buffer->GPU.HasValidCount) + { + result += *data; + } + ++data; } - ++data; + GPUParticlesCountReadback->Unmap(); } - GPUParticlesCountReadback->Unmap(); } else if (Emitters.HasItems()) { diff --git a/Source/Engine/Platform/Base/StringUtilsBase.cpp b/Source/Engine/Platform/Base/StringUtilsBase.cpp index d005f8a04..ce895bfa4 100644 --- a/Source/Engine/Platform/Base/StringUtilsBase.cpp +++ b/Source/Engine/Platform/Base/StringUtilsBase.cpp @@ -537,8 +537,7 @@ String StringUtils::GetZZString(const Char* str) if (*end == '\0') end++; } - const int len = end - str; - return String(str, len); + return String(str, (int32)(end - str)); } #undef STRING_UTILS_ITOSTR_BUFFER_SIZE diff --git a/Source/Engine/Renderer/GI/GlobalSurfaceAtlasPass.cpp b/Source/Engine/Renderer/GI/GlobalSurfaceAtlasPass.cpp index 5d87858b6..860c1b285 100644 --- a/Source/Engine/Renderer/GI/GlobalSurfaceAtlasPass.cpp +++ b/Source/Engine/Renderer/GI/GlobalSurfaceAtlasPass.cpp @@ -689,8 +689,8 @@ bool GlobalSurfaceAtlasPass::Render(RenderContext& renderContext, GPUContext* co objectsBufferCapacity = counter; notReady = false; } + _culledObjectsSizeBuffer->Unmap(); } - _culledObjectsSizeBuffer->Unmap(); // Allow to be ready if the buffer was already used if (notReady && surfaceAtlasData.CulledObjectsBuffer && surfaceAtlasData.CulledObjectsBuffer->IsAllocated()) diff --git a/Source/Engine/Renderer/GlobalSignDistanceFieldPass.cpp b/Source/Engine/Renderer/GlobalSignDistanceFieldPass.cpp index 993b0c4b7..f7d5fd5a5 100644 --- a/Source/Engine/Renderer/GlobalSignDistanceFieldPass.cpp +++ b/Source/Engine/Renderer/GlobalSignDistanceFieldPass.cpp @@ -549,8 +549,8 @@ bool GlobalSignDistanceFieldPass::Render(RenderContext& renderContext, GPUContex _cascadeIndex = cascadeIndex; _sdfData = &sdfData; const float objectMargin = _voxelSize * GLOBAL_SDF_RASTERIZE_CHUNK_MARGIN; - _sdfDataOriginMax = sdfData.Origin - objectMargin; - _sdfDataOriginMax = sdfData.Origin + objectMargin; + _sdfDataOriginMin = -sdfData.Origin - objectMargin; + _sdfDataOriginMax = -sdfData.Origin + objectMargin; { PROFILE_CPU_NAMED("Draw"); BoundingBox cascadeBoundsWorld = cascadeBounds.MakeOffsetted(sdfData.Origin); @@ -958,9 +958,9 @@ void GlobalSignDistanceFieldPass::RasterizeModelSDF(Actor* actor, const ModelBas { // Setup object data BoundingBox objectBoundsCascade; - Vector3::Clamp(objectBounds.Minimum - _sdfDataOriginMin, _cascadeBounds.Minimum, _cascadeBounds.Maximum, objectBoundsCascade.Minimum); + Vector3::Clamp(objectBounds.Minimum + _sdfDataOriginMin, _cascadeBounds.Minimum, _cascadeBounds.Maximum, objectBoundsCascade.Minimum); Vector3::Subtract(objectBoundsCascade.Minimum, _cascadeBounds.Minimum, objectBoundsCascade.Minimum); - Vector3::Clamp(objectBounds.Maximum - _sdfDataOriginMax, _cascadeBounds.Minimum, _cascadeBounds.Maximum, objectBoundsCascade.Maximum); + Vector3::Clamp(objectBounds.Maximum + _sdfDataOriginMax, _cascadeBounds.Minimum, _cascadeBounds.Maximum, objectBoundsCascade.Maximum); Vector3::Subtract(objectBoundsCascade.Maximum, _cascadeBounds.Minimum, objectBoundsCascade.Maximum); const Int3 objectChunkMin(objectBoundsCascade.Minimum / _chunkSize); const Int3 objectChunkMax(objectBoundsCascade.Maximum / _chunkSize); @@ -1019,9 +1019,9 @@ void GlobalSignDistanceFieldPass::RasterizeHeightfield(Actor* actor, GPUTexture* { // Setup object data BoundingBox objectBoundsCascade; - Vector3::Clamp(objectBounds.Minimum - _sdfDataOriginMin, _cascadeBounds.Minimum, _cascadeBounds.Maximum, objectBoundsCascade.Minimum); + Vector3::Clamp(objectBounds.Minimum + _sdfDataOriginMin, _cascadeBounds.Minimum, _cascadeBounds.Maximum, objectBoundsCascade.Minimum); Vector3::Subtract(objectBoundsCascade.Minimum, _cascadeBounds.Minimum, objectBoundsCascade.Minimum); - Vector3::Clamp(objectBounds.Maximum - _sdfDataOriginMax, _cascadeBounds.Minimum, _cascadeBounds.Maximum, objectBoundsCascade.Maximum); + Vector3::Clamp(objectBounds.Maximum + _sdfDataOriginMax, _cascadeBounds.Minimum, _cascadeBounds.Maximum, objectBoundsCascade.Maximum); Vector3::Subtract(objectBoundsCascade.Maximum, _cascadeBounds.Minimum, objectBoundsCascade.Maximum); const Int3 objectChunkMin(objectBoundsCascade.Minimum / _chunkSize); const Int3 objectChunkMax(objectBoundsCascade.Maximum / _chunkSize); diff --git a/Source/Engine/Renderer/Renderer.cpp b/Source/Engine/Renderer/Renderer.cpp index 584ab6b45..edac958bc 100644 --- a/Source/Engine/Renderer/Renderer.cpp +++ b/Source/Engine/Renderer/Renderer.cpp @@ -406,7 +406,10 @@ void RenderInner(SceneRenderTask* task, RenderContext& renderContext, RenderCont // Get the light accumulation buffer auto outputFormat = renderContext.Buffers->GetOutputFormat(); - auto tempDesc = GPUTextureDescription::New2D(renderContext.Buffers->GetWidth(), renderContext.Buffers->GetHeight(), outputFormat, GPUTextureFlags::ShaderResource | GPUTextureFlags::RenderTarget | GPUTextureFlags::UnorderedAccess); + auto tempFlags = GPUTextureFlags::ShaderResource | GPUTextureFlags::RenderTarget; + if (GPUDevice::Instance->Limits.HasCompute) + tempFlags |= GPUTextureFlags::UnorderedAccess; + auto tempDesc = GPUTextureDescription::New2D(renderContext.Buffers->GetWidth(), renderContext.Buffers->GetHeight(), outputFormat, tempFlags); auto lightBuffer = RenderTargetPool::Get(tempDesc); RENDER_TARGET_POOL_SET_NAME(lightBuffer, "LightBuffer"); diff --git a/Source/Engine/ShadowsOfMordor/Builder.BuildCache.cpp b/Source/Engine/ShadowsOfMordor/Builder.BuildCache.cpp index fb3ed6b2c..e2d19980c 100644 --- a/Source/Engine/ShadowsOfMordor/Builder.BuildCache.cpp +++ b/Source/Engine/ShadowsOfMordor/Builder.BuildCache.cpp @@ -103,7 +103,7 @@ void ShadowsOfMordor::Builder::SceneBuildCache::UpdateLightmaps() // Download buffer data if (lightmapEntry.LightmapData->DownloadData(ImportLightmapTextureData)) { - LOG(Warning, "Cannot download LightmapData."); + LOG(Error, "Cannot download LightmapData."); return; } diff --git a/Source/Engine/ShadowsOfMordor/Builder.Charts.cpp b/Source/Engine/ShadowsOfMordor/Builder.Charts.cpp index ccd9f1d0e..bd9974bc2 100644 --- a/Source/Engine/ShadowsOfMordor/Builder.Charts.cpp +++ b/Source/Engine/ShadowsOfMordor/Builder.Charts.cpp @@ -152,7 +152,7 @@ void ShadowsOfMordor::Builder::updateLightmaps() { auto texture = textures[textureIndex]; GPUDevice::Instance->Locker.Unlock(); - if (texture->WaitForLoaded()) + if (texture == nullptr || texture->WaitForLoaded()) { LOG(Error, "Lightmap load failed."); return; diff --git a/Source/Engine/ShadowsOfMordor/Builder.cpp b/Source/Engine/ShadowsOfMordor/Builder.cpp index ccf956d47..1ce6670d8 100644 --- a/Source/Engine/ShadowsOfMordor/Builder.cpp +++ b/Source/Engine/ShadowsOfMordor/Builder.cpp @@ -276,6 +276,7 @@ void ShadowsOfMordor::Builder::saveState() context->Flush(); Platform::Sleep(10); void* mapped = lightmapDataStaging->Map(GPUResourceMapMode::Read); + ASSERT(mapped); stream->WriteInt32(lightmapDataSize); stream->WriteBytes(mapped, lightmapDataSize); lightmapDataStaging->Unmap(); diff --git a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Textures.cpp b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Textures.cpp index 103101bec..7edd9a43f 100644 --- a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Textures.cpp +++ b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Textures.cpp @@ -637,8 +637,8 @@ void MaterialGenerator::ProcessGroupTextures(Box* box, Node* node, Value& value) // Write operations auto framesCount = writeLocal(VariantType::Float, String::Format(TEXT("{0}.x * {1}.y"), framesXY.Value, framesXY.Value), node); - frame = writeLocal(VariantType::Float, String::Format(TEXT("fmod(floor({0}), {1})"), frame.Value, framesCount.Value), node); - auto framesXYInv = writeOperation2(node, Value::One, framesXY, '/'); + frame = writeLocal(VariantType::Float, String::Format(TEXT("fmod({0}, {1})"), frame.Value, framesCount.Value), node); + auto framesXYInv = writeOperation2(node, Value::One.AsFloat2(), framesXY, '/'); auto frameY = writeLocal(VariantType::Float, String::Format(TEXT("abs({0} * {1}.y - (floor({2} * {3}.x) + {0} * 1))"), invertY.Value, framesXY.Value, frame.Value, framesXYInv.Value), node); auto frameX = writeLocal(VariantType::Float, String::Format(TEXT("abs({0} * {1}.x - (({2} - {1}.x * floor({2} * {3}.x)) + {0} * 1))"), invertX.Value, framesXY.Value, frame.Value, framesXYInv.Value), node); value = writeLocal(VariantType::Float2, String::Format(TEXT("({3} + float2({0}, {1})) * {2}"), frameX.Value, frameY.Value, framesXYInv.Value, uv.Value), node); diff --git a/Source/Engine/UI/GUI/Panels/ScrollBar.cs b/Source/Engine/UI/GUI/Panels/ScrollBar.cs index f28d7d4cb..d91a88adf 100644 --- a/Source/Engine/UI/GUI/Panels/ScrollBar.cs +++ b/Source/Engine/UI/GUI/Panels/ScrollBar.cs @@ -23,7 +23,7 @@ namespace FlaxEngine.GUI // Scrolling - private float _clickChange = 20, _scrollChange = 30; + private float _clickChange = 20, _scrollChange = 75; private float _minimum, _maximum = 100; private float _value, _targetValue; private readonly Orientation _orientation; @@ -146,6 +146,24 @@ namespace FlaxEngine.GUI } } + /// + /// Gets or sets the speed for the scroll on mouse wheel. + /// + public float ScrollSpeedWheel + { + get => _scrollChange; + set => _scrollChange = value; + } + + /// + /// Gets or sets the speed for the scroll on mouse click. + /// + public float ScrollSpeedClick + { + get => _clickChange; + set => _clickChange = value; + } + /// /// Gets the value slow down. /// diff --git a/Source/Engine/UI/GUI/Tooltip.cs b/Source/Engine/UI/GUI/Tooltip.cs index 9180d6d58..5089e0211 100644 --- a/Source/Engine/UI/GUI/Tooltip.cs +++ b/Source/Engine/UI/GUI/Tooltip.cs @@ -179,6 +179,8 @@ namespace FlaxEngine.GUI /// The target. public void OnMouseLeaveControl(Control target) { + if (Visible) + Hide(); _lastTarget = null; } diff --git a/Source/Shaders/GI/DDGI.shader b/Source/Shaders/GI/DDGI.shader index 1c7b577b2..e90155960 100644 --- a/Source/Shaders/GI/DDGI.shader +++ b/Source/Shaders/GI/DDGI.shader @@ -160,9 +160,9 @@ void CS_Classify(uint3 DispatchThreadId : SV_DispatchThreadID) uint sdfCascade = GetGlobalSDFCascade(GlobalSDF, probePosition); float4 CachedProbeOffsets[64]; // TODO: test performance diff when using shared memory and larger thread group (is it worth it?) - for(uint x = 0; x < 4; x++) - for(uint y = 0; y < 4; y++) - for(uint z = 0; z < 4; z++) + for (uint x = 0; x < 4; x++) + for (uint y = 0; y < 4; y++) + for (uint z = 0; z < 4; z++) { float3 offset = Remap(float3(x, y, z), 0, 3, -0.5f, 0.5f) * relocateLimit; float offsetSdf = SampleGlobalSDFCascade(GlobalSDF, GlobalSDFTex, probeBasePosition + offset, sdfCascade); @@ -273,7 +273,7 @@ void CS_TraceRays(uint3 DispatchThreadId : SV_DispatchThreadID) float4 probeData = LoadDDGIProbeData(DDGI, ProbesData, CascadeIndex, probeIndex); uint probeState = DecodeDDGIProbeState(probeData); uint probeRaysCount = GetProbeRaysCount(DDGI, probeState); - if (probeState == DDGI_PROBE_STATE_INACTIVE || probeRaysCount < rayIndex) + if (probeState == DDGI_PROBE_STATE_INACTIVE || rayIndex >= probeRaysCount) return; // Skip disabled probes or if current thread's ray is unused float3 probePosition = DecodeDDGIProbePosition(DDGI, probeData, CascadeIndex, probeIndex, probeCoords); float3 probeRayDirection = GetProbeRayDirection(DDGI, rayIndex); diff --git a/Source/Tools/Flax.Build/Build/NativeCpp/IncludesCache.cs b/Source/Tools/Flax.Build/Build/NativeCpp/IncludesCache.cs index 7f223df6a..51872df44 100644 --- a/Source/Tools/Flax.Build/Build/NativeCpp/IncludesCache.cs +++ b/Source/Tools/Flax.Build/Build/NativeCpp/IncludesCache.cs @@ -342,6 +342,16 @@ namespace Flax.Build.NativeCpp } } + // Relative to ThirdParty includes for library includes + if (!isValid && isLibraryInclude) + { + includedFilePath = Path.Combine(Globals.Root, "Source", "ThirdParty", includedFile); + if (FileExists(includedFilePath)) + { + isValid = true; + } + } + if (!isValid) { // Invalid include diff --git a/Source/Tools/Flax.Build/Projects/VisualStudio/VCProjectGenerator.cs b/Source/Tools/Flax.Build/Projects/VisualStudio/VCProjectGenerator.cs index a4b19f79e..a2901011f 100644 --- a/Source/Tools/Flax.Build/Projects/VisualStudio/VCProjectGenerator.cs +++ b/Source/Tools/Flax.Build/Projects/VisualStudio/VCProjectGenerator.cs @@ -105,8 +105,9 @@ namespace Flax.Build.Projects.VisualStudio vcProjectFileContent.AppendLine(" MakeFileProj"); if (Version >= VisualStudioVersion.VisualStudio2022) vcProjectFileContent.AppendLine(" false"); + vcProjectFileContent.AppendLine(" ./"); vcProjectFileContent.AppendLine(" "); - + // Default properties vcProjectFileContent.AppendLine(" "); @@ -343,6 +344,32 @@ namespace Flax.Build.Projects.VisualStudio vcUserFileContent.AppendLine(""); + if (platforms.Any(x => x.Target == TargetPlatform.Linux)) + { + // Override MSBuild .targets file with one that runs NMake commands (workaround for Rider on Linux) + var cppTargetsFileContent = new StringBuilder(); + cppTargetsFileContent.AppendLine(""); + cppTargetsFileContent.AppendLine(" "); + cppTargetsFileContent.AppendLine(" "); + cppTargetsFileContent.AppendLine(" "); + cppTargetsFileContent.AppendLine(" "); + cppTargetsFileContent.AppendLine(" "); + cppTargetsFileContent.AppendLine(" "); + cppTargetsFileContent.AppendLine(" "); + cppTargetsFileContent.AppendLine(" "); + cppTargetsFileContent.AppendLine(" "); + cppTargetsFileContent.AppendLine(" "); + cppTargetsFileContent.AppendLine(" "); + cppTargetsFileContent.AppendLine(" $(RootNamespace)$(Configuration.Split('.')[0])"); + cppTargetsFileContent.AppendLine(" $(OutDir)/$(TargetName)$(TargetExt)"); + cppTargetsFileContent.AppendLine(" "); + cppTargetsFileContent.AppendLine(""); + + Utilities.WriteFileIfChanged(Path.Combine(projectDirectory, "Microsoft.Cpp.targets"), cppTargetsFileContent.ToString()); + Utilities.WriteFileIfChanged(Path.Combine(projectDirectory, "Microsoft.Cpp.Default.props"), vcUserFileContent.ToString()); + Utilities.WriteFileIfChanged(Path.Combine(projectDirectory, "Microsoft.Cpp.props"), vcUserFileContent.ToString()); + } + // Save the files Utilities.WriteFileIfChanged(project.Path, vcProjectFileContent.ToString()); Utilities.WriteFileIfChanged(project.Path + ".filters", vcFiltersFileContent.ToString());