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;
}