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/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); + } + } + } +}