diff --git a/Source/Editor/GUI/Timeline/Tracks/CameraCutTrack.cs b/Source/Editor/GUI/Timeline/Tracks/CameraCutTrack.cs index f6a542faa..5ca9ff7ce 100644 --- a/Source/Editor/GUI/Timeline/Tracks/CameraCutTrack.cs +++ b/Source/Editor/GUI/Timeline/Tracks/CameraCutTrack.cs @@ -125,6 +125,8 @@ namespace FlaxEditor.GUI.Timeline.Tracks fov = cam.FieldOfView; customAspectRatio = cam.CustomAspectRatio; view.RenderLayersMask = cam.RenderLayersMask; + view.Flags = cam.RenderFlags; + view.Mode = cam.RenderMode; } // Try to evaluate camera properties based on the animated tracks diff --git a/Source/Editor/SceneGraph/Actors/AnimatedModelNode.cs b/Source/Editor/SceneGraph/Actors/AnimatedModelNode.cs index 604907ae4..0bf7f6afc 100644 --- a/Source/Editor/SceneGraph/Actors/AnimatedModelNode.cs +++ b/Source/Editor/SceneGraph/Actors/AnimatedModelNode.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.ComponentModel; using System.Linq; using FlaxEditor.GUI.ContextMenu; +using FlaxEditor.Windows; using FlaxEngine; namespace FlaxEditor.SceneGraph.Actors @@ -22,9 +23,9 @@ namespace FlaxEditor.SceneGraph.Actors } /// - public override void OnContextMenu(ContextMenu contextMenu) + public override void OnContextMenu(ContextMenu contextMenu, EditorWindow window) { - base.OnContextMenu(contextMenu); + base.OnContextMenu(contextMenu, window); var actor = (AnimatedModel)Actor; if (actor && actor.SkinnedModel) diff --git a/Source/Editor/SceneGraph/Actors/CameraNode.cs b/Source/Editor/SceneGraph/Actors/CameraNode.cs index 400a2e06f..1e01b5c50 100644 --- a/Source/Editor/SceneGraph/Actors/CameraNode.cs +++ b/Source/Editor/SceneGraph/Actors/CameraNode.cs @@ -6,6 +6,8 @@ using Real = System.Double; using Real = System.Single; #endif +using FlaxEditor.GUI.ContextMenu; +using FlaxEditor.Windows; using FlaxEngine; namespace FlaxEditor.SceneGraph.Actors @@ -23,6 +25,25 @@ namespace FlaxEditor.SceneGraph.Actors { } + /// + public override void OnContextMenu(ContextMenu contextMenu, EditorWindow window) + { + base.OnContextMenu(contextMenu, window); + if (window is not SceneTreeWindow win) + return; + var button = new ContextMenuButton(contextMenu, "Move Camera to View"); + button.Parent = contextMenu.ItemsContainer; + contextMenu.ItemsContainer.Children.Remove(button); + contextMenu.ItemsContainer.Children.Insert(4, button); + button.Clicked += () => + { + var c = Actor as Camera; + var viewport = Editor.Instance.Windows.EditWin.Viewport; + c.Position = viewport.ViewPosition; + c.Orientation = viewport.ViewOrientation; + }; + } + /// public override bool RayCastSelf(ref RayCastData ray, out Real distance, out Vector3 normal) { diff --git a/Source/Editor/SceneGraph/Actors/SceneNode.cs b/Source/Editor/SceneGraph/Actors/SceneNode.cs index 0405e4fdf..c2ee3645a 100644 --- a/Source/Editor/SceneGraph/Actors/SceneNode.cs +++ b/Source/Editor/SceneGraph/Actors/SceneNode.cs @@ -3,6 +3,7 @@ using System.IO; using FlaxEditor.GUI.ContextMenu; using FlaxEditor.SceneGraph.GUI; +using FlaxEditor.Windows; using FlaxEngine; namespace FlaxEditor.SceneGraph.Actors @@ -65,7 +66,7 @@ namespace FlaxEditor.SceneGraph.Actors public override SceneNode ParentScene => this; /// - public override void OnContextMenu(ContextMenu contextMenu) + public override void OnContextMenu(ContextMenu contextMenu, EditorWindow window) { contextMenu.AddSeparator(); var path = Scene.Path; @@ -80,7 +81,7 @@ namespace FlaxEditor.SceneGraph.Actors if (Level.ScenesCount > 1) contextMenu.AddButton("Unload all but this scene", OnUnloadAllButSelectedScene).LinkTooltip("Unloads all of the active scenes except for the selected scene.").Enabled = Editor.Instance.StateMachine.CurrentState.CanChangeScene; - base.OnContextMenu(contextMenu); + base.OnContextMenu(contextMenu, window); } private void OnSelect() diff --git a/Source/Editor/SceneGraph/Actors/SplineNode.cs b/Source/Editor/SceneGraph/Actors/SplineNode.cs index 1b35cdc35..3b9e8651d 100644 --- a/Source/Editor/SceneGraph/Actors/SplineNode.cs +++ b/Source/Editor/SceneGraph/Actors/SplineNode.cs @@ -9,6 +9,7 @@ using Real = System.Single; using System; using FlaxEditor.GUI.ContextMenu; using FlaxEditor.Modules; +using FlaxEditor.Windows; using FlaxEngine; using FlaxEngine.Json; using Object = FlaxEngine.Object; @@ -203,9 +204,9 @@ namespace FlaxEditor.SceneGraph.Actors } } - public override void OnContextMenu(ContextMenu contextMenu) + public override void OnContextMenu(ContextMenu contextMenu, EditorWindow window) { - ParentNode.OnContextMenu(contextMenu); + ParentNode.OnContextMenu(contextMenu, window); } public static SceneGraphNode Create(StateData state) @@ -272,9 +273,9 @@ namespace FlaxEditor.SceneGraph.Actors DebugDraw.DrawSphere(new BoundingSphere(pos, tangentSize), Color.YellowGreen, 0, false); } - public override void OnContextMenu(ContextMenu contextMenu) + public override void OnContextMenu(ContextMenu contextMenu, EditorWindow window) { - ParentNode.OnContextMenu(contextMenu); + ParentNode.OnContextMenu(contextMenu, window); } public override void OnDispose() @@ -354,9 +355,9 @@ namespace FlaxEditor.SceneGraph.Actors } /// - public override void OnContextMenu(ContextMenu contextMenu) + public override void OnContextMenu(ContextMenu contextMenu, EditorWindow window) { - base.OnContextMenu(contextMenu); + base.OnContextMenu(contextMenu, window); contextMenu.AddButton("Add spline model", OnAddSplineModel); contextMenu.AddButton("Add spline collider", OnAddSplineCollider); diff --git a/Source/Editor/SceneGraph/Actors/StaticModelNode.cs b/Source/Editor/SceneGraph/Actors/StaticModelNode.cs index 52176ab8b..8de1a7f22 100644 --- a/Source/Editor/SceneGraph/Actors/StaticModelNode.cs +++ b/Source/Editor/SceneGraph/Actors/StaticModelNode.cs @@ -3,6 +3,7 @@ using System; using FlaxEditor.Content; using FlaxEditor.GUI.ContextMenu; +using FlaxEditor.Windows; using FlaxEngine; namespace FlaxEditor.SceneGraph.Actors @@ -21,9 +22,9 @@ namespace FlaxEditor.SceneGraph.Actors } /// - public override void OnContextMenu(ContextMenu contextMenu) + public override void OnContextMenu(ContextMenu contextMenu, EditorWindow window) { - base.OnContextMenu(contextMenu); + base.OnContextMenu(contextMenu, window); contextMenu.AddButton("Add collider", OnAddMeshCollider).Enabled = ((StaticModel)Actor).Model != null; } diff --git a/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs b/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs index c4edda65e..d392e8309 100644 --- a/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs +++ b/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs @@ -599,7 +599,7 @@ namespace FlaxEditor.SceneGraph.GUI // Drag scripts else if (_dragScripts != null && _dragScripts.HasValidDrag) { - foreach(var script in _dragScripts.Objects) + foreach (var script in _dragScripts.Objects) { var customAction = script.HasPrefabLink ? new ReparentAction(script) : null; using (new UndoBlock(ActorNode.Root.Undo, script, "Change script parent", customAction)) @@ -616,7 +616,7 @@ namespace FlaxEditor.SceneGraph.GUI var spawnParent = myActor; if (DragOverMode == DragItemPositioning.Above || DragOverMode == DragItemPositioning.Below) spawnParent = newParent; - + for (int i = 0; i < _dragAssets.Objects.Count; i++) { var item = _dragAssets.Objects[i]; @@ -720,7 +720,7 @@ namespace FlaxEditor.SceneGraph.GUI for (var i = 0; i < tree.Selection.Count; i++) { var e = tree.Selection[i]; - + // Skip if parent is already selected to keep correct parenting if (tree.Selection.Contains(e.Parent)) continue; diff --git a/Source/Editor/SceneGraph/SceneGraphNode.cs b/Source/Editor/SceneGraph/SceneGraphNode.cs index a9c350bb2..672a239dc 100644 --- a/Source/Editor/SceneGraph/SceneGraphNode.cs +++ b/Source/Editor/SceneGraph/SceneGraphNode.cs @@ -11,6 +11,7 @@ using System.Collections.Generic; using System.Linq; using FlaxEditor.Modules; using FlaxEditor.SceneGraph.Actors; +using FlaxEditor.Windows; using FlaxEngine; namespace FlaxEditor.SceneGraph @@ -339,7 +340,7 @@ namespace FlaxEditor.SceneGraph /// /// Called when scene tree window wants to show the context menu. Allows to add custom options. /// - public virtual void OnContextMenu(FlaxEditor.GUI.ContextMenu.ContextMenu contextMenu) + public virtual void OnContextMenu(FlaxEditor.GUI.ContextMenu.ContextMenu contextMenu, EditorWindow window) { } diff --git a/Source/Editor/Surface/Archetypes/Tools.cs b/Source/Editor/Surface/Archetypes/Tools.cs index 9f6db7ea4..68bea9051 100644 --- a/Source/Editor/Surface/Archetypes/Tools.cs +++ b/Source/Editor/Surface/Archetypes/Tools.cs @@ -787,7 +787,7 @@ namespace FlaxEditor.Surface.Archetypes } } - private class AsNode : SurfaceNode + internal class AsNode : SurfaceNode { private TypePickerControl _picker; @@ -838,6 +838,15 @@ namespace FlaxEditor.Surface.Archetypes box.CurrentType = type ? type : ScriptType.FlaxObject; } + /// + /// Sets the type of the picker and the type of the output box + /// + /// Target Type + public void SetPickerValue(ScriptType type) + { + _picker.Value = type; + } + /// public override void OnDestroy() { @@ -999,6 +1008,15 @@ namespace FlaxEditor.Surface.Archetypes GetBox(4).CurrentType = type ? type : _picker.Type; } + /// + /// Sets the type of the picker and the type of the output box + /// + /// Target Type + public void SetPickerValue(ScriptType type) + { + _picker.Value = type; + } + /// public override void OnDestroy() { diff --git a/Source/Editor/Surface/ContextMenu/VisjectCM.cs b/Source/Editor/Surface/ContextMenu/VisjectCM.cs index 342429fc8..aa2069ee2 100644 --- a/Source/Editor/Surface/ContextMenu/VisjectCM.cs +++ b/Source/Editor/Surface/ContextMenu/VisjectCM.cs @@ -721,9 +721,7 @@ namespace FlaxEditor.Surface.ContextMenu SelectedItem = previousSelectedItem; // Scroll into view (without smoothing) - _panel1.VScrollBar.SmoothingScale = 0; - _panel1.ScrollViewTo(SelectedItem); - _panel1.VScrollBar.SmoothingScale = 1; + _panel1.ScrollViewTo(SelectedItem, true); } return true; } diff --git a/Source/Editor/Surface/Elements/Box.cs b/Source/Editor/Surface/Elements/Box.cs index a03c8f701..ba2009b2a 100644 --- a/Source/Editor/Surface/Elements/Box.cs +++ b/Source/Editor/Surface/Elements/Box.cs @@ -820,8 +820,62 @@ namespace FlaxEditor.Surface.Elements if (useCaster) { // Connect via Caster - //AddCaster(oB, iB); - throw new NotImplementedException("AddCaster(..) function"); + const float casterXOffset = 250; + if (Surface.Undo != null && Surface.Undo.Enabled) + { + bool undoEnabled = Surface.Undo.Enabled; + Surface.Undo.Enabled = false; + SurfaceNode node = Surface.Context.SpawnNode(7, 22, Float2.Zero); // 22 AsNode, 25 CastNode + Surface.Undo.Enabled = undoEnabled; + if (node is not Archetypes.Tools.AsNode castNode) + throw new Exception("Node is not a casting node!"); + + // Set the type of the casting node + undoEnabled = castNode.Surface.Undo.Enabled; + castNode.Surface.Undo.Enabled = false; + castNode.SetPickerValue(iB.CurrentType); + castNode.Surface.Undo.Enabled = undoEnabled; + if (node.GetBox(0) is not OutputBox castOutputBox || node.GetBox(1) is not InputBox castInputBox) + throw new NullReferenceException("Casting failed. Cast node is invalid!"); + + // We set the position of the cast node here to set it relative to the target nodes input box + undoEnabled = castNode.Surface.Undo.Enabled; + castNode.Surface.Undo.Enabled = false; + var wantedOffset = iB.ParentNode.Location - new Float2(casterXOffset, -(iB.LocalY - castOutputBox.LocalY)); + castNode.Location = Surface.Root.PointFromParent(ref wantedOffset); + castNode.Surface.Undo.Enabled = undoEnabled; + + var spawnNodeAction = new AddRemoveNodeAction(castNode, true); + + var connectToCastNodeAction = new ConnectBoxesAction(castInputBox, oB, true); + castInputBox.CreateConnection(oB); + connectToCastNodeAction.End(); + + var connectCastToTargetNodeAction = new ConnectBoxesAction(iB, castOutputBox, true); + iB.CreateConnection(castOutputBox); + connectCastToTargetNodeAction.End(); + + Surface.AddBatchedUndoAction(new MultiUndoAction(spawnNodeAction, connectToCastNodeAction, connectCastToTargetNodeAction)); + } + else + { + SurfaceNode node = Surface.Context.SpawnNode(7, 22, Float2.Zero); // 22 AsNode, 25 CastNode + if (node is not Archetypes.Tools.AsNode castNode) + throw new Exception("Node is not a casting node!"); + + // Set the type of the casting node + castNode.SetPickerValue(iB.CurrentType); + if (node.GetBox(0) is not OutputBox castOutputBox || node.GetBox(1) is not InputBox castInputBox) + throw new NullReferenceException("Casting failed. Cast node is invalid!"); + + // We set the position of the cast node here to set it relative to the target nodes input box + var wantedOffset = iB.ParentNode.Location - new Float2(casterXOffset, -(iB.LocalY - castOutputBox.LocalY)); + castNode.Location = Surface.Root.PointFromParent(ref wantedOffset); + + castInputBox.CreateConnection(oB); + iB.CreateConnection(castOutputBox); + } + Surface.MarkAsEdited(); } else { diff --git a/Source/Editor/Viewport/EditorViewport.cs b/Source/Editor/Viewport/EditorViewport.cs index 8c71a8b02..0c8bfc07d 100644 --- a/Source/Editor/Viewport/EditorViewport.cs +++ b/Source/Editor/Viewport/EditorViewport.cs @@ -2,6 +2,7 @@ using System; using System.Linq; +using FlaxEditor.Content.Settings; using FlaxEditor.GUI.ContextMenu; using FlaxEditor.GUI.Input; using FlaxEditor.Options; @@ -9,6 +10,8 @@ using FlaxEditor.Viewport.Cameras; using FlaxEditor.Viewport.Widgets; using FlaxEngine; using FlaxEngine.GUI; +using Newtonsoft.Json; +using JsonSerializer = FlaxEngine.Json.JsonSerializer; namespace FlaxEditor.Viewport { @@ -486,10 +489,63 @@ namespace FlaxEditor.Viewport } } + // View Layers + { + var viewLayers = ViewWidgetButtonMenu.AddChildMenu("View Layers").ContextMenu; + viewLayers.AddButton("Copy layers", () => Clipboard.Text = JsonSerializer.Serialize(Task.View.RenderLayersMask)); + viewLayers.AddButton("Paste layers", () => + { + try + { + Task.ViewLayersMask = JsonSerializer.Deserialize(Clipboard.Text); + } + catch + { + } + }); + viewLayers.AddButton("Reset layers", () => Task.ViewLayersMask = LayersMask.Default).Icon = Editor.Instance.Icons.Rotate32; + viewLayers.AddButton("Disable layers", () => Task.ViewLayersMask = new LayersMask(0)).Icon = Editor.Instance.Icons.Rotate32; + viewLayers.AddSeparator(); + var layers = LayersAndTagsSettings.GetCurrentLayers(); + if (layers != null && layers.Length > 0) + { + for (int i = 0; i < layers.Length; i++) + { + var layer = layers[i]; + var button = viewLayers.AddButton(layer); + button.CloseMenuOnClick = false; + button.Tag = 1 << i; + } + } + viewLayers.ButtonClicked += button => + { + if (button.Tag != null) + { + int layerIndex = (int)button.Tag; + LayersMask mask = new LayersMask(layerIndex); + Task.ViewLayersMask ^= mask; + button.Icon = (Task.ViewLayersMask & mask) != 0 ? Style.Current.CheckBoxTick : SpriteHandle.Invalid; + } + }; + viewLayers.VisibleChanged += WidgetViewLayersShowHide; + } + // View Flags { var viewFlags = ViewWidgetButtonMenu.AddChildMenu("View Flags").ContextMenu; + viewFlags.AddButton("Copy flags", () => Clipboard.Text = JsonSerializer.Serialize(Task.ViewFlags)); + viewFlags.AddButton("Paste flags", () => + { + try + { + Task.ViewFlags = JsonSerializer.Deserialize(Clipboard.Text); + } + catch + { + } + }); viewFlags.AddButton("Reset flags", () => Task.ViewFlags = ViewFlags.DefaultEditor).Icon = Editor.Instance.Icons.Rotate32; + viewFlags.AddButton("Disable flags", () => Task.ViewFlags = ViewFlags.None).Icon = Editor.Instance.Icons.Rotate32; viewFlags.AddSeparator(); for (int i = 0; i < EditorViewportViewFlagsValues.Length; i++) { @@ -504,7 +560,7 @@ namespace FlaxEditor.Viewport { var v = (ViewFlags)button.Tag; Task.ViewFlags ^= v; - button.Icon = (Task.View.Flags & v) != 0 ? Style.Current.CheckBoxTick : SpriteHandle.Invalid; + button.Icon = (Task.ViewFlags & v) != 0 ? Style.Current.CheckBoxTick : SpriteHandle.Invalid; } }; viewFlags.VisibleChanged += WidgetViewFlagsShowHide; @@ -513,6 +569,18 @@ namespace FlaxEditor.Viewport // Debug View { var debugView = ViewWidgetButtonMenu.AddChildMenu("Debug View").ContextMenu; + debugView.AddButton("Copy view", () => Clipboard.Text = JsonSerializer.Serialize(Task.ViewMode)); + debugView.AddButton("Paste view", () => + { + try + { + Task.ViewMode = JsonSerializer.Deserialize(Clipboard.Text); + } + catch + { + } + }); + debugView.AddSeparator(); for (int i = 0; i < EditorViewportViewModeValues.Length; i++) { ref var v = ref EditorViewportViewModeValues[i]; @@ -1084,9 +1152,9 @@ namespace FlaxEditor.Viewport _isVirtualMouseRightDown = false; // Cancel when mouse right or escape is pressed if (_wasVirtualMouseRightDown) wasControllingMouse = true; - if (_isVirtualMouseRightDown) + if (_isVirtualMouseRightDown) _isControllingMouse = _isVirtualMouseRightDown; - + if (wasControllingMouse != _isControllingMouse) { if (_isControllingMouse) @@ -1591,6 +1659,24 @@ namespace FlaxEditor.Viewport } } + private void WidgetViewLayersShowHide(Control cm) + { + if (cm.Visible == false) + return; + + var ccm = (ContextMenu)cm; + var layersMask = Task.ViewLayersMask; + foreach (var e in ccm.Items) + { + if (e is ContextMenuButton b && b != null && b.Tag != null) + { + int layerIndex = (int)b.Tag; + LayersMask mask = new LayersMask(layerIndex); + b.Icon = (layersMask & mask) != 0 ? Style.Current.CheckBoxTick : SpriteHandle.Invalid; + } + } + } + private float GetGamepadAxis(GamepadAxis axis) { var value = FlaxEngine.Input.GetGamepadAxis(InputGamepadIndex.All, axis); diff --git a/Source/Editor/Windows/Assets/PrefabWindow.Hierarchy.cs b/Source/Editor/Windows/Assets/PrefabWindow.Hierarchy.cs index 6448ebbb2..70aa1dca3 100644 --- a/Source/Editor/Windows/Assets/PrefabWindow.Hierarchy.cs +++ b/Source/Editor/Windows/Assets/PrefabWindow.Hierarchy.cs @@ -313,7 +313,7 @@ namespace FlaxEditor.Windows.Assets } if (showCustomNodeOptions) { - Selection[0].OnContextMenu(contextMenu); + Selection[0].OnContextMenu(contextMenu, this); } ContextMenuShow?.Invoke(contextMenu); diff --git a/Source/Editor/Windows/Assets/PrefabWindow.cs b/Source/Editor/Windows/Assets/PrefabWindow.cs index 50aafd46a..53d619b01 100644 --- a/Source/Editor/Windows/Assets/PrefabWindow.cs +++ b/Source/Editor/Windows/Assets/PrefabWindow.cs @@ -151,7 +151,7 @@ namespace FlaxEditor.Windows.Assets Graph = new LocalSceneGraph(new CustomRootNode(this)); _tree = new PrefabTree { - Margin = new Margin(0.0f, 0.0f, -16.0f, 0.0f), // Hide root node + Margin = new Margin(0.0f, 0.0f, -16.0f, _treePanel.ScrollBarsSize), // Hide root node IsScrollable = true, }; _tree.AddChild(Graph.Root.TreeNode); diff --git a/Source/Editor/Windows/SceneTreeWindow.ContextMenu.cs b/Source/Editor/Windows/SceneTreeWindow.ContextMenu.cs index 63d110368..a99c3ac7d 100644 --- a/Source/Editor/Windows/SceneTreeWindow.ContextMenu.cs +++ b/Source/Editor/Windows/SceneTreeWindow.ContextMenu.cs @@ -215,7 +215,7 @@ namespace FlaxEditor.Windows } if (showCustomNodeOptions) { - Editor.SceneEditing.Selection[0].OnContextMenu(contextMenu); + Editor.SceneEditing.Selection[0].OnContextMenu(contextMenu, this); } ContextMenuShow?.Invoke(contextMenu); diff --git a/Source/Editor/Windows/SceneTreeWindow.cs b/Source/Editor/Windows/SceneTreeWindow.cs index fba052269..cbaa27371 100644 --- a/Source/Editor/Windows/SceneTreeWindow.cs +++ b/Source/Editor/Windows/SceneTreeWindow.cs @@ -74,7 +74,7 @@ namespace FlaxEditor.Windows root.TreeNode.Expand(); _tree = new Tree(true) { - Margin = new Margin(0.0f, 0.0f, -16.0f, 0.0f), // Hide root node + Margin = new Margin(0.0f, 0.0f, -16.0f, _sceneTreePanel.ScrollBarsSize), // Hide root node IsScrollable = true, }; _tree.AddChild(root.TreeNode); diff --git a/Source/Editor/Windows/Search/ContentFinder.cs b/Source/Editor/Windows/Search/ContentFinder.cs index 05e1d6792..e19d212b8 100644 --- a/Source/Editor/Windows/Search/ContentFinder.cs +++ b/Source/Editor/Windows/Search/ContentFinder.cs @@ -54,9 +54,7 @@ namespace FlaxEditor.Windows.Search _selectedItem.BackgroundColor = Style.Current.BackgroundSelected; if (_matchedItems.Count > VisibleItemCount) { - _resultPanel.VScrollBar.SmoothingScale = 0; - _resultPanel.ScrollViewTo(_selectedItem); - _resultPanel.VScrollBar.SmoothingScale = 1; + _resultPanel.ScrollViewTo(_selectedItem, true); } } } diff --git a/Source/Engine/Graphics/Models/ModelData.Tool.cpp b/Source/Engine/Graphics/Models/ModelData.Tool.cpp index 89a227eb7..d061d0745 100644 --- a/Source/Engine/Graphics/Models/ModelData.Tool.cpp +++ b/Source/Engine/Graphics/Models/ModelData.Tool.cpp @@ -171,6 +171,8 @@ int32 FindVertex(const MeshData& mesh, int32 vertexIndex, int32 startIndex, int3 const Float3 vNormal = mesh.Normals.HasItems() ? mesh.Normals[vertexIndex] : Float3::Zero; const Float3 vTangent = mesh.Tangents.HasItems() ? mesh.Tangents[vertexIndex] : Float3::Zero; const Float2 vLightmapUV = mesh.LightmapUVs.HasItems() ? mesh.LightmapUVs[vertexIndex] : Float2::Zero; + const Color vColor = mesh.Colors.HasItems() ? mesh.Colors[vertexIndex] : Color::Black; // Assuming Color::Black as a default color + const int32 end = startIndex + searchRange; for (size_t i = 0; i < sparialSortCache.size(); i++) @@ -184,6 +186,8 @@ int32 FindVertex(const MeshData& mesh, int32 vertexIndex, int32 startIndex, int3 const Float3 vNormal = mesh.Normals.HasItems() ? mesh.Normals[vertexIndex] : Float3::Zero; const Float3 vTangent = mesh.Tangents.HasItems() ? mesh.Tangents[vertexIndex] : Float3::Zero; const Float2 vLightmapUV = mesh.LightmapUVs.HasItems() ? mesh.LightmapUVs[vertexIndex] : Float2::Zero; + const Color vColor = mesh.Colors.HasItems() ? mesh.Colors[vertexIndex] : Color::Black; // Assuming Color::Black as a default color + const int32 end = startIndex + searchRange; for (int32 v = startIndex; v < end; v++) @@ -201,6 +205,8 @@ int32 FindVertex(const MeshData& mesh, int32 vertexIndex, int32 startIndex, int3 continue; if (mesh.LightmapUVs.HasItems() && (vLightmapUV - mesh.LightmapUVs[v]).LengthSquared() > uvEpsSqr) continue; + if (mesh.Colors.HasItems() && vColor != mesh.Colors[v]) + continue; // TODO: check more components? return v; diff --git a/Source/Engine/Graphics/RenderTask.cs b/Source/Engine/Graphics/RenderTask.cs index a1f90a9c5..fd42c754c 100644 --- a/Source/Engine/Graphics/RenderTask.cs +++ b/Source/Engine/Graphics/RenderTask.cs @@ -39,5 +39,19 @@ namespace FlaxEngine View = view; } } + + /// + /// The rendering mask for layers. Used to exclude objects from rendering (via property). + /// + public LayersMask ViewLayersMask + { + get => View.RenderLayersMask; + set + { + var view = View; + view.RenderLayersMask = value; + View = view; + } + } } } diff --git a/Source/Engine/Graphics/RenderView.cpp b/Source/Engine/Graphics/RenderView.cpp index 46766c34d..efcbb1513 100644 --- a/Source/Engine/Graphics/RenderView.cpp +++ b/Source/Engine/Graphics/RenderView.cpp @@ -176,7 +176,7 @@ void RenderView::SetProjector(float nearPlane, float farPlane, const Float3& pos CullingFrustum = Frustum; } -void RenderView::CopyFrom(Camera* camera, Viewport* viewport) +void RenderView::CopyFrom(const Camera* camera, const Viewport* viewport) { const Vector3 cameraPos = camera->GetPosition(); LargeWorlds::UpdateOrigin(Origin, cameraPos); @@ -192,6 +192,8 @@ void RenderView::CopyFrom(Camera* camera, Viewport* viewport) Frustum.GetInvMatrix(IVP); CullingFrustum = Frustum; RenderLayersMask = camera->RenderLayersMask; + Flags = camera->RenderFlags; + Mode = camera->RenderMode; } void RenderView::GetWorldMatrix(const Transform& transform, Matrix& world) const diff --git a/Source/Engine/Graphics/RenderView.cs b/Source/Engine/Graphics/RenderView.cs index 091661514..b770037a3 100644 --- a/Source/Engine/Graphics/RenderView.cs +++ b/Source/Engine/Graphics/RenderView.cs @@ -102,6 +102,8 @@ namespace FlaxEngine NonJitteredProjection = Projection; TemporalAAJitter = Float4.Zero; RenderLayersMask = camera.RenderLayersMask; + Flags = camera.RenderFlags; + Mode = camera.RenderMode; UpdateCachedData(); } diff --git a/Source/Engine/Graphics/RenderView.h b/Source/Engine/Graphics/RenderView.h index 5cfe06a8d..e72ebaa45 100644 --- a/Source/Engine/Graphics/RenderView.h +++ b/Source/Engine/Graphics/RenderView.h @@ -295,10 +295,12 @@ public: /// Camera's FOV angle (in degrees) void SetProjector(float nearPlane, float farPlane, const Float3& position, const Float3& direction, const Float3& up, float angle); - // Copy view data from camera - // @param camera Camera to copy its data - // @param camera The custom viewport to use for view/projection matrices override. - void CopyFrom(Camera* camera, Viewport* viewport = nullptr); + /// + /// Copies view data from camera to the view. + /// + /// The camera to copy its data. + /// The custom viewport to use for view/projection matrices override. + void CopyFrom(const Camera* camera, const Viewport* viewport = nullptr); public: FORCE_INLINE DrawPass GetShadowsDrawPassMask(ShadowsCastingMode shadowsMode) const diff --git a/Source/Engine/Level/Actors/Camera.cpp b/Source/Engine/Level/Actors/Camera.cpp index f2625ca3b..5c5107803 100644 --- a/Source/Engine/Level/Actors/Camera.cpp +++ b/Source/Engine/Level/Actors/Camera.cpp @@ -415,6 +415,8 @@ void Camera::Serialize(SerializeStream& stream, const void* otherObj) SERIALIZE_MEMBER(Far, _far); SERIALIZE_MEMBER(OrthoScale, _orthoScale); SERIALIZE(RenderLayersMask); + SERIALIZE(RenderFlags); + SERIALIZE(RenderMode); } void Camera::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) @@ -429,6 +431,8 @@ void Camera::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier DESERIALIZE_MEMBER(Far, _far); DESERIALIZE_MEMBER(OrthoScale, _orthoScale); DESERIALIZE(RenderLayersMask); + DESERIALIZE(RenderFlags); + DESERIALIZE(RenderMode); } void Camera::OnEnable() diff --git a/Source/Engine/Level/Actors/Camera.h b/Source/Engine/Level/Actors/Camera.h index 5a0bece33..22d950dd4 100644 --- a/Source/Engine/Level/Actors/Camera.h +++ b/Source/Engine/Level/Actors/Camera.h @@ -7,6 +7,7 @@ #include "Engine/Core/Math/Viewport.h" #include "Engine/Core/Math/Ray.h" #include "Engine/Core/Types/LayersMask.h" +#include "Engine/Graphics/Enums.h" #include "Engine/Scripting/ScriptingObjectReference.h" #if USE_EDITOR #include "Engine/Content/AssetReference.h" @@ -134,6 +135,18 @@ public: API_FIELD(Attributes="EditorOrder(100), EditorDisplay(\"Camera\")") LayersMask RenderLayersMask; + /// + /// Frame rendering flags used to switch between graphics features for this camera. + /// + API_FIELD(Attributes = "EditorOrder(110), EditorDisplay(\"Camera\")") + ViewFlags RenderFlags = ViewFlags::DefaultGame; + + /// + /// Describes frame rendering modes for this camera. + /// + API_FIELD(Attributes = "EditorOrder(120), EditorDisplay(\"Camera\")") + ViewMode RenderMode = ViewMode::Default; + public: /// /// Projects the point from 3D world-space to game window coordinates (in screen pixels for default viewport calculated from ). diff --git a/Source/Engine/Tools/AudioTool/AudioTool.h b/Source/Engine/Tools/AudioTool/AudioTool.h index cd61ee37a..d7e28df57 100644 --- a/Source/Engine/Tools/AudioTool/AudioTool.h +++ b/Source/Engine/Tools/AudioTool/AudioTool.h @@ -79,6 +79,7 @@ public: void Serialize(SerializeStream& stream, const void* otherObj) override; void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) override; }; +#endif public: /// @@ -127,7 +128,6 @@ public: { return (input[2] << 24) | (input[1] << 16) | (input[0] << 8); } -#endif }; #endif diff --git a/Source/Engine/UI/GUI/Common/Button.cs b/Source/Engine/UI/GUI/Common/Button.cs index b50f3dd46..02337411b 100644 --- a/Source/Engine/UI/GUI/Common/Button.cs +++ b/Source/Engine/UI/GUI/Common/Button.cs @@ -314,6 +314,28 @@ namespace FlaxEngine.GUI return false; } + /// + public override bool OnMouseDoubleClick(Float2 location, MouseButton button) + { + if (base.OnMouseDoubleClick(location, button)) + return true; + + if (button == MouseButton.Left && _isPressed) + { + OnPressEnd(); + OnClick(); + return true; + } + + if (button == MouseButton.Left && !_isPressed) + { + OnPressBegin(); + return true; + } + + return false; + } + /// public override bool OnTouchDown(Float2 location, int pointerId) { diff --git a/Source/Engine/UI/GUI/Common/CheckBox.cs b/Source/Engine/UI/GUI/Common/CheckBox.cs index 2f1ff42a9..2128c0311 100644 --- a/Source/Engine/UI/GUI/Common/CheckBox.cs +++ b/Source/Engine/UI/GUI/Common/CheckBox.cs @@ -277,6 +277,27 @@ namespace FlaxEngine.GUI return base.OnMouseDown(location, button); } + /// + public override bool OnMouseDoubleClick(Float2 location, MouseButton button) + { + if (button == MouseButton.Left && !_isPressed) + { + OnPressBegin(); + return true; + } + + if (button == MouseButton.Left && _isPressed) + { + OnPressEnd(); + if (_box.Contains(ref location)) + { + OnClick(); + return true; + } + } + return base.OnMouseDoubleClick(location, button); + } + /// public override bool OnMouseUp(Float2 location, MouseButton button) { diff --git a/Source/Engine/UI/GUI/Common/Dropdown.cs b/Source/Engine/UI/GUI/Common/Dropdown.cs index 6ad962d6d..aea4e173a 100644 --- a/Source/Engine/UI/GUI/Common/Dropdown.cs +++ b/Source/Engine/UI/GUI/Common/Dropdown.cs @@ -666,6 +666,30 @@ namespace FlaxEngine.GUI return false; } + /// + public override bool OnMouseDoubleClick(Float2 location, MouseButton button) + { + if (base.OnMouseDoubleClick(location, button)) + return true; + + if (_touchDown && button == MouseButton.Left) + { + _touchDown = false; + ShowPopup(); + return true; + } + + if (button == MouseButton.Left) + { + _touchDown = true; + if (!IsPopupOpened) + Focus(); + return true; + } + + return false; + } + /// public override bool OnTouchDown(Float2 location, int pointerId) { diff --git a/Source/Engine/UI/GUI/Panels/DropPanel.cs b/Source/Engine/UI/GUI/Panels/DropPanel.cs index d650d6205..66e7413eb 100644 --- a/Source/Engine/UI/GUI/Panels/DropPanel.cs +++ b/Source/Engine/UI/GUI/Panels/DropPanel.cs @@ -501,6 +501,29 @@ namespace FlaxEngine.GUI return false; } + /// + public override bool OnMouseDoubleClick(Float2 location, MouseButton button) + { + if (base.OnMouseDoubleClick(location, button)) + return true; + + _mouseOverHeader = HeaderRectangle.Contains(location); + if (button == MouseButton.Left && _mouseOverHeader) + { + _mouseButtonLeftDown = true; + return true; + } + + if (button == MouseButton.Left && _mouseButtonLeftDown) + { + _mouseButtonLeftDown = false; + if (_mouseOverHeader) + Toggle(); + return true; + } + return false; + } + /// public override void OnMouseLeave() { diff --git a/Source/Engine/UI/GUI/Panels/ScrollBar.cs b/Source/Engine/UI/GUI/Panels/ScrollBar.cs index a51a1df40..abde37b37 100644 --- a/Source/Engine/UI/GUI/Panels/ScrollBar.cs +++ b/Source/Engine/UI/GUI/Panels/ScrollBar.cs @@ -23,9 +23,9 @@ namespace FlaxEngine.GUI // Scrolling - private float _clickChange = 20, _scrollChange = 100; + private float _clickChange = 20, _scrollChange = 50; private float _minimum, _maximum = 100; - private float _value, _targetValue; + private float _startValue, _value, _targetValue; private readonly Orientation _orientation; private RootControl.UpdateDelegate _update; @@ -42,6 +42,7 @@ namespace FlaxEngine.GUI // Smoothing private float _thumbOpacity = DefaultMinimumOpacity; + private float _scrollAnimationProgress = 0f; /// /// Gets the orientation. @@ -59,14 +60,19 @@ namespace FlaxEngine.GUI public float TrackThickness { get; set; } = 2.0f; /// - /// Gets or sets the value smoothing scale (0 to not use it). + /// The maximum time it takes to animate from current to target scroll position /// - public float SmoothingScale { get; set; } = 0.6f; + public float ScrollAnimationDuration { get; set; } = 0.18f; /// /// Gets a value indicating whether use scroll value smoothing. /// - public bool UseSmoothing => !Mathf.IsZero(SmoothingScale); + public bool UseSmoothing => EnableSmoothing && !Mathf.IsZero(ScrollAnimationDuration); + + /// + /// Enables scroll smoothing + /// + public bool EnableSmoothing { get; set; } = true; /// /// Gets or sets the minimum value. @@ -112,11 +118,15 @@ namespace FlaxEngine.GUI if (!Mathf.NearEqual(value, _targetValue)) { _targetValue = value; + _startValue = _value; + _scrollAnimationProgress = 0f; // Check if skip smoothing if (!UseSmoothing) { _value = value; + _startValue = value; + _scrollAnimationProgress = 1f; OnValueChanged(); } else @@ -208,7 +218,8 @@ namespace FlaxEngine.GUI { if (!Mathf.NearEqual(_value, _targetValue)) { - _value = _targetValue; + _value = _targetValue = _startValue; + _scrollAnimationProgress = 0f; SetUpdate(ref _update, null); OnValueChanged(); } @@ -274,7 +285,8 @@ namespace FlaxEngine.GUI internal void Reset() { - _value = _targetValue = 0; + _value = _targetValue = _startValue = 0; + _scrollAnimationProgress = 0f; } /// @@ -296,22 +308,39 @@ namespace FlaxEngine.GUI _thumbOpacity = isDeltaSlow ? targetOpacity : Mathf.Lerp(_thumbOpacity, targetOpacity, deltaTime * 10.0f); bool needUpdate = Mathf.Abs(_thumbOpacity - targetOpacity) > 0.001f; - // Ensure scroll bar is visible - if (Visible) + // Ensure scroll bar is visible and smoothing is required + if (Visible && Mathf.Abs(_targetValue - _value) > 0.01f) { - // Value smoothing - if (Mathf.Abs(_targetValue - _value) > 0.01f) + // Interpolate or not if running slow + float value; + if (!isDeltaSlow && UseSmoothing) { - // Interpolate or not if running slow - float value; - if (!isDeltaSlow && UseSmoothing) - value = Mathf.Lerp(_value, _targetValue, deltaTime * 20.0f * SmoothingScale); - else - value = _targetValue; - _value = value; - OnValueChanged(); - needUpdate = true; + // percentage of scroll from 0 to _scrollChange, ex. 0.5 at _scrollChange / 2 + var minScrollChangeRatio = Mathf.Clamp(Mathf.Abs(_targetValue - _startValue) / _scrollChange, 0, 1); + + // shorten the duration if we scrolled less than _scrollChange + var actualDuration = ScrollAnimationDuration * minScrollChangeRatio; + var step = deltaTime / actualDuration; + + var progress = _scrollAnimationProgress; + progress = Mathf.Clamp(progress + step, 0, 1); + + // https://easings.net/#easeOutSine + var easedProgress = Mathf.Sin((progress * Mathf.Pi) / 2); + value = Mathf.Lerp(_startValue, _targetValue, easedProgress); + + _scrollAnimationProgress = progress; } + else + { + value = _targetValue; + _startValue = _targetValue; + _scrollAnimationProgress = 0f; + } + + _value = value; + OnValueChanged(); + needUpdate = true; } // End updating if all animations are done @@ -371,7 +400,7 @@ namespace FlaxEngine.GUI float mousePosition = _orientation == Orientation.Vertical ? slidePosition.Y : slidePosition.X; float percentage = (mousePosition - _mouseOffset - _thumbSize / 2) / (TrackSize - _thumbSize); - Value = _minimum + percentage * (_maximum - _minimum); + TargetValue = _minimum + percentage * (_maximum - _minimum); } } @@ -381,7 +410,7 @@ namespace FlaxEngine.GUI if (ThumbEnabled) { // Scroll - Value = _value - delta * _scrollChange; + Value = _targetValue - delta * _scrollChange; } return true; }