diff --git a/Source/Editor/GUI/Docking/DockPanelProxy.cs b/Source/Editor/GUI/Docking/DockPanelProxy.cs index 15ff2cad0..e6e57de8e 100644 --- a/Source/Editor/GUI/Docking/DockPanelProxy.cs +++ b/Source/Editor/GUI/Docking/DockPanelProxy.cs @@ -13,6 +13,7 @@ namespace FlaxEditor.GUI.Docking public class DockPanelProxy : ContainerControl { private DockPanel _panel; + private double _dragEnterTime = -1; /// /// The is mouse down flag (left button). @@ -256,8 +257,8 @@ namespace FlaxEditor.GUI.Docking else { tabColor = style.BackgroundHighlighted; - Render2D.DrawLine(tabRect.BottomLeft - new Float2(0 , 1), tabRect.UpperLeft, tabColor); - Render2D.DrawLine(tabRect.BottomRight - new Float2(0 , 1), tabRect.UpperRight, tabColor); + Render2D.DrawLine(tabRect.BottomLeft - new Float2(0, 1), tabRect.UpperLeft, tabColor); + Render2D.DrawLine(tabRect.BottomRight - new Float2(0, 1), tabRect.UpperRight, tabColor); } if (tab.Icon.IsValid) @@ -477,11 +478,7 @@ namespace FlaxEditor.GUI.Docking var result = base.OnDragEnter(ref location, data); if (result != DragDropEffect.None) return result; - - if (TrySelectTabUnderLocation(ref location)) - return DragDropEffect.Move; - - return DragDropEffect.None; + return TrySelectTabUnderLocation(ref location); } /// @@ -490,11 +487,15 @@ namespace FlaxEditor.GUI.Docking var result = base.OnDragMove(ref location, data); if (result != DragDropEffect.None) return result; + return TrySelectTabUnderLocation(ref location); + } - if (TrySelectTabUnderLocation(ref location)) - return DragDropEffect.Move; + /// + public override void OnDragLeave() + { + _dragEnterTime = -1; - return DragDropEffect.None; + base.OnDragLeave(); } /// @@ -503,17 +504,25 @@ namespace FlaxEditor.GUI.Docking rect = new Rectangle(0, DockPanel.DefaultHeaderHeight, Width, Height - DockPanel.DefaultHeaderHeight); } - private bool TrySelectTabUnderLocation(ref Float2 location) + private DragDropEffect TrySelectTabUnderLocation(ref Float2 location) { var tab = GetTabAtPos(location, out _); if (tab != null) { + // Auto-select tab only if drag takes some time + var time = Platform.TimeSeconds; + if (_dragEnterTime < 0) + _dragEnterTime = time; + if (time - _dragEnterTime < 0.3f) + return DragDropEffect.Link; + _dragEnterTime = -1; + _panel.SelectTab(tab); Update(0); // Fake update - return true; + return DragDropEffect.Move; } - - return false; + _dragEnterTime = -1; + return DragDropEffect.None; } private void ShowContextMenu(DockWindow tab, ref Float2 location) diff --git a/Source/Editor/Gizmo/IGizmoOwner.cs b/Source/Editor/Gizmo/IGizmoOwner.cs index ff653196e..3ab0a917b 100644 --- a/Source/Editor/Gizmo/IGizmoOwner.cs +++ b/Source/Editor/Gizmo/IGizmoOwner.cs @@ -106,5 +106,11 @@ namespace FlaxEditor.Gizmo /// /// The nodes to select void Select(List nodes); + + /// + /// Spawns the actor in the viewport hierarchy. + /// + /// The new actor to spawn. + void Spawn(Actor actor); } } diff --git a/Source/Editor/Surface/Archetypes/Parameters.cs b/Source/Editor/Surface/Archetypes/Parameters.cs index 6ed5a61e6..4cf1af4f6 100644 --- a/Source/Editor/Surface/Archetypes/Parameters.cs +++ b/Source/Editor/Surface/Archetypes/Parameters.cs @@ -1084,7 +1084,7 @@ namespace FlaxEditor.Surface.Archetypes }, Elements = new[] { - NodeElementArchetype.Factory.Input(0, string.Empty, true, typeof(void), 0), + NodeElementArchetype.Factory.Input(0, string.Empty, false, typeof(void), 0), NodeElementArchetype.Factory.Input(1, string.Empty, true, ScriptType.Null, 1, 1), NodeElementArchetype.Factory.Output(0, string.Empty, typeof(void), 2, true), NodeElementArchetype.Factory.ComboBox(2 + 20, 0, 116) diff --git a/Source/Editor/Viewport/EditorGizmoViewport.cs b/Source/Editor/Viewport/EditorGizmoViewport.cs index a24dd2f38..fe579e7e5 100644 --- a/Source/Editor/Viewport/EditorGizmoViewport.cs +++ b/Source/Editor/Viewport/EditorGizmoViewport.cs @@ -86,6 +86,9 @@ namespace FlaxEditor.Viewport /// public abstract void Select(List nodes); + /// + public abstract void Spawn(Actor actor); + /// protected override bool IsControllingMouse => Gizmos.Active?.IsControllingMouse ?? false; diff --git a/Source/Editor/Viewport/MainEditorGizmoViewport.cs b/Source/Editor/Viewport/MainEditorGizmoViewport.cs index e85b6c2b3..24c8f1c4c 100644 --- a/Source/Editor/Viewport/MainEditorGizmoViewport.cs +++ b/Source/Editor/Viewport/MainEditorGizmoViewport.cs @@ -5,9 +5,7 @@ using System.Collections.Generic; using FlaxEditor.Content; using FlaxEditor.Gizmo; using FlaxEditor.GUI.ContextMenu; -using FlaxEditor.GUI.Drag; using FlaxEditor.SceneGraph; -using FlaxEditor.SceneGraph.Actors; using FlaxEditor.Scripting; using FlaxEditor.Viewport.Cameras; using FlaxEditor.Viewport.Modes; @@ -37,28 +35,8 @@ namespace FlaxEditor.Viewport private readonly ViewportWidgetButton _rotateSnapping; private readonly ViewportWidgetButton _scaleSnapping; - private readonly DragAssets _dragAssets; - private readonly DragActorType _dragActorType = new DragActorType(ValidateDragActorType); - private SelectionOutline _customSelectionOutline; - /// - /// The custom drag drop event arguments. - /// - /// - public class DragDropEventArgs : DragEventArgs - { - /// - /// The hit. - /// - public SceneGraphNode Hit; - - /// - /// The hit location. - /// - public Vector3 HitLocation; - } - /// /// The editor sprites rendering effect. /// @@ -137,15 +115,12 @@ namespace FlaxEditor.Viewport private bool _lockedFocus; private double _lockedFocusOffset; private readonly ViewportDebugDrawData _debugDrawData = new ViewportDebugDrawData(32); - private StaticModel _previewStaticModel; - private int _previewModelEntryIndex; - private BrushSurface _previewBrushSurface; private EditorSpritesRenderer _editorSpritesRenderer; /// /// Drag and drop handlers /// - public readonly DragHandlers DragHandlers = new DragHandlers(); + public readonly ViewportDragHandlers DragHandlers; /// /// The transform gizmo. @@ -219,7 +194,7 @@ namespace FlaxEditor.Viewport : base(Object.New(), editor.Undo, editor.Scene.Root) { _editor = editor; - _dragAssets = new DragAssets(ValidateDragItem); + DragHandlers = new ViewportDragHandlers(this, this, ValidateDragItem, ValidateDragActorType); var inputOptions = editor.Options.Options.Input; // Prepare rendering task @@ -408,9 +383,6 @@ namespace FlaxEditor.Viewport ViewWidgetButtonMenu.AddSeparator(); ViewWidgetButtonMenu.AddButton("Create camera here", CreateCameraAtView); - DragHandlers.Add(_dragActorType); - DragHandlers.Add(_dragAssets); - // Init gizmo modes { // Add default modes used by the editor @@ -430,7 +402,11 @@ namespace FlaxEditor.Viewport InputActions.Add(options => options.TranslateMode, () => TransformGizmo.ActiveMode = TransformGizmoBase.Mode.Translate); InputActions.Add(options => options.RotateMode, () => TransformGizmo.ActiveMode = TransformGizmoBase.Mode.Rotate); InputActions.Add(options => options.ScaleMode, () => TransformGizmo.ActiveMode = TransformGizmoBase.Mode.Scale); - InputActions.Add(options => options.ToggleTransformSpace, () => { OnTransformSpaceToggle(transformSpaceToggle); transformSpaceToggle.Checked = !transformSpaceToggle.Checked; }); + InputActions.Add(options => options.ToggleTransformSpace, () => + { + OnTransformSpaceToggle(transformSpaceToggle); + transformSpaceToggle.Checked = !transformSpaceToggle.Checked; + }); InputActions.Add(options => options.LockFocusSelection, LockFocusSelection); InputActions.Add(options => options.FocusSelection, FocusSelection); InputActions.Add(options => options.RotateSelection, RotateSelection); @@ -530,20 +506,9 @@ namespace FlaxEditor.Viewport private void OnCollectDrawCalls(ref RenderContext renderContext) { - if (_previewStaticModel) - { - _debugDrawData.HighlightModel(_previewStaticModel, _previewModelEntryIndex); - } - if (_previewBrushSurface.Brush != null) - { - _debugDrawData.HighlightBrushSurface(_previewBrushSurface); - } - + DragHandlers.CollectDrawCalls(_debugDrawData, ref renderContext); if (ShowNavigation) - { Editor.Internal_DrawNavMesh(); - } - _debugDrawData.OnDraw(ref renderContext); } @@ -942,78 +907,14 @@ namespace FlaxEditor.Viewport base.OnLeftMouseButtonUp(); } - private void GetHitLocation(ref Float2 location, out SceneGraphNode hit, out Vector3 hitLocation, out Vector3 hitNormal) - { - // Get mouse ray and try to hit any object - var ray = ConvertMouseToRay(ref location); - var view = new Ray(ViewPosition, ViewDirection); - var gridPlane = new Plane(Vector3.Zero, Vector3.Up); - var flags = SceneGraphNode.RayCastData.FlagTypes.SkipColliders | SceneGraphNode.RayCastData.FlagTypes.SkipEditorPrimitives; - hit = Editor.Instance.Scene.Root.RayCast(ref ray, ref view, out var closest, out var normal, flags); - if (hit != null) - { - // Use hit location - hitLocation = ray.Position + ray.Direction * closest; - hitNormal = normal; - } - else if (Grid.Enabled && CollisionsHelper.RayIntersectsPlane(ref ray, ref gridPlane, out closest) && closest < 4000.0f) - { - // Use grid location - hitLocation = ray.Position + ray.Direction * closest; - hitNormal = Vector3.Up; - } - else - { - // Use area in front of the viewport - hitLocation = ViewPosition + ViewDirection * 100; - hitNormal = Vector3.Up; - } - } - - private void SetDragEffects(ref Float2 location) - { - if (_dragAssets.HasValidDrag && _dragAssets.Objects[0].IsOfType()) - { - GetHitLocation(ref location, out var hit, out _, out _); - ClearDragEffects(); - var material = FlaxEngine.Content.LoadAsync(_dragAssets.Objects[0].ID); - if (material.IsDecal) - return; - - if (hit is StaticModelNode staticModelNode) - { - _previewStaticModel = (StaticModel)staticModelNode.Actor; - var ray = ConvertMouseToRay(ref location); - _previewStaticModel.IntersectsEntry(ref ray, out _, out _, out _previewModelEntryIndex); - } - else if (hit is BoxBrushNode.SideLinkNode brushSurfaceNode) - { - _previewBrushSurface = brushSurfaceNode.Surface; - } - } - } - - private void ClearDragEffects() - { - _previewStaticModel = null; - _previewModelEntryIndex = -1; - _previewBrushSurface = new BrushSurface(); - } - /// public override DragDropEffect OnDragEnter(ref Float2 location, DragData data) { - ClearDragEffects(); - + DragHandlers.ClearDragEffects(); var result = base.OnDragEnter(ref location, data); if (result != DragDropEffect.None) return result; - - result = DragHandlers.OnDragEnter(data); - - SetDragEffects(ref location); - - return result; + return DragHandlers.DragEnter(ref location, data); } private bool ValidateDragItem(ContentItem contentItem) @@ -1042,167 +943,29 @@ namespace FlaxEditor.Viewport /// public override DragDropEffect OnDragMove(ref Float2 location, DragData data) { - ClearDragEffects(); - + DragHandlers.ClearDragEffects(); var result = base.OnDragMove(ref location, data); if (result != DragDropEffect.None) return result; - - SetDragEffects(ref location); - - return DragHandlers.Effect; + return DragHandlers.DragEnter(ref location, data); } /// public override void OnDragLeave() { - ClearDragEffects(); - + DragHandlers.ClearDragEffects(); DragHandlers.OnDragLeave(); - base.OnDragLeave(); } - private Vector3 PostProcessSpawnedActorLocation(Actor actor, ref Vector3 hitLocation) - { - // Refresh actor position to ensure that cached bounds are valid - actor.Position = Vector3.One; - actor.Position = Vector3.Zero; - - // Place the object - //var location = hitLocation - (box.Size.Length * 0.5f) * ViewDirection; - var editorBounds = actor.EditorBoxChildren; - var bottomToCenter = actor.Position.Y - editorBounds.Minimum.Y; - var location = hitLocation + new Vector3(0, bottomToCenter, 0); - - // Apply grid snapping if enabled - if (UseSnapping || TransformGizmo.TranslationSnapEnable) - { - float snapValue = TransformGizmo.TranslationSnapValue; - location = new Vector3( - (int)(location.X / snapValue) * snapValue, - (int)(location.Y / snapValue) * snapValue, - (int)(location.Z / snapValue) * snapValue); - } - - return location; - } - - private void Spawn(Actor actor, ref Vector3 hitLocation, ref Vector3 hitNormal) - { - actor.Position = PostProcessSpawnedActorLocation(actor, ref hitLocation); - var parent = actor.Parent ?? Level.GetScene(0); - actor.Name = Utilities.Utils.IncrementNameNumber(actor.Name, x => parent.GetChild(x) == null); - Editor.Instance.SceneEditing.Spawn(actor); - Focus(); - } - - private void Spawn(AssetItem item, SceneGraphNode hit, ref Float2 location, ref Vector3 hitLocation, ref Vector3 hitNormal) - { - if (item.IsOfType()) - { - var material = FlaxEngine.Content.LoadAsync(item.ID); - if (material && !material.WaitForLoaded(100) && material.IsDecal) - { - var actor = new Decal - { - Material = material, - LocalOrientation = RootNode.RaycastNormalRotation(ref hitNormal), - Name = item.ShortName - }; - DebugDraw.DrawWireArrow(PostProcessSpawnedActorLocation(actor, ref hitNormal), actor.LocalOrientation, 1.0f, Color.Red, 1000000); - Spawn(actor, ref hitLocation, ref hitNormal); - } - else if (hit is StaticModelNode staticModelNode) - { - var staticModel = (StaticModel)staticModelNode.Actor; - var ray = ConvertMouseToRay(ref location); - if (staticModel.IntersectsEntry(ref ray, out _, out _, out var entryIndex)) - { - using (new UndoBlock(Undo, staticModel, "Change material")) - staticModel.SetMaterial(entryIndex, material); - } - } - else if (hit is BoxBrushNode.SideLinkNode brushSurfaceNode) - { - using (new UndoBlock(Undo, brushSurfaceNode.Brush, "Change material")) - { - var surface = brushSurfaceNode.Surface; - surface.Material = material; - brushSurfaceNode.Surface = surface; - } - } - return; - } - if (item.IsOfType()) - { - Editor.Instance.Scene.OpenScene(item.ID, true); - return; - } - { - var actor = item.OnEditorDrop(this); - actor.Name = item.ShortName; - Spawn(actor, ref hitLocation, ref hitNormal); - } - } - - private void Spawn(ScriptType item, SceneGraphNode hit, ref Float2 location, ref Vector3 hitLocation, ref Vector3 hitNormal) - { - var actor = item.CreateInstance() as Actor; - if (actor == null) - { - Editor.LogWarning("Failed to spawn actor of type " + item.TypeName); - return; - } - actor.Name = item.Name; - Spawn(actor, ref hitLocation, ref hitNormal); - } - /// public override DragDropEffect OnDragDrop(ref Float2 location, DragData data) { - ClearDragEffects(); - + DragHandlers.ClearDragEffects(); var result = base.OnDragDrop(ref location, data); if (result != DragDropEffect.None) return result; - - // Check if drag sth - Vector3 hitLocation = ViewPosition, hitNormal = -ViewDirection; - SceneGraphNode hit = null; - if (DragHandlers.HasValidDrag) - { - GetHitLocation(ref location, out hit, out hitLocation, out hitNormal); - } - - // Drag assets - if (_dragAssets.HasValidDrag) - { - result = _dragAssets.Effect; - - // Process items - for (int i = 0; i < _dragAssets.Objects.Count; i++) - { - var item = _dragAssets.Objects[i]; - Spawn(item, hit, ref location, ref hitLocation, ref hitNormal); - } - } - // Drag actor type - else if (_dragActorType.HasValidDrag) - { - result = _dragActorType.Effect; - - // Process items - for (int i = 0; i < _dragActorType.Objects.Count; i++) - { - var item = _dragActorType.Objects[i]; - Spawn(item, hit, ref location, ref hitLocation, ref hitNormal); - } - } - - DragHandlers.OnDragDrop(new DragDropEventArgs { Hit = hit, HitLocation = hitLocation }); - - return result; + return DragHandlers.DragDrop(ref location, data); } /// @@ -1211,6 +974,14 @@ namespace FlaxEditor.Viewport _editor.SceneEditing.Select(nodes); } + /// + public override void Spawn(Actor actor) + { + var parent = actor.Parent ?? Level.GetScene(0); + actor.Name = Utilities.Utils.IncrementNameNumber(actor.Name, x => parent.GetChild(x) == null); + Editor.Instance.SceneEditing.Spawn(actor); + } + /// public override void OnDestroy() { diff --git a/Source/Editor/Viewport/PrefabWindowViewport.cs b/Source/Editor/Viewport/PrefabWindowViewport.cs index eb462deb1..6e111f588 100644 --- a/Source/Editor/Viewport/PrefabWindowViewport.cs +++ b/Source/Editor/Viewport/PrefabWindowViewport.cs @@ -5,9 +5,7 @@ using System.Collections.Generic; using FlaxEditor.Content; using FlaxEditor.Gizmo; using FlaxEditor.GUI.ContextMenu; -using FlaxEditor.GUI.Drag; using FlaxEditor.SceneGraph; -using FlaxEditor.SceneGraph.Actors; using FlaxEditor.Scripting; using FlaxEditor.Viewport.Cameras; using FlaxEditor.Viewport.Previews; @@ -56,9 +54,11 @@ namespace FlaxEditor.Viewport private readonly ViewportDebugDrawData _debugDrawData = new ViewportDebugDrawData(32); private PrefabSpritesRenderer _spritesRenderer; - private readonly DragAssets _dragAssets; - private readonly DragActorType _dragActorType = new DragActorType(ValidateDragActorType); - private readonly DragHandlers _dragHandlers = new DragHandlers(); + + /// + /// Drag and drop handlers + /// + public readonly ViewportDragHandlers DragHandlers; /// /// The transform gizmo. @@ -81,7 +81,7 @@ namespace FlaxEditor.Viewport _window.SelectionChanged += OnSelectionChanged; Undo = window.Undo; ViewportCamera = new FPSCamera(); - _dragAssets = new DragAssets(ValidateDragItem); + DragHandlers = new ViewportDragHandlers(this, this, ValidateDragItem, ValidateDragActorType); ShowDebugDraw = true; ShowEditorPrimitives = true; Gizmos = new GizmosCollection(this); @@ -228,14 +228,15 @@ namespace FlaxEditor.Viewport _gizmoModeScale.Toggled += OnGizmoModeToggle; gizmoMode.Parent = this; - _dragHandlers.Add(_dragActorType); - _dragHandlers.Add(_dragAssets); - // Setup input actions InputActions.Add(options => options.TranslateMode, () => TransformGizmo.ActiveMode = TransformGizmoBase.Mode.Translate); InputActions.Add(options => options.RotateMode, () => TransformGizmo.ActiveMode = TransformGizmoBase.Mode.Rotate); InputActions.Add(options => options.ScaleMode, () => TransformGizmo.ActiveMode = TransformGizmoBase.Mode.Scale); - InputActions.Add(options => options.ToggleTransformSpace, () => { OnTransformSpaceToggle(transformSpaceToggle); transformSpaceToggle.Checked = !transformSpaceToggle.Checked; }); + InputActions.Add(options => options.ToggleTransformSpace, () => + { + OnTransformSpaceToggle(transformSpaceToggle); + transformSpaceToggle.Checked = !transformSpaceToggle.Checked; + }); InputActions.Add(options => options.FocusSelection, ShowSelectedActors); SetUpdate(ref _update, OnUpdate); @@ -267,6 +268,7 @@ namespace FlaxEditor.Viewport private void OnCollectDrawCalls(ref RenderContext renderContext) { + DragHandlers.CollectDrawCalls(_debugDrawData, ref renderContext); _debugDrawData.OnDraw(ref renderContext); } @@ -306,6 +308,7 @@ namespace FlaxEditor.Viewport var orient = ViewOrientation; ((FPSCamera)ViewportCamera).ShowActors(TransformGizmo.SelectedParents, ref orient); } + /// public EditorViewport Viewport => this; @@ -354,6 +357,12 @@ namespace FlaxEditor.Viewport _window.Select(nodes); } + /// + public void Spawn(Actor actor) + { + _window.Spawn(actor); + } + /// protected override bool IsControllingMouse => Gizmos.Active?.IsControllingMouse ?? false; @@ -669,11 +678,11 @@ namespace FlaxEditor.Viewport /// public override DragDropEffect OnDragEnter(ref Float2 location, DragData data) { + DragHandlers.ClearDragEffects(); var result = base.OnDragEnter(ref location, data); if (result != DragDropEffect.None) return result; - - return _dragHandlers.OnDragEnter(data); + return DragHandlers.DragEnter(ref location, data); } private bool ValidateDragItem(ContentItem contentItem) @@ -685,7 +694,6 @@ namespace FlaxEditor.Viewport if (assetItem.IsOfType()) return true; } - return false; } @@ -697,86 +705,21 @@ namespace FlaxEditor.Viewport /// public override DragDropEffect OnDragMove(ref Float2 location, DragData data) { + DragHandlers.ClearDragEffects(); var result = base.OnDragMove(ref location, data); if (result != DragDropEffect.None) return result; - - return _dragHandlers.Effect; + return DragHandlers.DragEnter(ref location, data); } /// public override void OnDragLeave() { - _dragHandlers.OnDragLeave(); - + DragHandlers.ClearDragEffects(); + DragHandlers.OnDragLeave(); base.OnDragLeave(); } - private Vector3 PostProcessSpawnedActorLocation(Actor actor, ref Vector3 hitLocation) - { - // Place the object - //var location = hitLocation - (box.Size.Length * 0.5f) * ViewDirection; - var location = hitLocation; - - // Apply grid snapping if enabled - if (UseSnapping || TransformGizmo.TranslationSnapEnable) - { - float snapValue = TransformGizmo.TranslationSnapValue; - location = new Vector3( - (int)(location.X / snapValue) * snapValue, - (int)(location.Y / snapValue) * snapValue, - (int)(location.Z / snapValue) * snapValue); - } - - return location; - } - - private void Spawn(AssetItem item, SceneGraphNode hit, ref Float2 location, ref Vector3 hitLocation) - { - if (item is BinaryAssetItem binaryAssetItem) - { - if (typeof(MaterialBase).IsAssignableFrom(binaryAssetItem.Type)) - { - if (hit is StaticModelNode staticModelNode) - { - var staticModel = (StaticModel)staticModelNode.Actor; - var ray = ConvertMouseToRay(ref location); - if (staticModel.IntersectsEntry(ref ray, out _, out _, out var entryIndex)) - { - var material = FlaxEngine.Content.LoadAsync(item.ID); - using (new UndoBlock(Undo, staticModel, "Change material")) - staticModel.SetMaterial(entryIndex, material); - } - } - return; - } - } - { - var actor = item.OnEditorDrop(this); - actor.Name = item.ShortName; - Spawn(actor, ref hitLocation); - } - } - - private void Spawn(Actor actor, ref Vector3 hitLocation) - { - actor.Position = PostProcessSpawnedActorLocation(actor, ref hitLocation); - _window.Spawn(actor); - Focus(); - } - - private void Spawn(ScriptType item, SceneGraphNode hit, ref Vector3 hitLocation) - { - var actor = item.CreateInstance() as Actor; - if (actor == null) - { - Editor.LogWarning("Failed to spawn actor of type " + item.TypeName); - return; - } - actor.Name = item.Name; - Spawn(actor, ref hitLocation); - } - /// /// Focuses the viewport on the current selection of the gizmo. /// @@ -814,57 +757,11 @@ namespace FlaxEditor.Viewport /// public override DragDropEffect OnDragDrop(ref Float2 location, DragData data) { + DragHandlers.ClearDragEffects(); var result = base.OnDragDrop(ref location, data); if (result != DragDropEffect.None) return result; - - // Check if drag sth - Vector3 hitLocation = ViewPosition; - SceneGraphNode hit = null; - if (_dragHandlers.HasValidDrag) - { - // Get mouse ray and try to hit any object - var ray = ConvertMouseToRay(ref location); - var view = new Ray(ViewPosition, ViewDirection); - hit = _window.Graph.Root.RayCast(ref ray, ref view, out var closest, SceneGraphNode.RayCastData.FlagTypes.SkipColliders); - if (hit != null) - { - // Use hit location - hitLocation = ray.Position + ray.Direction * closest; - } - else - { - // Use area in front of the viewport - hitLocation = ViewPosition + ViewDirection * 100; - } - } - - // Drag assets - if (_dragAssets.HasValidDrag) - { - result = _dragAssets.Effect; - - // Process items - for (int i = 0; i < _dragAssets.Objects.Count; i++) - { - var item = _dragAssets.Objects[i]; - Spawn(item, hit, ref location, ref hitLocation); - } - } - // Drag actor type - else if (_dragActorType.HasValidDrag) - { - result = _dragActorType.Effect; - - // Process items - for (int i = 0; i < _dragActorType.Objects.Count; i++) - { - var item = _dragActorType.Objects[i]; - Spawn(item, hit, ref hitLocation); - } - } - - return result; + return DragHandlers.DragDrop(ref location, data); } /// diff --git a/Source/Editor/Viewport/ViewportDraggingHelper.cs b/Source/Editor/Viewport/ViewportDraggingHelper.cs new file mode 100644 index 000000000..8a1b4f183 --- /dev/null +++ b/Source/Editor/Viewport/ViewportDraggingHelper.cs @@ -0,0 +1,257 @@ +// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. + +using System; +using System.Linq; +using FlaxEditor.Content; +using FlaxEditor.Gizmo; +using FlaxEditor.GUI.Drag; +using FlaxEditor.SceneGraph; +using FlaxEditor.SceneGraph.Actors; +using FlaxEditor.Scripting; +using FlaxEngine; +using FlaxEngine.GUI; + +namespace FlaxEditor.Viewport +{ + /// + /// Utility to help managing dragging assets, actors and other objects into the editor viewport. + /// + public class ViewportDragHandlers : DragHandlers + { + /// + /// The custom drag drop event arguments. + /// + /// + public class DragDropEventArgs : DragEventArgs + { + /// + /// The hit. + /// + public SceneGraphNode Hit; + + /// + /// The hit location. + /// + public Vector3 HitLocation; + } + + private readonly IGizmoOwner _owner; + private readonly EditorViewport _viewport; + private readonly DragAssets _dragAssets; + private readonly DragActorType _dragActorType; + + private StaticModel _previewStaticModel; + private int _previewModelEntryIndex; + private BrushSurface _previewBrushSurface; + + internal ViewportDragHandlers(IGizmoOwner owner, EditorViewport viewport, Func validateAsset, Func validateDragActorType) + { + _owner = owner; + _viewport = viewport; + Add(_dragAssets = new DragAssets(validateAsset)); + Add(_dragActorType = new DragActorType(validateDragActorType)); + } + + internal void ClearDragEffects() + { + _previewStaticModel = null; + _previewModelEntryIndex = -1; + _previewBrushSurface = new BrushSurface(); + } + + internal void CollectDrawCalls(ViewportDebugDrawData debugDrawData, ref RenderContext renderContext) + { + if (_previewStaticModel) + debugDrawData.HighlightModel(_previewStaticModel, _previewModelEntryIndex); + if (_previewBrushSurface.Brush != null) + debugDrawData.HighlightBrushSurface(_previewBrushSurface); + } + + internal DragDropEffect DragEnter(ref Float2 location, DragData data) + { + var result = OnDragEnter(data); + SetDragEffects(ref location); + return result; + } + + internal DragDropEffect DragMove(ref Float2 location, DragData data) + { + SetDragEffects(ref location); + return Effect; + } + + internal DragDropEffect DragDrop(ref Float2 location, DragData data) + { + Vector3 hitLocation = _viewport.ViewPosition, hitNormal = -_viewport.ViewDirection; + SceneGraphNode hit = null; + if (HasValidDrag) + { + GetHitLocation(ref location, out hit, out hitLocation, out hitNormal); + } + + var result = DragDropEffect.None; + if (_dragAssets.HasValidDrag) + { + result = _dragAssets.Effect; + foreach (var asset in _dragAssets.Objects) + Spawn(asset, hit, ref location, ref hitLocation, ref hitNormal); + } + else if (_dragActorType.HasValidDrag) + { + result = _dragActorType.Effect; + foreach (var actorType in _dragActorType.Objects) + Spawn(actorType, hit, ref location, ref hitLocation, ref hitNormal); + } + + OnDragDrop(new DragDropEventArgs { Hit = hit, HitLocation = hitLocation }); + + return result; + } + + private void SetDragEffects(ref Float2 location) + { + if (_dragAssets.HasValidDrag && _dragAssets.Objects[0].IsOfType()) + { + GetHitLocation(ref location, out var hit, out _, out _); + ClearDragEffects(); + var material = FlaxEngine.Content.LoadAsync(_dragAssets.Objects[0].ID); + if (material.IsDecal) + return; + + if (hit is StaticModelNode staticModelNode) + { + _previewStaticModel = (StaticModel)staticModelNode.Actor; + var ray = _viewport.ConvertMouseToRay(ref location); + _previewStaticModel.IntersectsEntry(ref ray, out _, out _, out _previewModelEntryIndex); + } + else if (hit is BoxBrushNode.SideLinkNode brushSurfaceNode) + { + _previewBrushSurface = brushSurfaceNode.Surface; + } + } + } + + private void GetHitLocation(ref Float2 location, out SceneGraphNode hit, out Vector3 hitLocation, out Vector3 hitNormal) + { + // Get mouse ray and try to hit any object + var ray = _viewport.ConvertMouseToRay(ref location); + var view = new Ray(_viewport.ViewPosition, _viewport.ViewDirection); + var gridPlane = new Plane(Vector3.Zero, Vector3.Up); + var flags = SceneGraphNode.RayCastData.FlagTypes.SkipColliders | SceneGraphNode.RayCastData.FlagTypes.SkipEditorPrimitives; + hit = _owner.SceneGraphRoot.RayCast(ref ray, ref view, out var closest, out var normal, flags); + var girdGizmo = (GridGizmo)_owner.Gizmos.FirstOrDefault(x => x is GridGizmo); + if (hit != null) + { + // Use hit location + hitLocation = ray.Position + ray.Direction * closest; + hitNormal = normal; + } + else if (girdGizmo != null && girdGizmo.Enabled && CollisionsHelper.RayIntersectsPlane(ref ray, ref gridPlane, out closest) && closest < 4000.0f) + { + // Use grid location + hitLocation = ray.Position + ray.Direction * closest; + hitNormal = Vector3.Up; + } + else + { + // Use area in front of the viewport + hitLocation = view.GetPoint(100); + hitNormal = Vector3.Up; + } + } + + private Vector3 PostProcessSpawnedActorLocation(Actor actor, ref Vector3 hitLocation) + { + // Refresh actor position to ensure that cached bounds are valid + actor.Position = Vector3.One; + actor.Position = Vector3.Zero; + + // Place the object + //var location = hitLocation - (box.Size.Length * 0.5f) * ViewDirection; + var editorBounds = actor.EditorBoxChildren; + var bottomToCenter = actor.Position.Y - editorBounds.Minimum.Y; + var location = hitLocation + new Vector3(0, bottomToCenter, 0); + + // Apply grid snapping if enabled + var transformGizmo = (TransformGizmo)_owner.Gizmos.FirstOrDefault(x => x is TransformGizmo); + if (transformGizmo != null && (_owner.UseSnapping || transformGizmo.TranslationSnapEnable)) + { + float snapValue = transformGizmo.TranslationSnapValue; + location = new Vector3( + (int)(location.X / snapValue) * snapValue, + (int)(location.Y / snapValue) * snapValue, + (int)(location.Z / snapValue) * snapValue); + } + + return location; + } + + private void Spawn(Actor actor, ref Vector3 hitLocation, ref Vector3 hitNormal) + { + actor.Position = PostProcessSpawnedActorLocation(actor, ref hitLocation); + _owner.Spawn(actor); + _viewport.Focus(); + } + + private void Spawn(ScriptType item, SceneGraphNode hit, ref Float2 location, ref Vector3 hitLocation, ref Vector3 hitNormal) + { + var actor = item.CreateInstance() as Actor; + if (actor == null) + { + Editor.LogWarning("Failed to spawn actor of type " + item.TypeName); + return; + } + actor.Name = item.Name; + Spawn(actor, ref hitLocation, ref hitNormal); + } + + private void Spawn(AssetItem item, SceneGraphNode hit, ref Float2 location, ref Vector3 hitLocation, ref Vector3 hitNormal) + { + if (item.IsOfType()) + { + var material = FlaxEngine.Content.LoadAsync(item.ID); + if (material && !material.WaitForLoaded(500) && material.IsDecal) + { + var actor = new Decal + { + Material = material, + LocalOrientation = RootNode.RaycastNormalRotation(ref hitNormal), + Name = item.ShortName + }; + DebugDraw.DrawWireArrow(PostProcessSpawnedActorLocation(actor, ref hitNormal), actor.LocalOrientation, 1.0f, Color.Red, 1000000); + Spawn(actor, ref hitLocation, ref hitNormal); + } + else if (hit is StaticModelNode staticModelNode) + { + var staticModel = (StaticModel)staticModelNode.Actor; + var ray = _viewport.ConvertMouseToRay(ref location); + if (staticModel.IntersectsEntry(ref ray, out _, out _, out var entryIndex)) + { + using (new UndoBlock(_owner.Undo, staticModel, "Change material")) + staticModel.SetMaterial(entryIndex, material); + } + } + else if (hit is BoxBrushNode.SideLinkNode brushSurfaceNode) + { + using (new UndoBlock(_owner.Undo, brushSurfaceNode.Brush, "Change material")) + { + var surface = brushSurfaceNode.Surface; + surface.Material = material; + brushSurfaceNode.Surface = surface; + } + } + return; + } + if (item.IsOfType()) + { + Editor.Instance.Scene.OpenScene(item.ID, true); + return; + } + { + var actor = item.OnEditorDrop(this); + actor.Name = item.ShortName; + Spawn(actor, ref hitLocation, ref hitNormal); + } + } + } +} diff --git a/Source/Editor/Windows/PluginsWindow.cs b/Source/Editor/Windows/PluginsWindow.cs index c5ec02b31..268c22581 100644 --- a/Source/Editor/Windows/PluginsWindow.cs +++ b/Source/Editor/Windows/PluginsWindow.cs @@ -190,7 +190,7 @@ namespace FlaxEditor.Windows }; _addPluginProjectButton = new Button { - Text = "Create Plugin Project", + Text = "Create Project", TooltipText = "Add new plugin project.", AnchorPreset = AnchorPresets.TopLeft, LocalLocation = new Float2(70, 18), @@ -201,7 +201,7 @@ namespace FlaxEditor.Windows _cloneProjectButton = new Button { - Text = "Clone Plugin Project", + Text = "Clone Project", TooltipText = "Git Clone a plugin project.", AnchorPreset = AnchorPresets.TopLeft, LocalLocation = new Float2(70 + _addPluginProjectButton.Size.X + 8, 18), diff --git a/Source/Editor/Windows/SceneTreeWindow.cs b/Source/Editor/Windows/SceneTreeWindow.cs index 250bd2301..63ba7b960 100644 --- a/Source/Editor/Windows/SceneTreeWindow.cs +++ b/Source/Editor/Windows/SceneTreeWindow.cs @@ -423,6 +423,7 @@ namespace FlaxEditor.Windows var actor = item.OnEditorDrop(this); actor.Name = item.ShortName; Level.SpawnActor(actor); + Editor.Scene.MarkSceneEdited(actor.Scene); } result = DragDropEffect.Move; } @@ -440,6 +441,7 @@ namespace FlaxEditor.Windows } actor.Name = item.Name; Level.SpawnActor(actor); + Editor.Scene.MarkSceneEdited(actor.Scene); } result = DragDropEffect.Move; } diff --git a/Source/Engine/Core/Types/StringBuilder.h b/Source/Engine/Core/Types/StringBuilder.h index 051554a23..068f245d0 100644 --- a/Source/Engine/Core/Types/StringBuilder.h +++ b/Source/Engine/Core/Types/StringBuilder.h @@ -3,6 +3,7 @@ #pragma once #include "String.h" +#include "StringView.h" #include "Engine/Core/Collections/Array.h" /// @@ -138,6 +139,11 @@ public: _data.Add(*str, str.Length()); return *this; } + StringBuilder& Append(const StringView& str) + { + _data.Add(*str, str.Length()); + return *this; + } // Append int to the string // @param val Value to append diff --git a/Source/Engine/Graphics/Graphics.cpp b/Source/Engine/Graphics/Graphics.cpp index fa03fb7cd..f91c58cea 100644 --- a/Source/Engine/Graphics/Graphics.cpp +++ b/Source/Engine/Graphics/Graphics.cpp @@ -67,7 +67,8 @@ void GraphicsSettings::Apply() Graphics::AllowCSMBlending = AllowCSMBlending; Graphics::GlobalSDFQuality = GlobalSDFQuality; Graphics::GIQuality = GIQuality; - Graphics::PostProcessSettings = PostProcessSettings; + Graphics::PostProcessSettings = ::PostProcessSettings(); + Graphics::PostProcessSettings.BlendWith(PostProcessSettings, 1.0f); } void Graphics::DisposeDevice() diff --git a/Source/Engine/Level/Actors/Sky.h b/Source/Engine/Level/Actors/Sky.h index 81aca3653..6d0935f78 100644 --- a/Source/Engine/Level/Actors/Sky.h +++ b/Source/Engine/Level/Actors/Sky.h @@ -31,19 +31,19 @@ public: /// /// Directional light that is used to simulate the sun. /// - API_FIELD(Attributes="EditorOrder(10), DefaultValue(null), EditorDisplay(\"Sun\")") + API_FIELD(Attributes="EditorOrder(10), DefaultValue(null), EditorDisplay(\"Sky\")") ScriptingObjectReference SunLight; /// /// The sun disc scale. /// - API_FIELD(Attributes="EditorOrder(20), DefaultValue(2.0f), EditorDisplay(\"Sun\"), Limit(0, 100, 0.01f)") + API_FIELD(Attributes="EditorOrder(20), DefaultValue(2.0f), EditorDisplay(\"Sky\"), Limit(0, 100, 0.01f)") float SunDiscScale = 2.0f; /// /// The sun power. /// - API_FIELD(Attributes="EditorOrder(30), DefaultValue(8.0f), EditorDisplay(\"Sun\"), Limit(0, 1000, 0.01f)") + API_FIELD(Attributes="EditorOrder(30), DefaultValue(8.0f), EditorDisplay(\"Sky\"), Limit(0, 1000, 0.01f)") float SunPower = 8.0f; private: diff --git a/Source/Engine/Level/Level.cpp b/Source/Engine/Level/Level.cpp index a95b8070f..5f1432d31 100644 --- a/Source/Engine/Level/Level.cpp +++ b/Source/Engine/Level/Level.cpp @@ -1064,6 +1064,7 @@ bool Level::loadScene(rapidjson_flax::Value& data, int32 engineBuild, Scene** ou } } } + prefabSyncData.InitNewObjects(); } // /\ all above this has to be done on an any thread diff --git a/Source/Engine/Level/SceneObjectsFactory.cpp b/Source/Engine/Level/SceneObjectsFactory.cpp index a46edf070..985da6c29 100644 --- a/Source/Engine/Level/SceneObjectsFactory.cpp +++ b/Source/Engine/Level/SceneObjectsFactory.cpp @@ -403,6 +403,15 @@ SceneObjectsFactory::PrefabSyncData::PrefabSyncData(Array& sceneOb { } +void SceneObjectsFactory::PrefabSyncData::InitNewObjects() +{ + for (int32 i = 0; i < NewObjects.Count(); i++) + { + SceneObject* obj = SceneObjects[InitialCount + i]; + obj->Initialize(); + } +} + void SceneObjectsFactory::SetupPrefabInstances(Context& context, const PrefabSyncData& data) { PROFILE_CPU_NAMED("SetupPrefabInstances"); diff --git a/Source/Engine/Level/SceneObjectsFactory.h b/Source/Engine/Level/SceneObjectsFactory.h index b1a9b8a9d..86fd5364a 100644 --- a/Source/Engine/Level/SceneObjectsFactory.h +++ b/Source/Engine/Level/SceneObjectsFactory.h @@ -78,6 +78,7 @@ public: ISerializeModifier* Modifier; PrefabSyncData(Array& sceneObjects, const ISerializable::DeserializeStream& data, ISerializeModifier* modifier); + void InitNewObjects(); private: struct NewObj diff --git a/Source/Engine/Platform/Apple/ApplePlatform.cpp b/Source/Engine/Platform/Apple/ApplePlatform.cpp index c939c1af2..8a2e1d7ef 100644 --- a/Source/Engine/Platform/Apple/ApplePlatform.cpp +++ b/Source/Engine/Platform/Apple/ApplePlatform.cpp @@ -43,6 +43,7 @@ #include #if CRASH_LOG_ENABLE #include +#include #endif CPUInfo Cpu; @@ -502,20 +503,42 @@ Array ApplePlatform::GetStackFrames(int32 skipCount, { char** names = backtrace_symbols(callstack + skipCount, useCount); result.Resize(useCount); + Array parts; + int32 len; +#define COPY_STR(str, dst) \ + len = Math::Min(str.Length(), ARRAY_COUNT(frame.dst) - 1); \ + Platform::MemoryCopy(frame.dst, str.Get(), len); \ + frame.dst[len] = 0 for (int32 i = 0; i < useCount; i++) { - char* name = names[i]; + const StringAnsi name(names[i]); StackFrame& frame = result[i]; frame.ProgramCounter = callstack[skipCount + i]; frame.ModuleName[0] = 0; frame.FileName[0] = 0; frame.LineNumber = 0; - int32 nameLen = Math::Min(StringUtils::Length(name), ARRAY_COUNT(frame.FunctionName) - 1); - Platform::MemoryCopy(frame.FunctionName, name, nameLen); - frame.FunctionName[nameLen] = 0; - + + // Decode name + parts.Clear(); + name.Split(' ', parts); + if (parts.Count() == 6) + { + COPY_STR(parts[1], ModuleName); + const StringAnsiView toDemangle(parts[3]); + int status = 0; + char* demangled = __cxxabiv1::__cxa_demangle(*toDemangle, 0, 0, &status); + const StringAnsiView toCopy = demangled && status == 0 ? StringAnsiView(demangled) : StringAnsiView(toDemangle); + COPY_STR(toCopy, FunctionName); + if (demangled) + free(demangled); + } + else + { + COPY_STR(name, FunctionName); + } } free(names); +#undef COPY_STR } #endif return result; diff --git a/Source/Engine/Platform/Base/PlatformBase.cpp b/Source/Engine/Platform/Base/PlatformBase.cpp index d15bec61c..9a6b1b9dc 100644 --- a/Source/Engine/Platform/Base/PlatformBase.cpp +++ b/Source/Engine/Platform/Base/PlatformBase.cpp @@ -633,14 +633,21 @@ String PlatformBase::GetStackTrace(int32 skipCount, int32 maxDepth, void* contex for (const auto& frame : stackFrames) { StringAsUTF16 functionName(frame.FunctionName); + const StringView functionNameStr(functionName.Get()); if (StringUtils::Length(frame.FileName) != 0) { StringAsUTF16 fileName(frame.FileName); - result.AppendFormat(TEXT(" at {0}() in {1}:line {2}\n"), functionName.Get(), fileName.Get(), frame.LineNumber); + result.Append(TEXT(" at ")).Append(functionNameStr); + if (!functionNameStr.EndsWith(')')) + result.Append(TEXT("()")); + result.AppendFormat(TEXT("in {0}:line {1}\n"),fileName.Get(), frame.LineNumber); } else if (StringUtils::Length(frame.FunctionName) != 0) { - result.AppendFormat(TEXT(" at {0}()\n"), functionName.Get()); + result.Append(TEXT(" at ")).Append(functionNameStr); + if (!functionNameStr.EndsWith(')')) + result.Append(TEXT("()")); + result.Append('\n'); } else { diff --git a/Source/Tools/Flax.Build/Build/DotNet/DotNetSdk.cs b/Source/Tools/Flax.Build/Build/DotNet/DotNetSdk.cs index c2999999d..8eea4d6dd 100644 --- a/Source/Tools/Flax.Build/Build/DotNet/DotNetSdk.cs +++ b/Source/Tools/Flax.Build/Build/DotNet/DotNetSdk.cs @@ -249,11 +249,17 @@ namespace Flax.Build if (dotnetSdkVersions == null) dotnetSdkVersions = GetVersions(Path.Combine(dotnetPath, "sdk")); if (dotnetRuntimeVersions == null) - dotnetRuntimeVersions = GetVersions(Path.Combine(dotnetPath, "shared/Microsoft.NETCore.App")); + dotnetRuntimeVersions = GetVersions(Path.Combine(dotnetPath, "shared", "Microsoft.NETCore.App")); + + dotnetSdkVersions = dotnetSdkVersions.Where(x => IsValidVersion(Path.Combine(dotnetPath, "sdk", x))); + dotnetRuntimeVersions = dotnetRuntimeVersions.Where(x => IsValidVersion(Path.Combine(dotnetPath, "shared", "Microsoft.NETCore.App", x))); dotnetSdkVersions = dotnetSdkVersions.OrderByDescending(ParseVersion); dotnetRuntimeVersions = dotnetRuntimeVersions.OrderByDescending(ParseVersion); + Log.Verbose($"Found the following .NET SDK versions: {string.Join(", ", dotnetSdkVersions)}"); + Log.Verbose($"Found the following .NET runtime versions: {string.Join(", ", dotnetRuntimeVersions)}"); + string dotnetSdkVersion = dotnetSdkVersions.FirstOrDefault(x => ParseVersion(x).Major >= MinimumVersion.Major && ParseVersion(x).Major <= MaximumVersion.Major); string dotnetRuntimeVersion = dotnetRuntimeVersions.FirstOrDefault(x => ParseVersion(x).Major >= MinimumVersion.Major && ParseVersion(x).Major <= MaximumVersion.Major); if (string.IsNullOrEmpty(dotnetSdkVersion)) @@ -276,7 +282,7 @@ namespace Flax.Build // Pick SDK runtime if (!TryAddHostRuntime(platform, architecture, rid) && !TryAddHostRuntime(platform, architecture, ridFallback)) { - var path = Path.Combine(RootPath, $"packs/Microsoft.NETCore.App.Host.{rid}/{RuntimeVersionName}/runtimes/{rid}/native"); + var path = Path.Combine(RootPath, "packs", $"Microsoft.NETCore.App.Host.{rid}", RuntimeVersionName, "runtimes", rid, "native"); Log.Warning($"Missing .NET SDK host runtime for {platform} {architecture} ({path})."); return; } @@ -465,6 +471,11 @@ namespace Flax.Build .FirstOrDefault(); } + private static bool IsValidVersion(string versionPath) + { + return File.Exists(Path.Combine(versionPath, ".version")); + } + private static string SearchForDotnetLocationLinux() { if (File.Exists("/etc/dotnet/install_location")) // Officially recommended dotnet location file diff --git a/Source/Tools/Flax.Build/global.json b/Source/Tools/Flax.Build/global.json index a6e7dc6c3..3fe9e80f6 100644 --- a/Source/Tools/Flax.Build/global.json +++ b/Source/Tools/Flax.Build/global.json @@ -1,6 +1,6 @@ { "sdk": { "version": "7.0.0", - "rollForward": "latestMinor" + "rollForward": "latestMajor" } } \ No newline at end of file diff --git a/global.json b/global.json index a6e7dc6c3..3fe9e80f6 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { "version": "7.0.0", - "rollForward": "latestMinor" + "rollForward": "latestMajor" } } \ No newline at end of file