Merge remote-tracking branch 'origin/master' into 1.8
This commit is contained in:
@@ -161,7 +161,7 @@ namespace FlaxEditor.Content.GUI
|
|||||||
{
|
{
|
||||||
var style = Style.Current;
|
var style = Style.Current;
|
||||||
var rect = new Rectangle(Float2.Zero, Size);
|
var rect = new Rectangle(Float2.Zero, Size);
|
||||||
var color = IsDragOver ? style.BackgroundSelected * 0.6f : (_mouseDown ? style.BackgroundSelected : (IsMouseOver ? style.BackgroundHighlighted : Color.Transparent));
|
var color = IsDragOver ? Color.Transparent : (_mouseDown ? style.BackgroundSelected : (IsMouseOver ? style.BackgroundHighlighted : Color.Transparent));
|
||||||
Render2D.FillRectangle(rect, color);
|
Render2D.FillRectangle(rect, color);
|
||||||
Render2D.DrawSprite(Editor.Instance.Icons.ArrowRight12, new Rectangle(rect.Location.X, rect.Y + rect.Size.Y * 0.25f, rect.Size.X, rect.Size.X), EnabledInHierarchy ? style.Foreground : style.ForegroundDisabled);
|
Render2D.DrawSprite(Editor.Instance.Icons.ArrowRight12, new Rectangle(rect.Location.X, rect.Y + rect.Size.Y * 0.25f, rect.Size.X, rect.Size.X), EnabledInHierarchy ? style.Foreground : style.ForegroundDisabled);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,123 +0,0 @@
|
|||||||
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
|
||||||
|
|
||||||
using FlaxEditor.GUI.Drag;
|
|
||||||
using FlaxEditor.SceneGraph;
|
|
||||||
using FlaxEngine;
|
|
||||||
using FlaxEngine.GUI;
|
|
||||||
|
|
||||||
namespace FlaxEditor.Content.GUI
|
|
||||||
{
|
|
||||||
public partial class ContentView
|
|
||||||
{
|
|
||||||
private bool _validDragOver;
|
|
||||||
private DragActors _dragActors;
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public override DragDropEffect OnDragEnter(ref Float2 location, DragData data)
|
|
||||||
{
|
|
||||||
var result = base.OnDragEnter(ref location, data);
|
|
||||||
if (result != DragDropEffect.None)
|
|
||||||
return result;
|
|
||||||
|
|
||||||
// Check if drop file(s)
|
|
||||||
if (data is DragDataFiles)
|
|
||||||
{
|
|
||||||
_validDragOver = true;
|
|
||||||
return DragDropEffect.Copy;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if drop actor(s)
|
|
||||||
if (_dragActors == null)
|
|
||||||
_dragActors = new DragActors(ValidateDragActors);
|
|
||||||
if (_dragActors.OnDragEnter(data))
|
|
||||||
{
|
|
||||||
_validDragOver = true;
|
|
||||||
return DragDropEffect.Move;
|
|
||||||
}
|
|
||||||
|
|
||||||
return DragDropEffect.None;
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool ValidateDragActors(ActorNode actor)
|
|
||||||
{
|
|
||||||
return actor.CanCreatePrefab && Editor.Instance.Windows.ContentWin.CurrentViewFolder.CanHaveAssets;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ImportActors(DragActors actors, ContentFolder location)
|
|
||||||
{
|
|
||||||
foreach (var actorNode in actors.Objects)
|
|
||||||
{
|
|
||||||
var actor = actorNode.Actor;
|
|
||||||
if (actors.Objects.Contains(actorNode.ParentNode as ActorNode))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
Editor.Instance.Prefabs.CreatePrefab(actor, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public override DragDropEffect OnDragMove(ref Float2 location, DragData data)
|
|
||||||
{
|
|
||||||
_validDragOver = false;
|
|
||||||
var result = base.OnDragMove(ref location, data);
|
|
||||||
if (result != DragDropEffect.None)
|
|
||||||
return result;
|
|
||||||
|
|
||||||
if (data is DragDataFiles)
|
|
||||||
{
|
|
||||||
_validDragOver = true;
|
|
||||||
result = DragDropEffect.Copy;
|
|
||||||
}
|
|
||||||
else if (_dragActors != null && _dragActors.HasValidDrag)
|
|
||||||
{
|
|
||||||
_validDragOver = true;
|
|
||||||
result = DragDropEffect.Move;
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public override DragDropEffect OnDragDrop(ref Float2 location, DragData data)
|
|
||||||
{
|
|
||||||
var result = base.OnDragDrop(ref location, data);
|
|
||||||
if (result != DragDropEffect.None)
|
|
||||||
return result;
|
|
||||||
|
|
||||||
// Check if drop file(s)
|
|
||||||
if (data is DragDataFiles files)
|
|
||||||
{
|
|
||||||
// Import files
|
|
||||||
var currentFolder = Editor.Instance.Windows.ContentWin.CurrentViewFolder;
|
|
||||||
if (currentFolder != null)
|
|
||||||
Editor.Instance.ContentImporting.Import(files.Files, currentFolder);
|
|
||||||
result = DragDropEffect.Copy;
|
|
||||||
}
|
|
||||||
// Check if drop actor(s)
|
|
||||||
else if (_dragActors != null && _dragActors.HasValidDrag)
|
|
||||||
{
|
|
||||||
// Import actors
|
|
||||||
var currentFolder = Editor.Instance.Windows.ContentWin.CurrentViewFolder;
|
|
||||||
if (currentFolder != null)
|
|
||||||
ImportActors(_dragActors, currentFolder);
|
|
||||||
|
|
||||||
_dragActors.OnDragDrop();
|
|
||||||
result = DragDropEffect.Move;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clear cache
|
|
||||||
_validDragOver = false;
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public override void OnDragLeave()
|
|
||||||
{
|
|
||||||
_validDragOver = false;
|
|
||||||
_dragActors?.OnDragLeave();
|
|
||||||
|
|
||||||
base.OnDragLeave();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -3,7 +3,9 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using FlaxEditor.GUI.Drag;
|
||||||
using FlaxEditor.Options;
|
using FlaxEditor.Options;
|
||||||
|
using FlaxEditor.SceneGraph;
|
||||||
using FlaxEditor.Windows;
|
using FlaxEditor.Windows;
|
||||||
using FlaxEngine;
|
using FlaxEngine;
|
||||||
using FlaxEngine.GUI;
|
using FlaxEngine.GUI;
|
||||||
@@ -52,14 +54,17 @@ namespace FlaxEditor.Content.GUI
|
|||||||
public partial class ContentView : ContainerControl, IContentItemOwner
|
public partial class ContentView : ContainerControl, IContentItemOwner
|
||||||
{
|
{
|
||||||
private readonly List<ContentItem> _items = new List<ContentItem>(256);
|
private readonly List<ContentItem> _items = new List<ContentItem>(256);
|
||||||
private readonly List<ContentItem> _selection = new List<ContentItem>(16);
|
private readonly List<ContentItem> _selection = new List<ContentItem>();
|
||||||
|
|
||||||
private float _viewScale = 1.0f;
|
private float _viewScale = 1.0f;
|
||||||
private ContentViewType _viewType = ContentViewType.Tiles;
|
private ContentViewType _viewType = ContentViewType.Tiles;
|
||||||
private bool _isRubberBandSpanning = false;
|
private bool _isRubberBandSpanning;
|
||||||
private Float2 _mousePresslocation;
|
private Float2 _mousePressLocation;
|
||||||
private Rectangle _rubberBandRectangle;
|
private Rectangle _rubberBandRectangle;
|
||||||
|
|
||||||
|
private bool _validDragOver;
|
||||||
|
private DragActors _dragActors;
|
||||||
|
|
||||||
#region External Events
|
#region External Events
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -193,6 +198,7 @@ namespace FlaxEditor.Content.GUI
|
|||||||
OnDelete?.Invoke(_selection);
|
OnDelete?.Invoke(_selection);
|
||||||
}),
|
}),
|
||||||
new InputActionsContainer.Binding(options => options.SelectAll, SelectAll),
|
new InputActionsContainer.Binding(options => options.SelectAll, SelectAll),
|
||||||
|
new InputActionsContainer.Binding(options => options.DeselectAll, DeselectAll),
|
||||||
new InputActionsContainer.Binding(options => options.Rename, () =>
|
new InputActionsContainer.Binding(options => options.Rename, () =>
|
||||||
{
|
{
|
||||||
if (HasSelection && _selection[0].CanRename)
|
if (HasSelection && _selection[0].CanRename)
|
||||||
@@ -397,10 +403,7 @@ namespace FlaxEditor.Content.GUI
|
|||||||
PerformLayout();
|
PerformLayout();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
private void BulkSelectUpdate(bool select = true)
|
||||||
/// Selects all the items.
|
|
||||||
/// </summary>
|
|
||||||
public void SelectAll()
|
|
||||||
{
|
{
|
||||||
// Lock layout
|
// Lock layout
|
||||||
var wasLayoutLocked = IsLayoutLocked;
|
var wasLayoutLocked = IsLayoutLocked;
|
||||||
@@ -408,13 +411,30 @@ namespace FlaxEditor.Content.GUI
|
|||||||
|
|
||||||
// Select items
|
// Select items
|
||||||
_selection.Clear();
|
_selection.Clear();
|
||||||
_selection.AddRange(_items);
|
if (select)
|
||||||
|
_selection.AddRange(_items);
|
||||||
|
|
||||||
// Unload and perform UI layout
|
// Unload and perform UI layout
|
||||||
IsLayoutLocked = wasLayoutLocked;
|
IsLayoutLocked = wasLayoutLocked;
|
||||||
PerformLayout();
|
PerformLayout();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Selects all the items.
|
||||||
|
/// </summary>
|
||||||
|
public void SelectAll()
|
||||||
|
{
|
||||||
|
BulkSelectUpdate(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Deselects all the items.
|
||||||
|
/// </summary>
|
||||||
|
public void DeselectAll()
|
||||||
|
{
|
||||||
|
BulkSelectUpdate(false);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Deselects the specified item.
|
/// Deselects the specified item.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -595,7 +615,9 @@ namespace FlaxEditor.Content.GUI
|
|||||||
// Check if drag is over
|
// Check if drag is over
|
||||||
if (IsDragOver && _validDragOver)
|
if (IsDragOver && _validDragOver)
|
||||||
{
|
{
|
||||||
Render2D.FillRectangle(new Rectangle(Float2.Zero, Size), style.BackgroundSelected * 0.4f);
|
var bounds = new Rectangle(Float2.One, Size - Float2.One * 2);
|
||||||
|
Render2D.FillRectangle(bounds, style.Selection);
|
||||||
|
Render2D.DrawRectangle(bounds, style.SelectionBorder);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if it's an empty thing
|
// Check if it's an empty thing
|
||||||
@@ -604,10 +626,11 @@ namespace FlaxEditor.Content.GUI
|
|||||||
Render2D.DrawText(style.FontSmall, IsSearching ? "No results" : "Empty", new Rectangle(Float2.Zero, Size), style.ForegroundDisabled, TextAlignment.Center, TextAlignment.Center);
|
Render2D.DrawText(style.FontSmall, IsSearching ? "No results" : "Empty", new Rectangle(Float2.Zero, Size), style.ForegroundDisabled, TextAlignment.Center, TextAlignment.Center);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Selection
|
||||||
if (_isRubberBandSpanning)
|
if (_isRubberBandSpanning)
|
||||||
{
|
{
|
||||||
Render2D.FillRectangle(_rubberBandRectangle, Color.Orange * 0.4f);
|
Render2D.FillRectangle(_rubberBandRectangle, style.Selection);
|
||||||
Render2D.DrawRectangle(_rubberBandRectangle, Color.Orange);
|
Render2D.DrawRectangle(_rubberBandRectangle, style.SelectionBorder);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -619,8 +642,8 @@ namespace FlaxEditor.Content.GUI
|
|||||||
|
|
||||||
if (button == MouseButton.Left)
|
if (button == MouseButton.Left)
|
||||||
{
|
{
|
||||||
_mousePresslocation = location;
|
_mousePressLocation = location;
|
||||||
_rubberBandRectangle = new Rectangle(_mousePresslocation, 0, 0);
|
_rubberBandRectangle = new Rectangle(_mousePressLocation, 0, 0);
|
||||||
_isRubberBandSpanning = true;
|
_isRubberBandSpanning = true;
|
||||||
StartMouseCapture();
|
StartMouseCapture();
|
||||||
}
|
}
|
||||||
@@ -632,8 +655,8 @@ namespace FlaxEditor.Content.GUI
|
|||||||
{
|
{
|
||||||
if (_isRubberBandSpanning)
|
if (_isRubberBandSpanning)
|
||||||
{
|
{
|
||||||
_rubberBandRectangle.Width = location.X - _mousePresslocation.X;
|
_rubberBandRectangle.Width = location.X - _mousePressLocation.X;
|
||||||
_rubberBandRectangle.Height = location.Y - _mousePresslocation.Y;
|
_rubberBandRectangle.Height = location.Y - _mousePressLocation.Y;
|
||||||
}
|
}
|
||||||
|
|
||||||
base.OnMouseMove(location);
|
base.OnMouseMove(location);
|
||||||
@@ -768,6 +791,114 @@ namespace FlaxEditor.Content.GUI
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override DragDropEffect OnDragEnter(ref Float2 location, DragData data)
|
||||||
|
{
|
||||||
|
var result = base.OnDragEnter(ref location, data);
|
||||||
|
if (result != DragDropEffect.None)
|
||||||
|
return result;
|
||||||
|
|
||||||
|
// Check if drop file(s)
|
||||||
|
if (data is DragDataFiles)
|
||||||
|
{
|
||||||
|
_validDragOver = true;
|
||||||
|
return DragDropEffect.Copy;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if drop actor(s)
|
||||||
|
if (_dragActors == null)
|
||||||
|
_dragActors = new DragActors(ValidateDragActors);
|
||||||
|
if (_dragActors.OnDragEnter(data))
|
||||||
|
{
|
||||||
|
_validDragOver = true;
|
||||||
|
return DragDropEffect.Move;
|
||||||
|
}
|
||||||
|
|
||||||
|
return DragDropEffect.None;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool ValidateDragActors(ActorNode actor)
|
||||||
|
{
|
||||||
|
return actor.CanCreatePrefab && Editor.Instance.Windows.ContentWin.CurrentViewFolder.CanHaveAssets;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ImportActors(DragActors actors, ContentFolder location)
|
||||||
|
{
|
||||||
|
foreach (var actorNode in actors.Objects)
|
||||||
|
{
|
||||||
|
var actor = actorNode.Actor;
|
||||||
|
if (actors.Objects.Contains(actorNode.ParentNode as ActorNode))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
Editor.Instance.Prefabs.CreatePrefab(actor, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override DragDropEffect OnDragMove(ref Float2 location, DragData data)
|
||||||
|
{
|
||||||
|
_validDragOver = false;
|
||||||
|
var result = base.OnDragMove(ref location, data);
|
||||||
|
if (result != DragDropEffect.None)
|
||||||
|
return result;
|
||||||
|
|
||||||
|
if (data is DragDataFiles)
|
||||||
|
{
|
||||||
|
_validDragOver = true;
|
||||||
|
result = DragDropEffect.Copy;
|
||||||
|
}
|
||||||
|
else if (_dragActors != null && _dragActors.HasValidDrag)
|
||||||
|
{
|
||||||
|
_validDragOver = true;
|
||||||
|
result = DragDropEffect.Move;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override DragDropEffect OnDragDrop(ref Float2 location, DragData data)
|
||||||
|
{
|
||||||
|
var result = base.OnDragDrop(ref location, data);
|
||||||
|
if (result != DragDropEffect.None)
|
||||||
|
return result;
|
||||||
|
|
||||||
|
// Check if drop file(s)
|
||||||
|
if (data is DragDataFiles files)
|
||||||
|
{
|
||||||
|
// Import files
|
||||||
|
var currentFolder = Editor.Instance.Windows.ContentWin.CurrentViewFolder;
|
||||||
|
if (currentFolder != null)
|
||||||
|
Editor.Instance.ContentImporting.Import(files.Files, currentFolder);
|
||||||
|
result = DragDropEffect.Copy;
|
||||||
|
}
|
||||||
|
// Check if drop actor(s)
|
||||||
|
else if (_dragActors != null && _dragActors.HasValidDrag)
|
||||||
|
{
|
||||||
|
// Import actors
|
||||||
|
var currentFolder = Editor.Instance.Windows.ContentWin.CurrentViewFolder;
|
||||||
|
if (currentFolder != null)
|
||||||
|
ImportActors(_dragActors, currentFolder);
|
||||||
|
|
||||||
|
_dragActors.OnDragDrop();
|
||||||
|
result = DragDropEffect.Move;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear cache
|
||||||
|
_validDragOver = false;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void OnDragLeave()
|
||||||
|
{
|
||||||
|
_validDragOver = false;
|
||||||
|
_dragActors?.OnDragLeave();
|
||||||
|
|
||||||
|
base.OnDragLeave();
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
protected override void PerformLayoutBeforeChildren()
|
protected override void PerformLayoutBeforeChildren()
|
||||||
{
|
{
|
||||||
@@ -833,6 +964,9 @@ namespace FlaxEditor.Content.GUI
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void OnDestroy()
|
public override void OnDestroy()
|
||||||
{
|
{
|
||||||
|
if (IsDisposing)
|
||||||
|
return;
|
||||||
|
|
||||||
// Ensure to unlink all items
|
// Ensure to unlink all items
|
||||||
ClearItems();
|
ClearItems();
|
||||||
|
|
||||||
|
|||||||
@@ -241,7 +241,12 @@ namespace FlaxEditor.Content
|
|||||||
|
|
||||||
// Check if drag is over
|
// Check if drag is over
|
||||||
if (IsDragOver && _validDragOver)
|
if (IsDragOver && _validDragOver)
|
||||||
Render2D.FillRectangle(new Rectangle(Float2.Zero, Size), Style.Current.BackgroundSelected * 0.6f);
|
{
|
||||||
|
var style = Style.Current;
|
||||||
|
var bounds = new Rectangle(Float2.Zero, Size);
|
||||||
|
Render2D.FillRectangle(bounds, style.Selection);
|
||||||
|
Render2D.DrawRectangle(bounds, style.SelectionBorder);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool ValidateDragItem(ContentItem item)
|
private bool ValidateDragItem(ContentItem item)
|
||||||
|
|||||||
@@ -75,6 +75,8 @@ namespace FlaxEditor.Content
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void Create(string outputPath, object arg)
|
public override void Create(string outputPath, object arg)
|
||||||
{
|
{
|
||||||
|
bool resetTransform = false;
|
||||||
|
var transform = Transform.Identity;
|
||||||
if (!(arg is Actor actor))
|
if (!(arg is Actor actor))
|
||||||
{
|
{
|
||||||
// Create default prefab root object
|
// Create default prefab root object
|
||||||
@@ -86,8 +88,17 @@ namespace FlaxEditor.Content
|
|||||||
// Cleanup it after usage
|
// Cleanup it after usage
|
||||||
Object.Destroy(actor, 20.0f);
|
Object.Destroy(actor, 20.0f);
|
||||||
}
|
}
|
||||||
|
else if (actor.Scene != null)
|
||||||
|
{
|
||||||
|
// Create prefab with identity transform so the actor instance on a level will have it customized
|
||||||
|
resetTransform = true;
|
||||||
|
transform = actor.LocalTransform;
|
||||||
|
actor.LocalTransform = Transform.Identity;
|
||||||
|
}
|
||||||
|
|
||||||
PrefabManager.CreatePrefab(actor, outputPath, true);
|
PrefabManager.CreatePrefab(actor, outputPath, true);
|
||||||
|
if (resetTransform)
|
||||||
|
actor.LocalTransform = transform;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
|||||||
@@ -747,7 +747,7 @@ namespace FlaxEditor.CustomEditors
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public void SetValueToDefault()
|
public void SetValueToDefault()
|
||||||
{
|
{
|
||||||
SetValueCloned(Values.DefaultValue);
|
SetValue(Utilities.Utils.CloneValue(Values.DefaultValue));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -784,19 +784,7 @@ namespace FlaxEditor.CustomEditors
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
SetValueCloned(Values.ReferenceValue);
|
SetValue(Utilities.Utils.CloneValue(Values.ReferenceValue));
|
||||||
}
|
|
||||||
|
|
||||||
private void SetValueCloned(object value)
|
|
||||||
{
|
|
||||||
// For objects (eg. arrays) we need to clone them to prevent editing default/reference value within editor
|
|
||||||
if (value != null && !value.GetType().IsValueType)
|
|
||||||
{
|
|
||||||
var json = JsonSerializer.Serialize(value);
|
|
||||||
value = JsonSerializer.Deserialize(json, value.GetType());
|
|
||||||
}
|
|
||||||
|
|
||||||
SetValue(value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -245,9 +245,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
|||||||
{
|
{
|
||||||
// Special case for new Script added to actor
|
// Special case for new Script added to actor
|
||||||
if (editor.Values[0] is Script script && !script.HasPrefabLink)
|
if (editor.Values[0] is Script script && !script.HasPrefabLink)
|
||||||
{
|
|
||||||
return CreateDiffNode(editor);
|
return CreateDiffNode(editor);
|
||||||
}
|
|
||||||
|
|
||||||
// Skip if no change detected
|
// Skip if no change detected
|
||||||
var isRefEdited = editor.Values.IsReferenceValueModified;
|
var isRefEdited = editor.Values.IsReferenceValueModified;
|
||||||
@@ -258,9 +256,16 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
|||||||
if (editor.ChildrenEditors.Count == 0 || (isRefEdited && editor is CollectionEditor))
|
if (editor.ChildrenEditors.Count == 0 || (isRefEdited && editor is CollectionEditor))
|
||||||
result = CreateDiffNode(editor);
|
result = CreateDiffNode(editor);
|
||||||
bool isScriptEditorWithRefValue = editor is ScriptsEditor && editor.Values.HasReferenceValue;
|
bool isScriptEditorWithRefValue = editor is ScriptsEditor && editor.Values.HasReferenceValue;
|
||||||
|
bool isActorEditorInLevel = editor is ActorEditor && editor.Values[0] is Actor actor && actor.IsPrefabRoot && actor.Scene != null;
|
||||||
for (int i = 0; i < editor.ChildrenEditors.Count; i++)
|
for (int i = 0; i < editor.ChildrenEditors.Count; i++)
|
||||||
{
|
{
|
||||||
var child = ProcessDiff(editor.ChildrenEditors[i], !isScriptEditorWithRefValue);
|
var childEditor = editor.ChildrenEditors[i];
|
||||||
|
|
||||||
|
// Special case for root actor transformation (can be applied only in Prefab editor, not in Level)
|
||||||
|
if (isActorEditorInLevel && childEditor.Values.Info.Name is "LocalPosition" or "LocalOrientation" or "LocalScale")
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var child = ProcessDiff(childEditor, !isScriptEditorWithRefValue);
|
||||||
if (child != null)
|
if (child != null)
|
||||||
{
|
{
|
||||||
if (result == null)
|
if (result == null)
|
||||||
@@ -276,7 +281,6 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
|||||||
{
|
{
|
||||||
var prefabObjectScript = prefabObjectScripts[j];
|
var prefabObjectScript = prefabObjectScripts[j];
|
||||||
bool isRemoved = true;
|
bool isRemoved = true;
|
||||||
|
|
||||||
for (int i = 0; i < editor.ChildrenEditors.Count; i++)
|
for (int i = 0; i < editor.ChildrenEditors.Count; i++)
|
||||||
{
|
{
|
||||||
if (editor.ChildrenEditors[i].Values is ScriptsEditor.ScriptsContainer container && container.PrefabObjectId == prefabObjectScript.PrefabObjectID)
|
if (editor.ChildrenEditors[i].Values is ScriptsEditor.ScriptsContainer container && container.PrefabObjectId == prefabObjectScript.PrefabObjectID)
|
||||||
@@ -286,14 +290,12 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isRemoved)
|
if (isRemoved)
|
||||||
{
|
{
|
||||||
var dummy = new RemovedScriptDummy
|
var dummy = new RemovedScriptDummy
|
||||||
{
|
{
|
||||||
PrefabObject = prefabObjectScript
|
PrefabObject = prefabObjectScript
|
||||||
};
|
};
|
||||||
|
|
||||||
var child = CreateDiffNode(dummy);
|
var child = CreateDiffNode(dummy);
|
||||||
if (result == null)
|
if (result == null)
|
||||||
result = CreateDiffNode(editor);
|
result = CreateDiffNode(editor);
|
||||||
|
|||||||
@@ -67,25 +67,6 @@ public class MissingScriptEditor : GenericEditor
|
|||||||
base.Initialize(layout);
|
base.Initialize(layout);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void FindActorsWithMatchingMissingScript(List<MissingScript> missingScripts)
|
|
||||||
{
|
|
||||||
foreach (Actor actor in Level.GetActors(typeof(Actor)))
|
|
||||||
{
|
|
||||||
for (int scriptIndex = 0; scriptIndex < actor.ScriptsCount; scriptIndex++)
|
|
||||||
{
|
|
||||||
Script actorScript = actor.Scripts[scriptIndex];
|
|
||||||
if (actorScript is not MissingScript missingActorScript)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
MissingScript currentMissing = Values[0] as MissingScript;
|
|
||||||
if (missingActorScript.MissingTypeName != currentMissing.MissingTypeName)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
missingScripts.Add(missingActorScript);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void RunReplacementMultiCast(List<IUndoAction> actions)
|
private void RunReplacementMultiCast(List<IUndoAction> actions)
|
||||||
{
|
{
|
||||||
if (actions.Count == 0)
|
if (actions.Count == 0)
|
||||||
@@ -104,16 +85,54 @@ public class MissingScriptEditor : GenericEditor
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ReplaceScript(ScriptType script, bool replaceAllInScene)
|
private void GetMissingScripts(Actor actor, string currentMissingTypeName, List<MissingScript> missingScripts)
|
||||||
{
|
{
|
||||||
var actions = new List<IUndoAction>(4);
|
// Iterate over the scripts of this actor
|
||||||
|
for (int i = 0; i < actor.ScriptsCount; i++)
|
||||||
|
{
|
||||||
|
if (actor.GetScript(i) is MissingScript otherMissing &&
|
||||||
|
otherMissing.MissingTypeName == currentMissingTypeName)
|
||||||
|
{
|
||||||
|
missingScripts.Add(otherMissing);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Iterate over this actor children (recursive)
|
||||||
|
for (int i = 0; i < actor.ChildrenCount; i++)
|
||||||
|
{
|
||||||
|
GetMissingScripts(actor.GetChild(i), currentMissingTypeName, missingScripts);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ReplaceScript(ScriptType script)
|
||||||
|
{
|
||||||
var missingScripts = new List<MissingScript>();
|
var missingScripts = new List<MissingScript>();
|
||||||
if (!replaceAllInScene)
|
var currentMissing = (MissingScript)Values[0];
|
||||||
missingScripts.Add((MissingScript)Values[0]);
|
var currentMissingTypeName = currentMissing.MissingTypeName;
|
||||||
|
if (_shouldReplaceAllCheckbox.Checked)
|
||||||
|
{
|
||||||
|
if (currentMissing.Scene == null && currentMissing.Actor != null)
|
||||||
|
{
|
||||||
|
// Find all missing scripts in prefab instance
|
||||||
|
GetMissingScripts(currentMissing.Actor.GetPrefabRoot(), currentMissingTypeName, missingScripts);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Find all missing scripts in all loaded levels that match this type
|
||||||
|
for (int i = 0; i < Level.ScenesCount; i++)
|
||||||
|
{
|
||||||
|
GetMissingScripts(Level.GetScene(i), currentMissingTypeName, missingScripts);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
else
|
else
|
||||||
FindActorsWithMatchingMissingScript(missingScripts);
|
{
|
||||||
|
// Use the current instance only
|
||||||
|
foreach (var value in Values)
|
||||||
|
missingScripts.Add((MissingScript)value);
|
||||||
|
}
|
||||||
|
|
||||||
|
var actions = new List<IUndoAction>(4);
|
||||||
foreach (var missingScript in missingScripts)
|
foreach (var missingScript in missingScripts)
|
||||||
actions.Add(AddRemoveScript.Add(missingScript.Actor, script));
|
actions.Add(AddRemoveScript.Add(missingScript.Actor, script));
|
||||||
RunReplacementMultiCast(actions);
|
RunReplacementMultiCast(actions);
|
||||||
@@ -149,7 +168,7 @@ public class MissingScriptEditor : GenericEditor
|
|||||||
var cm = new ItemsListContextMenu(180);
|
var cm = new ItemsListContextMenu(180);
|
||||||
for (int i = 0; i < scripts.Count; i++)
|
for (int i = 0; i < scripts.Count; i++)
|
||||||
cm.AddItem(new TypeSearchPopup.TypeItemView(scripts[i]));
|
cm.AddItem(new TypeSearchPopup.TypeItemView(scripts[i]));
|
||||||
cm.ItemClicked += item => ReplaceScript((ScriptType)item.Tag, _shouldReplaceAllCheckbox.Checked);
|
cm.ItemClicked += item => ReplaceScript((ScriptType)item.Tag);
|
||||||
cm.SortItems();
|
cm.SortItems();
|
||||||
cm.Show(_dropPanel, _replaceScriptButton.BottomLeft - new Float2((cm.Width - _replaceScriptButton.Width) / 2, 0));
|
cm.Show(_dropPanel, _replaceScriptButton.BottomLeft - new Float2((cm.Width - _replaceScriptButton.Width) / 2, 0));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -160,7 +160,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
|||||||
var actions = new List<IUndoAction>();
|
var actions = new List<IUndoAction>();
|
||||||
foreach (var body in bodies)
|
foreach (var body in bodies)
|
||||||
{
|
{
|
||||||
var action = new Actions.DeleteActorsAction(new List<SceneGraphNode> { SceneGraphFactory.FindNode(body.ID) });
|
var action = new Actions.DeleteActorsAction(body);
|
||||||
action.Do();
|
action.Do();
|
||||||
actions.Add(action);
|
actions.Add(action);
|
||||||
}
|
}
|
||||||
@@ -185,7 +185,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
|||||||
var body = bodies.FirstOrDefault(x => x.Name == name);
|
var body = bodies.FirstOrDefault(x => x.Name == name);
|
||||||
if (body != null)
|
if (body != null)
|
||||||
{
|
{
|
||||||
var action = new Actions.DeleteActorsAction(new List<SceneGraphNode> { SceneGraphFactory.FindNode(body.ID) });
|
var action = new Actions.DeleteActorsAction(body);
|
||||||
action.Do();
|
action.Do();
|
||||||
Presenter.Undo?.AddAction(action);
|
Presenter.Undo?.AddAction(action);
|
||||||
}
|
}
|
||||||
@@ -224,7 +224,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Remove joint that will no longer be valid
|
// Remove joint that will no longer be valid
|
||||||
var action = new Actions.DeleteActorsAction(new List<SceneGraphNode> { SceneGraphFactory.FindNode(joint.ID) });
|
var action = new Actions.DeleteActorsAction(joint);
|
||||||
action.Do();
|
action.Do();
|
||||||
Presenter.Undo?.AddAction(action);
|
Presenter.Undo?.AddAction(action);
|
||||||
}
|
}
|
||||||
@@ -233,7 +233,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
|||||||
|
|
||||||
// Remove body
|
// Remove body
|
||||||
{
|
{
|
||||||
var action = new Actions.DeleteActorsAction(new List<SceneGraphNode> { SceneGraphFactory.FindNode(body.ID) });
|
var action = new Actions.DeleteActorsAction(body);
|
||||||
action.Do();
|
action.Do();
|
||||||
Presenter.Undo?.AddAction(action);
|
Presenter.Undo?.AddAction(action);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -151,8 +151,8 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
|||||||
if (IsDragOver && _dragHandlers != null && _dragHandlers.HasValidDrag)
|
if (IsDragOver && _dragHandlers != null && _dragHandlers.HasValidDrag)
|
||||||
{
|
{
|
||||||
var area = new Rectangle(Float2.Zero, size);
|
var area = new Rectangle(Float2.Zero, size);
|
||||||
Render2D.FillRectangle(area, Color.Orange * 0.5f);
|
Render2D.FillRectangle(area, style.Selection);
|
||||||
Render2D.DrawRectangle(area, Color.Black);
|
Render2D.DrawRectangle(area, style.SelectionBorder);
|
||||||
}
|
}
|
||||||
|
|
||||||
base.Draw();
|
base.Draw();
|
||||||
@@ -520,7 +520,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
|||||||
{
|
{
|
||||||
base.Draw();
|
base.Draw();
|
||||||
|
|
||||||
var color = FlaxEngine.GUI.Style.Current.BackgroundSelected * (IsDragOver ? 0.9f : 0.1f);
|
var color = Style.Current.BackgroundSelected * (IsDragOver ? 0.9f : 0.1f);
|
||||||
Render2D.FillRectangle(new Rectangle(Float2.Zero, Size), color);
|
Render2D.FillRectangle(new Rectangle(Float2.Zero, Size), color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -589,25 +589,27 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
|||||||
LayoutElementsContainer yEl;
|
LayoutElementsContainer yEl;
|
||||||
LayoutElementsContainer hEl;
|
LayoutElementsContainer hEl;
|
||||||
LayoutElementsContainer vEl;
|
LayoutElementsContainer vEl;
|
||||||
|
Color axisColorX = ActorTransformEditor.AxisColorX;
|
||||||
|
Color axisColorY = ActorTransformEditor.AxisColorY;
|
||||||
if (xEq)
|
if (xEq)
|
||||||
{
|
{
|
||||||
xEl = UniformPanelCapsuleForObjectWithText(horUp, "X: ", xItem.GetValues(Values));
|
xEl = UniformPanelCapsuleForObjectWithText(horUp, "X: ", xItem.GetValues(Values), axisColorX);
|
||||||
vEl = UniformPanelCapsuleForObjectWithText(horDown, "Width: ", widthItem.GetValues(Values));
|
vEl = UniformPanelCapsuleForObjectWithText(horDown, "Width: ", widthItem.GetValues(Values), axisColorX);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
xEl = UniformPanelCapsuleForObjectWithText(horUp, "Left: ", leftItem.GetValues(Values));
|
xEl = UniformPanelCapsuleForObjectWithText(horUp, "Left: ", leftItem.GetValues(Values), axisColorX);
|
||||||
vEl = UniformPanelCapsuleForObjectWithText(horDown, "Right: ", rightItem.GetValues(Values));
|
vEl = UniformPanelCapsuleForObjectWithText(horDown, "Right: ", rightItem.GetValues(Values), axisColorX);
|
||||||
}
|
}
|
||||||
if (yEq)
|
if (yEq)
|
||||||
{
|
{
|
||||||
yEl = UniformPanelCapsuleForObjectWithText(horUp, "Y: ", yItem.GetValues(Values));
|
yEl = UniformPanelCapsuleForObjectWithText(horUp, "Y: ", yItem.GetValues(Values), axisColorY);
|
||||||
hEl = UniformPanelCapsuleForObjectWithText(horDown, "Height: ", heightItem.GetValues(Values));
|
hEl = UniformPanelCapsuleForObjectWithText(horDown, "Height: ", heightItem.GetValues(Values), axisColorY);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
yEl = UniformPanelCapsuleForObjectWithText(horUp, "Top: ", topItem.GetValues(Values));
|
yEl = UniformPanelCapsuleForObjectWithText(horUp, "Top: ", topItem.GetValues(Values), axisColorY);
|
||||||
hEl = UniformPanelCapsuleForObjectWithText(horDown, "Bottom: ", bottomItem.GetValues(Values));
|
hEl = UniformPanelCapsuleForObjectWithText(horDown, "Bottom: ", bottomItem.GetValues(Values), axisColorY);
|
||||||
}
|
}
|
||||||
xEl.Control.AnchorMin = new Float2(0, xEl.Control.AnchorMin.Y);
|
xEl.Control.AnchorMin = new Float2(0, xEl.Control.AnchorMin.Y);
|
||||||
xEl.Control.AnchorMax = new Float2(0.5f, xEl.Control.AnchorMax.Y);
|
xEl.Control.AnchorMax = new Float2(0.5f, xEl.Control.AnchorMax.Y);
|
||||||
@@ -624,28 +626,34 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
|||||||
|
|
||||||
private VerticalPanelElement VerticalPanelWithoutMargin(LayoutElementsContainer cont)
|
private VerticalPanelElement VerticalPanelWithoutMargin(LayoutElementsContainer cont)
|
||||||
{
|
{
|
||||||
var horUp = cont.VerticalPanel();
|
var panel = cont.VerticalPanel();
|
||||||
horUp.Panel.Margin = Margin.Zero;
|
panel.Panel.Margin = Margin.Zero;
|
||||||
return horUp;
|
return panel;
|
||||||
}
|
}
|
||||||
|
|
||||||
private CustomElementsContainer<UniformGridPanel> UniformGridTwoByOne(LayoutElementsContainer cont)
|
private CustomElementsContainer<UniformGridPanel> UniformGridTwoByOne(LayoutElementsContainer cont)
|
||||||
{
|
{
|
||||||
var horUp = cont.CustomContainer<UniformGridPanel>();
|
var grid = cont.CustomContainer<UniformGridPanel>();
|
||||||
horUp.CustomControl.SlotsHorizontally = 2;
|
grid.CustomControl.SlotsHorizontally = 2;
|
||||||
horUp.CustomControl.SlotsVertically = 1;
|
grid.CustomControl.SlotsVertically = 1;
|
||||||
horUp.CustomControl.SlotPadding = Margin.Zero;
|
grid.CustomControl.SlotPadding = Margin.Zero;
|
||||||
horUp.CustomControl.ClipChildren = false;
|
grid.CustomControl.ClipChildren = false;
|
||||||
return horUp;
|
return grid;
|
||||||
}
|
}
|
||||||
|
|
||||||
private CustomElementsContainer<UniformGridPanel> UniformPanelCapsuleForObjectWithText(LayoutElementsContainer el, string text, ValueContainer values)
|
private CustomElementsContainer<UniformGridPanel> UniformPanelCapsuleForObjectWithText(LayoutElementsContainer el, string text, ValueContainer values, Color borderColor)
|
||||||
{
|
{
|
||||||
CustomElementsContainer<UniformGridPanel> hor = UniformGridTwoByOne(el);
|
var grid = UniformGridTwoByOne(el);
|
||||||
hor.CustomControl.SlotPadding = new Margin(5, 5, 0, 0);
|
grid.CustomControl.SlotPadding = new Margin(5, 5, 1, 1);
|
||||||
LabelElement lab = hor.Label(text);
|
var label = grid.Label(text);
|
||||||
hor.Object(values);
|
var editor = grid.Object(values);
|
||||||
return hor;
|
if (editor is FloatEditor floatEditor && floatEditor.Element is FloatValueElement floatEditorElement)
|
||||||
|
{
|
||||||
|
var back = FlaxEngine.GUI.Style.Current.TextBoxBackground;
|
||||||
|
floatEditorElement.ValueBox.BorderColor = Color.Lerp(borderColor, back, ActorTransformEditor.AxisGreyOutFactor);
|
||||||
|
floatEditorElement.ValueBox.BorderSelectedColor = borderColor;
|
||||||
|
}
|
||||||
|
return grid;
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool _cachedXEq;
|
private bool _cachedXEq;
|
||||||
|
|||||||
@@ -26,6 +26,11 @@ namespace FlaxEditor.CustomEditors.Editors
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static Color AxisColorZ = new Color(0.0f, 0.0235294f, 1.0f, 1.0f);
|
public static Color AxisColorZ = new Color(0.0f, 0.0235294f, 1.0f, 1.0f);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The axes colors grey out scale when input field is not focused.
|
||||||
|
/// </summary>
|
||||||
|
public static float AxisGreyOutFactor = 0.6f;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Custom editor for actor position property.
|
/// Custom editor for actor position property.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -39,12 +44,11 @@ namespace FlaxEditor.CustomEditors.Editors
|
|||||||
|
|
||||||
// Override colors
|
// Override colors
|
||||||
var back = FlaxEngine.GUI.Style.Current.TextBoxBackground;
|
var back = FlaxEngine.GUI.Style.Current.TextBoxBackground;
|
||||||
var grayOutFactor = 0.6f;
|
XElement.ValueBox.BorderColor = Color.Lerp(AxisColorX, back, AxisGreyOutFactor);
|
||||||
XElement.ValueBox.BorderColor = Color.Lerp(AxisColorX, back, grayOutFactor);
|
|
||||||
XElement.ValueBox.BorderSelectedColor = AxisColorX;
|
XElement.ValueBox.BorderSelectedColor = AxisColorX;
|
||||||
YElement.ValueBox.BorderColor = Color.Lerp(AxisColorY, back, grayOutFactor);
|
YElement.ValueBox.BorderColor = Color.Lerp(AxisColorY, back, AxisGreyOutFactor);
|
||||||
YElement.ValueBox.BorderSelectedColor = AxisColorY;
|
YElement.ValueBox.BorderSelectedColor = AxisColorY;
|
||||||
ZElement.ValueBox.BorderColor = Color.Lerp(AxisColorZ, back, grayOutFactor);
|
ZElement.ValueBox.BorderColor = Color.Lerp(AxisColorZ, back, AxisGreyOutFactor);
|
||||||
ZElement.ValueBox.BorderSelectedColor = AxisColorZ;
|
ZElement.ValueBox.BorderSelectedColor = AxisColorZ;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -62,12 +66,11 @@ namespace FlaxEditor.CustomEditors.Editors
|
|||||||
|
|
||||||
// Override colors
|
// Override colors
|
||||||
var back = FlaxEngine.GUI.Style.Current.TextBoxBackground;
|
var back = FlaxEngine.GUI.Style.Current.TextBoxBackground;
|
||||||
var grayOutFactor = 0.6f;
|
XElement.ValueBox.BorderColor = Color.Lerp(AxisColorX, back, AxisGreyOutFactor);
|
||||||
XElement.ValueBox.BorderColor = Color.Lerp(AxisColorX, back, grayOutFactor);
|
|
||||||
XElement.ValueBox.BorderSelectedColor = AxisColorX;
|
XElement.ValueBox.BorderSelectedColor = AxisColorX;
|
||||||
YElement.ValueBox.BorderColor = Color.Lerp(AxisColorY, back, grayOutFactor);
|
YElement.ValueBox.BorderColor = Color.Lerp(AxisColorY, back, AxisGreyOutFactor);
|
||||||
YElement.ValueBox.BorderSelectedColor = AxisColorY;
|
YElement.ValueBox.BorderSelectedColor = AxisColorY;
|
||||||
ZElement.ValueBox.BorderColor = Color.Lerp(AxisColorZ, back, grayOutFactor);
|
ZElement.ValueBox.BorderColor = Color.Lerp(AxisColorZ, back, AxisGreyOutFactor);
|
||||||
ZElement.ValueBox.BorderSelectedColor = AxisColorZ;
|
ZElement.ValueBox.BorderSelectedColor = AxisColorZ;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
using FlaxEditor.Scripting;
|
|
||||||
using FlaxEngine;
|
using FlaxEngine;
|
||||||
using FlaxEngine.Utilities;
|
using FlaxEngine.Utilities;
|
||||||
|
|
||||||
@@ -47,8 +46,9 @@ namespace FlaxEditor.CustomEditors.Editors
|
|||||||
if (elementType.IsValueType || NotNullItems)
|
if (elementType.IsValueType || NotNullItems)
|
||||||
{
|
{
|
||||||
// Fill new entries with the last value
|
// Fill new entries with the last value
|
||||||
|
var lastValue = array.GetValue(oldSize - 1);
|
||||||
for (int i = oldSize; i < newSize; i++)
|
for (int i = oldSize; i < newSize; i++)
|
||||||
Array.Copy(array, oldSize - 1, newValues, i, 1);
|
newValues.SetValue(Utilities.Utils.CloneValue(lastValue), i);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -542,9 +542,10 @@ namespace FlaxEditor.CustomEditors.Editors
|
|||||||
{
|
{
|
||||||
if (_dragHandlers is { HasValidDrag: true })
|
if (_dragHandlers is { HasValidDrag: true })
|
||||||
{
|
{
|
||||||
|
var style = FlaxEngine.GUI.Style.Current;
|
||||||
var area = new Rectangle(Float2.Zero, Size);
|
var area = new Rectangle(Float2.Zero, Size);
|
||||||
Render2D.FillRectangle(area, Color.Orange * 0.5f);
|
Render2D.FillRectangle(area, style.Selection);
|
||||||
Render2D.DrawRectangle(area, Color.Black);
|
Render2D.DrawRectangle(area, style.SelectionBorder);
|
||||||
}
|
}
|
||||||
|
|
||||||
base.Draw();
|
base.Draw();
|
||||||
|
|||||||
@@ -220,7 +220,11 @@ namespace FlaxEditor.CustomEditors.Editors
|
|||||||
|
|
||||||
// Check if drag is over
|
// Check if drag is over
|
||||||
if (IsDragOver && _hasValidDragOver)
|
if (IsDragOver && _hasValidDragOver)
|
||||||
Render2D.FillRectangle(new Rectangle(Float2.Zero, Size), style.BackgroundSelected * 0.4f);
|
{
|
||||||
|
var bounds = new Rectangle(Float2.Zero, Size);
|
||||||
|
Render2D.FillRectangle(bounds, style.Selection);
|
||||||
|
Render2D.DrawRectangle(bounds, style.SelectionBorder);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
|||||||
@@ -175,7 +175,11 @@ namespace FlaxEditor.CustomEditors.Editors
|
|||||||
|
|
||||||
// Check if drag is over
|
// Check if drag is over
|
||||||
if (IsDragOver && _hasValidDragOver)
|
if (IsDragOver && _hasValidDragOver)
|
||||||
Render2D.FillRectangle(new Rectangle(Float2.Zero, Size), style.BackgroundSelected * 0.4f);
|
{
|
||||||
|
var bounds = new Rectangle(Float2.Zero, Size);
|
||||||
|
Render2D.FillRectangle(bounds, style.Selection);
|
||||||
|
Render2D.DrawRectangle(bounds, style.SelectionBorder);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
|||||||
@@ -1676,6 +1676,9 @@ namespace FlaxEditor
|
|||||||
[return: MarshalAs(UnmanagedType.U1)]
|
[return: MarshalAs(UnmanagedType.U1)]
|
||||||
internal static partial bool Internal_CanSetToRoot(IntPtr prefab, IntPtr newRoot);
|
internal static partial bool Internal_CanSetToRoot(IntPtr prefab, IntPtr newRoot);
|
||||||
|
|
||||||
|
[LibraryImport("FlaxEngine", EntryPoint = "EditorInternal_GetPrefabNestedObject", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(StringMarshaller))]
|
||||||
|
internal static partial void Internal_GetPrefabNestedObject(IntPtr prefabId, IntPtr prefabObjectId, IntPtr outPrefabId, IntPtr outPrefabObjectId);
|
||||||
|
|
||||||
[LibraryImport("FlaxEngine", EntryPoint = "EditorInternal_GetAnimationTime", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(StringMarshaller))]
|
[LibraryImport("FlaxEngine", EntryPoint = "EditorInternal_GetAnimationTime", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(StringMarshaller))]
|
||||||
internal static partial float Internal_GetAnimationTime(IntPtr animatedModel);
|
internal static partial float Internal_GetAnimationTime(IntPtr animatedModel);
|
||||||
|
|
||||||
|
|||||||
@@ -182,7 +182,11 @@ namespace FlaxEditor.GUI
|
|||||||
|
|
||||||
// Check if drag is over
|
// Check if drag is over
|
||||||
if (IsDragOver && _dragOverElement != null && _dragOverElement.HasValidDrag)
|
if (IsDragOver && _dragOverElement != null && _dragOverElement.HasValidDrag)
|
||||||
Render2D.FillRectangle(iconRect, style.BackgroundSelected * 0.4f);
|
{
|
||||||
|
var bounds = new Rectangle(Float2.Zero, Size);
|
||||||
|
Render2D.FillRectangle(bounds, style.Selection);
|
||||||
|
Render2D.DrawRectangle(bounds, style.SelectionBorder);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
|||||||
@@ -359,6 +359,17 @@ namespace FlaxEditor.GUI.ContextMenu
|
|||||||
ButtonClicked?.Invoke(button);
|
ButtonClicked?.Invoke(button);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void Show(Control parent, Float2 location)
|
||||||
|
{
|
||||||
|
// Remove last separator to make context menu look better
|
||||||
|
int lastIndex = _panel.Children.Count - 1;
|
||||||
|
if (lastIndex >= 0 && _panel.Children[lastIndex] is ContextMenuSeparator separator)
|
||||||
|
separator.Dispose();
|
||||||
|
|
||||||
|
base.Show(parent, location);
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override bool ContainsPoint(ref Float2 location, bool precise)
|
public override bool ContainsPoint(ref Float2 location, bool precise)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -168,30 +168,30 @@ namespace FlaxEditor.GUI.ContextMenu
|
|||||||
bool isUp = false, isLeft = false;
|
bool isUp = false, isLeft = false;
|
||||||
if (UseAutomaticDirectionFix)
|
if (UseAutomaticDirectionFix)
|
||||||
{
|
{
|
||||||
|
var parentMenu = parent as ContextMenu;
|
||||||
if (monitorBounds.Bottom < rightBottomLocationSS.Y)
|
if (monitorBounds.Bottom < rightBottomLocationSS.Y)
|
||||||
{
|
{
|
||||||
// Direction: up
|
|
||||||
isUp = true;
|
isUp = true;
|
||||||
locationSS.Y -= dpiSize.Y;
|
locationSS.Y -= dpiSize.Y;
|
||||||
|
if (parentMenu != null && parentMenu._childCM != null)
|
||||||
// Offset to fix sub-menu location
|
|
||||||
if (parent is ContextMenu menu && menu._childCM != null)
|
|
||||||
locationSS.Y += 30.0f * dpiScale;
|
locationSS.Y += 30.0f * dpiScale;
|
||||||
}
|
}
|
||||||
if (monitorBounds.Right < rightBottomLocationSS.X || _parentCM?.Direction == ContextMenuDirection.LeftDown || _parentCM?.Direction == ContextMenuDirection.LeftUp)
|
if (parentMenu == null)
|
||||||
{
|
{
|
||||||
// Direction: left
|
if (monitorBounds.Right < rightBottomLocationSS.X)
|
||||||
isLeft = true;
|
|
||||||
|
|
||||||
if (IsSubMenu && _parentCM != null)
|
|
||||||
{
|
|
||||||
locationSS.X -= _parentCM.Width + dpiSize.X;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
|
isLeft = true;
|
||||||
locationSS.X -= dpiSize.X;
|
locationSS.X -= dpiSize.X;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (monitorBounds.Right < rightBottomLocationSS.X || _parentCM?.Direction == ContextMenuDirection.LeftDown || _parentCM?.Direction == ContextMenuDirection.LeftUp)
|
||||||
|
{
|
||||||
|
isLeft = true;
|
||||||
|
if (IsSubMenu && _parentCM != null)
|
||||||
|
locationSS.X -= _parentCM.Width + dpiSize.X;
|
||||||
|
else
|
||||||
|
locationSS.X -= dpiSize.X;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update direction flag
|
// Update direction flag
|
||||||
|
|||||||
@@ -484,6 +484,7 @@ namespace FlaxEditor.GUI
|
|||||||
cm.AddSeparator();
|
cm.AddSeparator();
|
||||||
cm.AddButton("Edit all keyframes", () => _editor.EditAllKeyframes(this, location));
|
cm.AddButton("Edit all keyframes", () => _editor.EditAllKeyframes(this, location));
|
||||||
cm.AddButton("Select all keyframes", _editor.SelectAll);
|
cm.AddButton("Select all keyframes", _editor.SelectAll);
|
||||||
|
cm.AddButton("Deselect all keyframes", _editor.DeselectAll);
|
||||||
cm.AddButton("Copy all keyframes", () =>
|
cm.AddButton("Copy all keyframes", () =>
|
||||||
{
|
{
|
||||||
_editor.SelectAll();
|
_editor.SelectAll();
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ using System.Globalization;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using FlaxEditor.CustomEditors;
|
using FlaxEditor.CustomEditors;
|
||||||
using FlaxEditor.GUI.ContextMenu;
|
using FlaxEditor.GUI.ContextMenu;
|
||||||
|
using FlaxEditor.Options;
|
||||||
using FlaxEngine;
|
using FlaxEngine;
|
||||||
using FlaxEngine.GUI;
|
using FlaxEngine.GUI;
|
||||||
|
|
||||||
@@ -713,15 +714,28 @@ namespace FlaxEditor.GUI
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void BulkSelectUpdate(bool select = true)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < _points.Count; i++)
|
||||||
|
{
|
||||||
|
_points[i].IsSelected = select;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Selects all keyframes.
|
/// Selects all keyframes.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void SelectAll()
|
public void SelectAll()
|
||||||
{
|
{
|
||||||
for (int i = 0; i < _points.Count; i++)
|
BulkSelectUpdate(true);
|
||||||
{
|
}
|
||||||
_points[i].IsSelected = true;
|
|
||||||
}
|
/// <summary>
|
||||||
|
/// Deselects all keyframes.
|
||||||
|
/// </summary>
|
||||||
|
public void DeselectAll()
|
||||||
|
{
|
||||||
|
BulkSelectUpdate(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -899,8 +913,8 @@ namespace FlaxEditor.GUI
|
|||||||
_mainPanel.PointToParent(_contents.PointToParent(_contents._leftMouseDownPos)),
|
_mainPanel.PointToParent(_contents.PointToParent(_contents._leftMouseDownPos)),
|
||||||
_mainPanel.PointToParent(_contents.PointToParent(_contents._mousePos))
|
_mainPanel.PointToParent(_contents.PointToParent(_contents._mousePos))
|
||||||
);
|
);
|
||||||
Render2D.FillRectangle(selectionRect, Color.Orange * 0.4f);
|
Render2D.FillRectangle(selectionRect, style.Selection);
|
||||||
Render2D.DrawRectangle(selectionRect, Color.Orange);
|
Render2D.DrawRectangle(selectionRect, style.SelectionBorder);
|
||||||
}
|
}
|
||||||
|
|
||||||
base.Draw();
|
base.Draw();
|
||||||
@@ -926,34 +940,35 @@ namespace FlaxEditor.GUI
|
|||||||
if (base.OnKeyDown(key))
|
if (base.OnKeyDown(key))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
switch (key)
|
InputOptions options = Editor.Instance.Options.Options.Input;
|
||||||
|
if (options.SelectAll.Process(this))
|
||||||
|
{
|
||||||
|
SelectAll();
|
||||||
|
UpdateTangents();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (options.DeselectAll.Process(this))
|
||||||
|
{
|
||||||
|
DeselectAll();
|
||||||
|
UpdateTangents();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (options.Delete.Process(this))
|
||||||
{
|
{
|
||||||
case KeyboardKeys.Delete:
|
|
||||||
RemoveKeyframes();
|
RemoveKeyframes();
|
||||||
return true;
|
return true;
|
||||||
case KeyboardKeys.A:
|
|
||||||
if (Root.GetKey(KeyboardKeys.Control))
|
|
||||||
{
|
|
||||||
SelectAll();
|
|
||||||
UpdateTangents();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case KeyboardKeys.C:
|
|
||||||
if (Root.GetKey(KeyboardKeys.Control))
|
|
||||||
{
|
|
||||||
CopyKeyframes();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case KeyboardKeys.V:
|
|
||||||
if (Root.GetKey(KeyboardKeys.Control))
|
|
||||||
{
|
|
||||||
KeyframesEditorUtils.Paste(this);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
else if (options.Copy.Process(this))
|
||||||
|
{
|
||||||
|
CopyKeyframes();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (options.Paste.Process(this))
|
||||||
|
{
|
||||||
|
KeyframesEditorUtils.Paste(this);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -201,8 +201,9 @@ namespace FlaxEditor.GUI.Input
|
|||||||
if (_isSliding)
|
if (_isSliding)
|
||||||
{
|
{
|
||||||
// Draw overlay
|
// Draw overlay
|
||||||
// TODO: render nicer overlay with some glow from the borders (inside)
|
var bounds = new Rectangle(Float2.Zero, Size);
|
||||||
Render2D.FillRectangle(new Rectangle(Float2.Zero, Size), Color.Orange * 0.3f);
|
Render2D.FillRectangle(bounds, style.Selection);
|
||||||
|
Render2D.DrawRectangle(bounds, style.SelectionBorder);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -122,6 +122,10 @@ namespace FlaxEditor.GUI
|
|||||||
if (IsMouseOver || IsFocused)
|
if (IsMouseOver || IsFocused)
|
||||||
Render2D.FillRectangle(new Rectangle(Float2.Zero, Size), style.BackgroundHighlighted);
|
Render2D.FillRectangle(new Rectangle(Float2.Zero, Size), style.BackgroundHighlighted);
|
||||||
|
|
||||||
|
// Indent for drop panel items is handled by drop panel margin
|
||||||
|
if (Parent is not DropPanel)
|
||||||
|
textRect.Location += new Float2(Editor.Instance.Icons.ArrowRight12.Size.X + 2, 0);
|
||||||
|
|
||||||
// Draw all highlights
|
// Draw all highlights
|
||||||
if (_highlights != null)
|
if (_highlights != null)
|
||||||
{
|
{
|
||||||
@@ -262,6 +266,10 @@ namespace FlaxEditor.GUI
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
category.Visible = anyVisible;
|
category.Visible = anyVisible;
|
||||||
|
if (string.IsNullOrEmpty(_searchBox.Text))
|
||||||
|
category.Close(false);
|
||||||
|
else
|
||||||
|
category.Open(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -338,9 +346,14 @@ namespace FlaxEditor.GUI
|
|||||||
var categoryPanel = new DropPanel
|
var categoryPanel = new DropPanel
|
||||||
{
|
{
|
||||||
HeaderText = item.Category,
|
HeaderText = item.Category,
|
||||||
|
ArrowImageOpened = new SpriteBrush(Editor.Instance.Icons.ArrowDown12),
|
||||||
|
ArrowImageClosed = new SpriteBrush(Editor.Instance.Icons.ArrowRight12),
|
||||||
|
EnableDropDownIcon = true,
|
||||||
|
ItemsMargin = new Margin(28, 0, 2, 2),
|
||||||
|
HeaderColor = Style.Current.Background,
|
||||||
Parent = parent,
|
Parent = parent,
|
||||||
};
|
};
|
||||||
categoryPanel.Open(false);
|
categoryPanel.Close(false);
|
||||||
_categoryPanels.Add(categoryPanel);
|
_categoryPanels.Add(categoryPanel);
|
||||||
parent = categoryPanel;
|
parent = categoryPanel;
|
||||||
}
|
}
|
||||||
@@ -382,6 +395,7 @@ namespace FlaxEditor.GUI
|
|||||||
item2.UpdateFilter(null);
|
item2.UpdateFilter(null);
|
||||||
}
|
}
|
||||||
category.Visible = true;
|
category.Visible = true;
|
||||||
|
category.Close(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -45,7 +45,8 @@ namespace FlaxEditor.GUI
|
|||||||
// Draw background
|
// Draw background
|
||||||
if (IsDragOver && _validDragOver)
|
if (IsDragOver && _validDragOver)
|
||||||
{
|
{
|
||||||
Render2D.FillRectangle(clientRect, Style.Current.BackgroundSelected * 0.6f);
|
Render2D.FillRectangle(clientRect, style.Selection);
|
||||||
|
Render2D.DrawRectangle(clientRect, style.SelectionBorder);
|
||||||
}
|
}
|
||||||
else if (_isPressed)
|
else if (_isPressed)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -227,8 +227,8 @@ namespace FlaxEditor.GUI.Timeline.GUI
|
|||||||
if (_isSelecting)
|
if (_isSelecting)
|
||||||
{
|
{
|
||||||
var selectionRect = Rectangle.FromPoints(_selectingStartPos, _mousePos);
|
var selectionRect = Rectangle.FromPoints(_selectingStartPos, _mousePos);
|
||||||
Render2D.FillRectangle(selectionRect, Color.Orange * 0.4f);
|
Render2D.FillRectangle(selectionRect, style.Selection);
|
||||||
Render2D.DrawRectangle(selectionRect, Color.Orange);
|
Render2D.DrawRectangle(selectionRect, style.SelectionBorder);
|
||||||
}
|
}
|
||||||
|
|
||||||
DrawChildren();
|
DrawChildren();
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ using System.Linq;
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using FlaxEditor.CustomEditors;
|
using FlaxEditor.CustomEditors;
|
||||||
using FlaxEditor.GUI.ContextMenu;
|
using FlaxEditor.GUI.ContextMenu;
|
||||||
|
using FlaxEditor.Options;
|
||||||
using FlaxEditor.Scripting;
|
using FlaxEditor.Scripting;
|
||||||
using FlaxEngine;
|
using FlaxEngine;
|
||||||
using FlaxEngine.GUI;
|
using FlaxEngine.GUI;
|
||||||
@@ -432,6 +433,7 @@ namespace FlaxEditor.GUI
|
|||||||
if (_editor.EnableKeyframesValueEdit)
|
if (_editor.EnableKeyframesValueEdit)
|
||||||
cm.AddButton("Edit all keyframes", () => _editor.EditAllKeyframes(this, location));
|
cm.AddButton("Edit all keyframes", () => _editor.EditAllKeyframes(this, location));
|
||||||
cm.AddButton("Select all keyframes", _editor.SelectAll).Enabled = _editor._points.Count > 0;
|
cm.AddButton("Select all keyframes", _editor.SelectAll).Enabled = _editor._points.Count > 0;
|
||||||
|
cm.AddButton("Deselect all keyframes", _editor.DeselectAll).Enabled = _editor._points.Count > 0;
|
||||||
cm.AddButton("Copy all keyframes", () =>
|
cm.AddButton("Copy all keyframes", () =>
|
||||||
{
|
{
|
||||||
_editor.SelectAll();
|
_editor.SelectAll();
|
||||||
@@ -902,7 +904,7 @@ namespace FlaxEditor.GUI
|
|||||||
var k = new Keyframe
|
var k = new Keyframe
|
||||||
{
|
{
|
||||||
Time = keyframesPos.X,
|
Time = keyframesPos.X,
|
||||||
Value = DefaultValue,
|
Value = Utilities.Utils.CloneValue(DefaultValue),
|
||||||
};
|
};
|
||||||
OnEditingStart();
|
OnEditingStart();
|
||||||
AddKeyframe(k);
|
AddKeyframe(k);
|
||||||
@@ -1209,15 +1211,28 @@ namespace FlaxEditor.GUI
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void BulkSelectUpdate(bool select = true)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < _points.Count; i++)
|
||||||
|
{
|
||||||
|
_points[i].IsSelected = select;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Selects all keyframes.
|
/// Selects all keyframes.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void SelectAll()
|
public void SelectAll()
|
||||||
{
|
{
|
||||||
for (int i = 0; i < _points.Count; i++)
|
BulkSelectUpdate(true);
|
||||||
{
|
}
|
||||||
_points[i].IsSelected = true;
|
|
||||||
}
|
/// <summary>
|
||||||
|
/// Deselects all keyframes.
|
||||||
|
/// </summary>
|
||||||
|
public void DeselectAll()
|
||||||
|
{
|
||||||
|
BulkSelectUpdate(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
@@ -1241,8 +1256,8 @@ namespace FlaxEditor.GUI
|
|||||||
_mainPanel.PointToParent(_contents.PointToParent(_contents._leftMouseDownPos)),
|
_mainPanel.PointToParent(_contents.PointToParent(_contents._leftMouseDownPos)),
|
||||||
_mainPanel.PointToParent(_contents.PointToParent(_contents._mousePos))
|
_mainPanel.PointToParent(_contents.PointToParent(_contents._mousePos))
|
||||||
);
|
);
|
||||||
Render2D.FillRectangle(selectionRect, Color.Orange * 0.4f);
|
Render2D.FillRectangle(selectionRect, style.Selection);
|
||||||
Render2D.DrawRectangle(selectionRect, Color.Orange);
|
Render2D.DrawRectangle(selectionRect, style.SelectionBorder);
|
||||||
}
|
}
|
||||||
|
|
||||||
base.Draw();
|
base.Draw();
|
||||||
@@ -1268,33 +1283,33 @@ namespace FlaxEditor.GUI
|
|||||||
if (base.OnKeyDown(key))
|
if (base.OnKeyDown(key))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
switch (key)
|
InputOptions options = Editor.Instance.Options.Options.Input;
|
||||||
|
if (options.SelectAll.Process(this))
|
||||||
|
{
|
||||||
|
SelectAll();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (options.DeselectAll.Process(this))
|
||||||
|
{
|
||||||
|
DeselectAll();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (options.Delete.Process(this))
|
||||||
{
|
{
|
||||||
case KeyboardKeys.Delete:
|
|
||||||
RemoveKeyframes();
|
RemoveKeyframes();
|
||||||
return true;
|
return true;
|
||||||
case KeyboardKeys.A:
|
|
||||||
if (Root.GetKey(KeyboardKeys.Control))
|
|
||||||
{
|
|
||||||
SelectAll();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case KeyboardKeys.C:
|
|
||||||
if (Root.GetKey(KeyboardKeys.Control))
|
|
||||||
{
|
|
||||||
CopyKeyframes();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case KeyboardKeys.V:
|
|
||||||
if (Root.GetKey(KeyboardKeys.Control))
|
|
||||||
{
|
|
||||||
KeyframesEditorUtils.Paste(this);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
else if (options.Copy.Process(this))
|
||||||
|
{
|
||||||
|
CopyKeyframes();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (options.Paste.Process(this))
|
||||||
|
{
|
||||||
|
KeyframesEditorUtils.Paste(this);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -304,7 +304,9 @@ namespace FlaxEditor.GUI.Timeline
|
|||||||
if (IsDragOver && _currentDragEffect != DragDropEffect.None)
|
if (IsDragOver && _currentDragEffect != DragDropEffect.None)
|
||||||
{
|
{
|
||||||
var style = Style.Current;
|
var style = Style.Current;
|
||||||
Render2D.FillRectangle(new Rectangle(Float2.Zero, Size), style.BackgroundSelected * 0.4f);
|
var bounds = new Rectangle(Float2.Zero, Size);
|
||||||
|
Render2D.FillRectangle(bounds, style.Selection);
|
||||||
|
Render2D.DrawRectangle(bounds, style.SelectionBorder);
|
||||||
}
|
}
|
||||||
|
|
||||||
base.Draw();
|
base.Draw();
|
||||||
|
|||||||
@@ -187,6 +187,11 @@ namespace FlaxEditor.GUI.Timeline
|
|||||||
private bool _showPreviewValues = true;
|
private bool _showPreviewValues = true;
|
||||||
private PlaybackStates _state = PlaybackStates.Disabled;
|
private PlaybackStates _state = PlaybackStates.Disabled;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The Track that is being dragged over. This could have a value when not dragging.
|
||||||
|
/// </summary>
|
||||||
|
internal Track DraggedOverTrack = null;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Flag used to mark modified timeline data.
|
/// Flag used to mark modified timeline data.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -774,14 +774,33 @@ namespace FlaxEditor.GUI.Timeline
|
|||||||
/// Updates the drag over mode based on the given mouse location.
|
/// Updates the drag over mode based on the given mouse location.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="location">The location.</param>
|
/// <param name="location">The location.</param>
|
||||||
private void UpdateDrawPositioning(ref Float2 location)
|
private void UpdateDragPositioning(ref Float2 location)
|
||||||
{
|
{
|
||||||
|
// Check collision with drag areas
|
||||||
if (new Rectangle(0, 0 - DefaultDragInsertPositionMargin - DefaultNodeOffsetY, Width, DefaultDragInsertPositionMargin * 2.0f).Contains(location))
|
if (new Rectangle(0, 0 - DefaultDragInsertPositionMargin - DefaultNodeOffsetY, Width, DefaultDragInsertPositionMargin * 2.0f).Contains(location))
|
||||||
_dragOverMode = DragItemPositioning.Above;
|
_dragOverMode = DragItemPositioning.Above;
|
||||||
else if (IsCollapsed && new Rectangle(0, Height - DefaultDragInsertPositionMargin, Width, DefaultDragInsertPositionMargin * 2.0f).Contains(location))
|
else if (IsCollapsed && new Rectangle(0, Height - DefaultDragInsertPositionMargin, Width, DefaultDragInsertPositionMargin * 2.0f).Contains(location))
|
||||||
_dragOverMode = DragItemPositioning.Below;
|
_dragOverMode = DragItemPositioning.Below;
|
||||||
else
|
else
|
||||||
_dragOverMode = DragItemPositioning.At;
|
_dragOverMode = DragItemPositioning.At;
|
||||||
|
|
||||||
|
// Update DraggedOverTrack
|
||||||
|
var timeline = Timeline;
|
||||||
|
if (_dragOverMode == DragItemPositioning.None)
|
||||||
|
{
|
||||||
|
if (timeline != null && timeline.DraggedOverTrack == this)
|
||||||
|
timeline.DraggedOverTrack = null;
|
||||||
|
}
|
||||||
|
else if (timeline != null)
|
||||||
|
timeline.DraggedOverTrack = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ClearDragPositioning()
|
||||||
|
{
|
||||||
|
_dragOverMode = DragItemPositioning.None;
|
||||||
|
var timeline = Timeline;
|
||||||
|
if (timeline != null && timeline.DraggedOverTrack == this)
|
||||||
|
timeline.DraggedOverTrack = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -975,26 +994,21 @@ namespace FlaxEditor.GUI.Timeline
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Draw drag and drop effect
|
// Draw drag and drop effect
|
||||||
if (IsDragOver && _isDragOverHeader)
|
if (IsDragOver && _timeline.DraggedOverTrack == this)
|
||||||
{
|
{
|
||||||
Color dragOverColor = style.BackgroundSelected * 0.6f;
|
|
||||||
Rectangle rect;
|
|
||||||
switch (_dragOverMode)
|
switch (_dragOverMode)
|
||||||
{
|
{
|
||||||
case DragItemPositioning.At:
|
case DragItemPositioning.At:
|
||||||
rect = textRect;
|
Render2D.FillRectangle(textRect, style.Selection);
|
||||||
|
Render2D.DrawRectangle(textRect, style.SelectionBorder);
|
||||||
break;
|
break;
|
||||||
case DragItemPositioning.Above:
|
case DragItemPositioning.Above:
|
||||||
rect = new Rectangle(textRect.X, textRect.Y - DefaultDragInsertPositionMargin - DefaultNodeOffsetY, textRect.Width, DefaultDragInsertPositionMargin * 2.0f);
|
Render2D.DrawRectangle(new Rectangle(textRect.X, textRect.Top - DefaultDragInsertPositionMargin * 0.5f - DefaultNodeOffsetY - _margin.Top, textRect.Width, DefaultDragInsertPositionMargin), style.SelectionBorder);
|
||||||
break;
|
break;
|
||||||
case DragItemPositioning.Below:
|
case DragItemPositioning.Below:
|
||||||
rect = new Rectangle(textRect.X, textRect.Bottom - DefaultDragInsertPositionMargin, textRect.Width, DefaultDragInsertPositionMargin * 2.0f);
|
Render2D.DrawRectangle(new Rectangle(textRect.X, textRect.Bottom + _margin.Bottom - DefaultDragInsertPositionMargin * 0.5f, textRect.Width, DefaultDragInsertPositionMargin), style.SelectionBorder);
|
||||||
break;
|
|
||||||
default:
|
|
||||||
rect = Rectangle.Empty;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
Render2D.FillRectangle(rect, dragOverColor);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
base.Draw();
|
base.Draw();
|
||||||
@@ -1170,18 +1184,18 @@ namespace FlaxEditor.GUI.Timeline
|
|||||||
_dragOverMode = DragItemPositioning.None;
|
_dragOverMode = DragItemPositioning.None;
|
||||||
if (result == DragDropEffect.None)
|
if (result == DragDropEffect.None)
|
||||||
{
|
{
|
||||||
UpdateDrawPositioning(ref location);
|
UpdateDragPositioning(ref location);
|
||||||
|
|
||||||
// Check if mouse is over header
|
// Check if mouse is over header
|
||||||
_isDragOverHeader = TestHeaderHit(ref location);
|
_isDragOverHeader = TestHeaderHit(ref location);
|
||||||
if (_isDragOverHeader)
|
if (_isDragOverHeader)
|
||||||
{
|
{
|
||||||
// Check if mouse is over arrow
|
if (Timeline != null)
|
||||||
|
Timeline.DraggedOverTrack = this;
|
||||||
|
|
||||||
|
// Expand node if mouse goes over arrow
|
||||||
if (_children.Count > 0 && ArrowRect.Contains(location))
|
if (_children.Count > 0 && ArrowRect.Contains(location))
|
||||||
{
|
|
||||||
// Expand track
|
|
||||||
Expand();
|
Expand();
|
||||||
}
|
|
||||||
|
|
||||||
result = OnDragEnterHeader(data);
|
result = OnDragEnterHeader(data);
|
||||||
}
|
}
|
||||||
@@ -1199,21 +1213,18 @@ namespace FlaxEditor.GUI.Timeline
|
|||||||
var result = base.OnDragMove(ref location, data);
|
var result = base.OnDragMove(ref location, data);
|
||||||
|
|
||||||
// Check if no children handled that event
|
// Check if no children handled that event
|
||||||
_dragOverMode = DragItemPositioning.None;
|
ClearDragPositioning();
|
||||||
if (result == DragDropEffect.None)
|
if (result == DragDropEffect.None)
|
||||||
{
|
{
|
||||||
UpdateDrawPositioning(ref location);
|
UpdateDragPositioning(ref location);
|
||||||
|
|
||||||
// Check if mouse is over header
|
// Check if mouse is over header
|
||||||
bool isDragOverHeader = TestHeaderHit(ref location);
|
bool isDragOverHeader = TestHeaderHit(ref location);
|
||||||
if (isDragOverHeader)
|
if (isDragOverHeader)
|
||||||
{
|
{
|
||||||
// Check if mouse is over arrow
|
// Expand node if mouse goes over arrow
|
||||||
if (_children.Count > 0 && ArrowRect.Contains(location))
|
if (_children.Count > 0 && ArrowRect.Contains(location))
|
||||||
{
|
|
||||||
// Expand track
|
|
||||||
Expand();
|
Expand();
|
||||||
}
|
|
||||||
|
|
||||||
if (!_isDragOverHeader)
|
if (!_isDragOverHeader)
|
||||||
result = OnDragEnterHeader(data);
|
result = OnDragEnterHeader(data);
|
||||||
@@ -1226,10 +1237,8 @@ namespace FlaxEditor.GUI.Timeline
|
|||||||
}
|
}
|
||||||
_isDragOverHeader = isDragOverHeader;
|
_isDragOverHeader = isDragOverHeader;
|
||||||
|
|
||||||
if (result == DragDropEffect.None || !isDragOverHeader)
|
if (result == DragDropEffect.None)
|
||||||
{
|
|
||||||
_dragOverMode = DragItemPositioning.None;
|
_dragOverMode = DragItemPositioning.None;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
@@ -1243,7 +1252,7 @@ namespace FlaxEditor.GUI.Timeline
|
|||||||
// Check if no children handled that event
|
// Check if no children handled that event
|
||||||
if (result == DragDropEffect.None)
|
if (result == DragDropEffect.None)
|
||||||
{
|
{
|
||||||
UpdateDrawPositioning(ref location);
|
UpdateDragPositioning(ref location);
|
||||||
|
|
||||||
// Check if mouse is over header
|
// Check if mouse is over header
|
||||||
if (TestHeaderHit(ref location))
|
if (TestHeaderHit(ref location))
|
||||||
@@ -1254,7 +1263,7 @@ namespace FlaxEditor.GUI.Timeline
|
|||||||
|
|
||||||
// Clear cache
|
// Clear cache
|
||||||
_isDragOverHeader = false;
|
_isDragOverHeader = false;
|
||||||
_dragOverMode = DragItemPositioning.None;
|
ClearDragPositioning();
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -1262,15 +1271,15 @@ namespace FlaxEditor.GUI.Timeline
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void OnDragLeave()
|
public override void OnDragLeave()
|
||||||
{
|
{
|
||||||
base.OnDragLeave();
|
|
||||||
|
|
||||||
// Clear cache
|
// Clear cache
|
||||||
if (_isDragOverHeader)
|
if (_isDragOverHeader)
|
||||||
{
|
{
|
||||||
_isDragOverHeader = false;
|
_isDragOverHeader = false;
|
||||||
OnDragLeaveHeader();
|
OnDragLeaveHeader();
|
||||||
}
|
}
|
||||||
_dragOverMode = DragItemPositioning.None;
|
ClearDragPositioning();
|
||||||
|
|
||||||
|
base.OnDragLeave();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
|||||||
@@ -204,7 +204,7 @@ namespace FlaxEditor.GUI.Timeline.Tracks
|
|||||||
b.TooltipText = Utilities.Utils.GetTooltip(actorNode.Actor);
|
b.TooltipText = Utilities.Utils.GetTooltip(actorNode.Actor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
menu.AddButton("Select...", OnClickedSelect).TooltipText = "Opens actor picker dialog to select the target actor for this track";
|
menu.AddButton("Retarget...", OnClickedSelect).TooltipText = "Opens actor picker dialog to select the target actor for this track";
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ using System.Reflection;
|
|||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using FlaxEditor.GUI.Timeline.Undo;
|
using FlaxEditor.GUI.Timeline.Undo;
|
||||||
|
using FlaxEditor.Scripting;
|
||||||
|
using FlaxEditor.Utilities;
|
||||||
using FlaxEngine;
|
using FlaxEngine;
|
||||||
using FlaxEngine.GUI;
|
using FlaxEngine.GUI;
|
||||||
using FlaxEngine.Utilities;
|
using FlaxEngine.Utilities;
|
||||||
@@ -56,7 +58,6 @@ namespace FlaxEditor.GUI.Timeline.Tracks
|
|||||||
throw new Exception("Invalid track data.");
|
throw new Exception("Invalid track data.");
|
||||||
|
|
||||||
var keyframes = new KeyframesEditor.Keyframe[keyframesCount];
|
var keyframes = new KeyframesEditor.Keyframe[keyframesCount];
|
||||||
var dataBuffer = new byte[e.ValueSize];
|
|
||||||
var propertyType = TypeUtils.GetManagedType(e.MemberTypeName);
|
var propertyType = TypeUtils.GetManagedType(e.MemberTypeName);
|
||||||
if (propertyType == null)
|
if (propertyType == null)
|
||||||
{
|
{
|
||||||
@@ -67,20 +68,40 @@ namespace FlaxEditor.GUI.Timeline.Tracks
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
GCHandle handle = GCHandle.Alloc(dataBuffer, GCHandleType.Pinned);
|
if (e.ValueSize != 0)
|
||||||
for (int i = 0; i < keyframesCount; i++)
|
|
||||||
{
|
{
|
||||||
var time = stream.ReadSingle();
|
// POD value type - use raw memory
|
||||||
stream.Read(dataBuffer, 0, e.ValueSize);
|
var dataBuffer = new byte[e.ValueSize];
|
||||||
var value = Marshal.PtrToStructure(handle.AddrOfPinnedObject(), propertyType);
|
GCHandle handle = GCHandle.Alloc(dataBuffer, GCHandleType.Pinned);
|
||||||
|
for (int i = 0; i < keyframesCount; i++)
|
||||||
keyframes[i] = new KeyframesEditor.Keyframe
|
|
||||||
{
|
{
|
||||||
Time = time,
|
var time = stream.ReadSingle();
|
||||||
Value = value,
|
stream.Read(dataBuffer, 0, e.ValueSize);
|
||||||
};
|
var value = Marshal.PtrToStructure(handle.AddrOfPinnedObject(), propertyType);
|
||||||
|
|
||||||
|
keyframes[i] = new KeyframesEditor.Keyframe
|
||||||
|
{
|
||||||
|
Time = time,
|
||||||
|
Value = value,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
handle.Free();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Generic value - use Json storage (as UTF-8)
|
||||||
|
for (int i = 0; i < keyframesCount; i++)
|
||||||
|
{
|
||||||
|
var time = stream.ReadSingle();
|
||||||
|
var len = stream.ReadInt32();
|
||||||
|
var value = len != 0 ? FlaxEngine.Json.JsonSerializer.Deserialize(Encoding.UTF8.GetString(stream.ReadBytes(len)), propertyType) : null;
|
||||||
|
keyframes[i] = new KeyframesEditor.Keyframe
|
||||||
|
{
|
||||||
|
Time = time,
|
||||||
|
Value = value,
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
handle.Free();
|
|
||||||
|
|
||||||
e.Keyframes.DefaultValue = e.GetDefaultValue(propertyType);
|
e.Keyframes.DefaultValue = e.GetDefaultValue(propertyType);
|
||||||
e.Keyframes.SetKeyframes(keyframes);
|
e.Keyframes.SetKeyframes(keyframes);
|
||||||
@@ -113,17 +134,35 @@ namespace FlaxEditor.GUI.Timeline.Tracks
|
|||||||
stream.Write(propertyTypeNameData);
|
stream.Write(propertyTypeNameData);
|
||||||
stream.Write('\0');
|
stream.Write('\0');
|
||||||
|
|
||||||
var dataBuffer = new byte[e.ValueSize];
|
if (e.ValueSize != 0)
|
||||||
IntPtr ptr = Marshal.AllocHGlobal(e.ValueSize);
|
|
||||||
for (int i = 0; i < keyframes.Count; i++)
|
|
||||||
{
|
{
|
||||||
var keyframe = keyframes[i];
|
// POD value type - use raw memory
|
||||||
Marshal.StructureToPtr(keyframe.Value, ptr, true);
|
var dataBuffer = new byte[e.ValueSize];
|
||||||
Marshal.Copy(ptr, dataBuffer, 0, e.ValueSize);
|
IntPtr ptr = Marshal.AllocHGlobal(e.ValueSize);
|
||||||
stream.Write(keyframe.Time);
|
for (int i = 0; i < keyframes.Count; i++)
|
||||||
stream.Write(dataBuffer);
|
{
|
||||||
|
var keyframe = keyframes[i];
|
||||||
|
Marshal.StructureToPtr(keyframe.Value, ptr, true);
|
||||||
|
Marshal.Copy(ptr, dataBuffer, 0, e.ValueSize);
|
||||||
|
stream.Write(keyframe.Time);
|
||||||
|
stream.Write(dataBuffer);
|
||||||
|
}
|
||||||
|
Marshal.FreeHGlobal(ptr);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Generic value - use Json storage (as UTF-8)
|
||||||
|
for (int i = 0; i < keyframes.Count; i++)
|
||||||
|
{
|
||||||
|
var keyframe = keyframes[i];
|
||||||
|
stream.Write(keyframe.Time);
|
||||||
|
var json = keyframe.Value != null ? FlaxEngine.Json.JsonSerializer.Serialize(keyframe.Value) : null;
|
||||||
|
var len = json?.Length ?? 0;
|
||||||
|
stream.Write(len);
|
||||||
|
if (len > 0)
|
||||||
|
stream.Write(Encoding.UTF8.GetBytes(json));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Marshal.FreeHGlobal(ptr);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private byte[] _keyframesEditingStartData;
|
private byte[] _keyframesEditingStartData;
|
||||||
@@ -281,7 +320,7 @@ namespace FlaxEditor.GUI.Timeline.Tracks
|
|||||||
private void OnKeyframesEditingEnd()
|
private void OnKeyframesEditingEnd()
|
||||||
{
|
{
|
||||||
var after = EditTrackAction.CaptureData(this);
|
var after = EditTrackAction.CaptureData(this);
|
||||||
if (!Utils.ArraysEqual(_keyframesEditingStartData, after))
|
if (!FlaxEngine.Utils.ArraysEqual(_keyframesEditingStartData, after))
|
||||||
Timeline.AddBatchedUndoAction(new EditTrackAction(Timeline, this, _keyframesEditingStartData, after));
|
Timeline.AddBatchedUndoAction(new EditTrackAction(Timeline, this, _keyframesEditingStartData, after));
|
||||||
_keyframesEditingStartData = null;
|
_keyframesEditingStartData = null;
|
||||||
}
|
}
|
||||||
@@ -308,7 +347,10 @@ namespace FlaxEditor.GUI.Timeline.Tracks
|
|||||||
/// <returns>The default value.</returns>
|
/// <returns>The default value.</returns>
|
||||||
protected virtual object GetDefaultValue(Type propertyType)
|
protected virtual object GetDefaultValue(Type propertyType)
|
||||||
{
|
{
|
||||||
return Activator.CreateInstance(propertyType);
|
var value = TypeUtils.GetDefaultValue(new ScriptType(propertyType));
|
||||||
|
if (value == null)
|
||||||
|
value = Activator.CreateInstance(propertyType);
|
||||||
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
|||||||
@@ -424,6 +424,7 @@ namespace FlaxEditor.GUI.Timeline.Tracks
|
|||||||
{ typeof(Guid), KeyframesPropertyTrack.GetArchetype() },
|
{ typeof(Guid), KeyframesPropertyTrack.GetArchetype() },
|
||||||
{ typeof(DateTime), KeyframesPropertyTrack.GetArchetype() },
|
{ typeof(DateTime), KeyframesPropertyTrack.GetArchetype() },
|
||||||
{ typeof(TimeSpan), KeyframesPropertyTrack.GetArchetype() },
|
{ typeof(TimeSpan), KeyframesPropertyTrack.GetArchetype() },
|
||||||
|
{ typeof(LocalizedString), KeyframesPropertyTrack.GetArchetype() },
|
||||||
{ typeof(string), StringPropertyTrack.GetArchetype() },
|
{ typeof(string), StringPropertyTrack.GetArchetype() },
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using FlaxEditor.Options;
|
||||||
using FlaxEngine;
|
using FlaxEngine;
|
||||||
using FlaxEngine.Assertions;
|
using FlaxEngine.Assertions;
|
||||||
using FlaxEngine.GUI;
|
using FlaxEngine.GUI;
|
||||||
@@ -43,7 +44,7 @@ namespace FlaxEditor.GUI.Tree
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The TreeNode that is being dragged over. This could have a value when not dragging.
|
/// The TreeNode that is being dragged over. This could have a value when not dragging.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public TreeNode DraggedOverNode = null;
|
internal TreeNode DraggedOverNode = null;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Action fired when tree nodes selection gets changed.
|
/// Action fired when tree nodes selection gets changed.
|
||||||
@@ -315,10 +316,7 @@ namespace FlaxEditor.GUI.Tree
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
private void BulkSelectUpdateExpanded(bool select = true)
|
||||||
/// Select all expanded nodes
|
|
||||||
/// </summary>
|
|
||||||
public void SelectAllExpanded()
|
|
||||||
{
|
{
|
||||||
if (_supportMultiSelect)
|
if (_supportMultiSelect)
|
||||||
{
|
{
|
||||||
@@ -327,7 +325,8 @@ namespace FlaxEditor.GUI.Tree
|
|||||||
|
|
||||||
// Update selection
|
// Update selection
|
||||||
Selection.Clear();
|
Selection.Clear();
|
||||||
WalkSelectExpandedTree(Selection, _children[0] as TreeNode);
|
if (select)
|
||||||
|
WalkSelectExpandedTree(Selection, _children[0] as TreeNode);
|
||||||
|
|
||||||
// Check if changed
|
// Check if changed
|
||||||
if (Selection.Count != prev.Count || !Selection.SequenceEqual(prev))
|
if (Selection.Count != prev.Count || !Selection.SequenceEqual(prev))
|
||||||
@@ -338,6 +337,22 @@ namespace FlaxEditor.GUI.Tree
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Select all expanded nodes
|
||||||
|
/// </summary>
|
||||||
|
public void SelectAllExpanded()
|
||||||
|
{
|
||||||
|
BulkSelectUpdateExpanded(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Deselect all nodes
|
||||||
|
/// </summary>
|
||||||
|
public void DeselectAll()
|
||||||
|
{
|
||||||
|
BulkSelectUpdateExpanded(false);
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void Update(float deltaTime)
|
public override void Update(float deltaTime)
|
||||||
{
|
{
|
||||||
@@ -472,14 +487,19 @@ namespace FlaxEditor.GUI.Tree
|
|||||||
// Check if can use multi selection
|
// Check if can use multi selection
|
||||||
if (_supportMultiSelect)
|
if (_supportMultiSelect)
|
||||||
{
|
{
|
||||||
bool isCtrlDown = Root.GetKey(KeyboardKeys.Control);
|
InputOptions options = Editor.Instance.Options.Options.Input;
|
||||||
|
|
||||||
// Select all expanded nodes
|
// Select all expanded nodes
|
||||||
if (key == KeyboardKeys.A && isCtrlDown)
|
if (options.SelectAll.Process(this))
|
||||||
{
|
{
|
||||||
SelectAllExpanded();
|
SelectAllExpanded();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
else if (options.DeselectAll.Process(this))
|
||||||
|
{
|
||||||
|
DeselectAll();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return base.OnKeyDown(key);
|
return base.OnKeyDown(key);
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ namespace FlaxEditor.GUI.Tree
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The default drag insert position margin.
|
/// The default drag insert position margin.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public const float DefaultDragInsertPositionMargin = 2.0f;
|
public const float DefaultDragInsertPositionMargin = 3.0f;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The default node offset on Y axis.
|
/// The default node offset on Y axis.
|
||||||
@@ -42,6 +42,7 @@ namespace FlaxEditor.GUI.Tree
|
|||||||
|
|
||||||
private DragItemPositioning _dragOverMode;
|
private DragItemPositioning _dragOverMode;
|
||||||
private bool _isDragOverHeader;
|
private bool _isDragOverHeader;
|
||||||
|
private static ulong _dragEndFrame;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the text.
|
/// Gets or sets the text.
|
||||||
@@ -546,14 +547,33 @@ namespace FlaxEditor.GUI.Tree
|
|||||||
/// Updates the drag over mode based on the given mouse location.
|
/// Updates the drag over mode based on the given mouse location.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="location">The location.</param>
|
/// <param name="location">The location.</param>
|
||||||
private void UpdateDrawPositioning(ref Float2 location)
|
private void UpdateDragPositioning(ref Float2 location)
|
||||||
{
|
{
|
||||||
|
// Check collision with drag areas
|
||||||
if (new Rectangle(_headerRect.X, _headerRect.Y - DefaultDragInsertPositionMargin - DefaultNodeOffsetY, _headerRect.Width, DefaultDragInsertPositionMargin * 2.0f).Contains(location))
|
if (new Rectangle(_headerRect.X, _headerRect.Y - DefaultDragInsertPositionMargin - DefaultNodeOffsetY, _headerRect.Width, DefaultDragInsertPositionMargin * 2.0f).Contains(location))
|
||||||
_dragOverMode = DragItemPositioning.Above;
|
_dragOverMode = DragItemPositioning.Above;
|
||||||
else if ((IsCollapsed || !HasAnyVisibleChild) && new Rectangle(_headerRect.X, _headerRect.Bottom - DefaultDragInsertPositionMargin, _headerRect.Width, DefaultDragInsertPositionMargin * 2.0f).Contains(location))
|
else if ((IsCollapsed || !HasAnyVisibleChild) && new Rectangle(_headerRect.X, _headerRect.Bottom - DefaultDragInsertPositionMargin, _headerRect.Width, DefaultDragInsertPositionMargin * 2.0f).Contains(location))
|
||||||
_dragOverMode = DragItemPositioning.Below;
|
_dragOverMode = DragItemPositioning.Below;
|
||||||
else
|
else
|
||||||
_dragOverMode = DragItemPositioning.At;
|
_dragOverMode = DragItemPositioning.At;
|
||||||
|
|
||||||
|
// Update DraggedOverNode
|
||||||
|
var tree = ParentTree;
|
||||||
|
if (_dragOverMode == DragItemPositioning.None)
|
||||||
|
{
|
||||||
|
if (tree != null && tree.DraggedOverNode == this)
|
||||||
|
tree.DraggedOverNode = null;
|
||||||
|
}
|
||||||
|
else if (tree != null)
|
||||||
|
tree.DraggedOverNode = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ClearDragPositioning()
|
||||||
|
{
|
||||||
|
_dragOverMode = DragItemPositioning.None;
|
||||||
|
var tree = ParentTree;
|
||||||
|
if (tree != null && tree.DraggedOverNode == this)
|
||||||
|
tree.DraggedOverNode = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -661,27 +681,19 @@ namespace FlaxEditor.GUI.Tree
|
|||||||
// Draw drag and drop effect
|
// Draw drag and drop effect
|
||||||
if (IsDragOver && _tree.DraggedOverNode == this)
|
if (IsDragOver && _tree.DraggedOverNode == this)
|
||||||
{
|
{
|
||||||
Color dragOverColor = style.BackgroundSelected;
|
|
||||||
Rectangle rect;
|
|
||||||
switch (_dragOverMode)
|
switch (_dragOverMode)
|
||||||
{
|
{
|
||||||
case DragItemPositioning.At:
|
case DragItemPositioning.At:
|
||||||
dragOverColor *= 0.6f;
|
Render2D.FillRectangle(textRect, style.Selection);
|
||||||
rect = textRect;
|
Render2D.DrawRectangle(textRect, style.SelectionBorder);
|
||||||
break;
|
break;
|
||||||
case DragItemPositioning.Above:
|
case DragItemPositioning.Above:
|
||||||
dragOverColor *= 1.2f;
|
Render2D.DrawRectangle(new Rectangle(textRect.X, textRect.Top - DefaultDragInsertPositionMargin * 0.5f - DefaultNodeOffsetY - _margin.Top, textRect.Width, DefaultDragInsertPositionMargin), style.SelectionBorder);
|
||||||
rect = new Rectangle(textRect.X, textRect.Top - DefaultDragInsertPositionMargin - DefaultNodeOffsetY - _margin.Top, textRect.Width, DefaultDragInsertPositionMargin * 2.0f);
|
|
||||||
break;
|
break;
|
||||||
case DragItemPositioning.Below:
|
case DragItemPositioning.Below:
|
||||||
dragOverColor *= 1.2f;
|
Render2D.DrawRectangle(new Rectangle(textRect.X, textRect.Bottom + _margin.Bottom - DefaultDragInsertPositionMargin * 0.5f, textRect.Width, DefaultDragInsertPositionMargin), style.SelectionBorder);
|
||||||
rect = new Rectangle(textRect.X, textRect.Bottom + _margin.Bottom - DefaultDragInsertPositionMargin, textRect.Width, DefaultDragInsertPositionMargin * 2.0f);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
rect = Rectangle.Empty;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
Render2D.FillRectangle(rect, dragOverColor);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Base
|
// Base
|
||||||
@@ -736,9 +748,8 @@ namespace FlaxEditor.GUI.Tree
|
|||||||
UpdateMouseOverFlags(location);
|
UpdateMouseOverFlags(location);
|
||||||
|
|
||||||
// Clear flag for left button
|
// Clear flag for left button
|
||||||
if (button == MouseButton.Left)
|
if (button == MouseButton.Left && _isMouseDown)
|
||||||
{
|
{
|
||||||
// Clear flag
|
|
||||||
_isMouseDown = false;
|
_isMouseDown = false;
|
||||||
_mouseDownTime = -1;
|
_mouseDownTime = -1;
|
||||||
}
|
}
|
||||||
@@ -746,6 +757,10 @@ namespace FlaxEditor.GUI.Tree
|
|||||||
// Check if mouse hits bar and node isn't a root
|
// Check if mouse hits bar and node isn't a root
|
||||||
if (_mouseOverHeader)
|
if (_mouseOverHeader)
|
||||||
{
|
{
|
||||||
|
// Skip mouse up event right after drag drop ends
|
||||||
|
if (button == MouseButton.Left && Engine.FrameCount - _dragEndFrame < 10)
|
||||||
|
return true;
|
||||||
|
|
||||||
// Prevent from selecting node when user is just clicking at an arrow
|
// Prevent from selecting node when user is just clicking at an arrow
|
||||||
if (!_mouseOverArrow)
|
if (!_mouseOverArrow)
|
||||||
{
|
{
|
||||||
@@ -937,20 +952,18 @@ namespace FlaxEditor.GUI.Tree
|
|||||||
_dragOverMode = DragItemPositioning.None;
|
_dragOverMode = DragItemPositioning.None;
|
||||||
if (result == DragDropEffect.None)
|
if (result == DragDropEffect.None)
|
||||||
{
|
{
|
||||||
UpdateDrawPositioning(ref location);
|
UpdateDragPositioning(ref location);
|
||||||
if (ParentTree != null)
|
|
||||||
ParentTree.DraggedOverNode = this;
|
|
||||||
|
|
||||||
// Check if mouse is over header
|
// Check if mouse is over header
|
||||||
_isDragOverHeader = TestHeaderHit(ref location);
|
_isDragOverHeader = TestHeaderHit(ref location);
|
||||||
if (_isDragOverHeader)
|
if (_isDragOverHeader)
|
||||||
{
|
{
|
||||||
// Check if mouse is over arrow
|
if (ParentTree != null)
|
||||||
|
ParentTree.DraggedOverNode = this;
|
||||||
|
|
||||||
|
// Expand node if mouse goes over arrow
|
||||||
if (ArrowRect.Contains(location) && HasAnyVisibleChild)
|
if (ArrowRect.Contains(location) && HasAnyVisibleChild)
|
||||||
{
|
|
||||||
// Expand node (no animation)
|
|
||||||
Expand(true);
|
Expand(true);
|
||||||
}
|
|
||||||
|
|
||||||
result = OnDragEnterHeader(data);
|
result = OnDragEnterHeader(data);
|
||||||
}
|
}
|
||||||
@@ -968,26 +981,31 @@ namespace FlaxEditor.GUI.Tree
|
|||||||
var result = base.OnDragMove(ref location, data);
|
var result = base.OnDragMove(ref location, data);
|
||||||
|
|
||||||
// Check if no children handled that event
|
// Check if no children handled that event
|
||||||
_dragOverMode = DragItemPositioning.None;
|
ClearDragPositioning();
|
||||||
if (result == DragDropEffect.None)
|
if (result == DragDropEffect.None)
|
||||||
{
|
{
|
||||||
UpdateDrawPositioning(ref location);
|
UpdateDragPositioning(ref location);
|
||||||
|
|
||||||
// Check if mouse is over header
|
// Check if mouse is over header
|
||||||
bool isDragOverHeader = TestHeaderHit(ref location);
|
bool isDragOverHeader = TestHeaderHit(ref location);
|
||||||
if (isDragOverHeader)
|
if (isDragOverHeader)
|
||||||
{
|
{
|
||||||
if (ArrowRect.Contains(location) && HasAnyVisibleChild)
|
if (ParentTree != null)
|
||||||
{
|
ParentTree.DraggedOverNode = this;
|
||||||
// Expand node (no animation)
|
|
||||||
Expand(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// Expand node if mouse goes over arrow
|
||||||
|
if (ArrowRect.Contains(location) && HasAnyVisibleChild)
|
||||||
|
Expand(true);
|
||||||
|
|
||||||
if (!_isDragOverHeader)
|
if (!_isDragOverHeader)
|
||||||
result = OnDragEnterHeader(data);
|
result = OnDragEnterHeader(data);
|
||||||
else
|
else
|
||||||
result = OnDragMoveHeader(data);
|
result = OnDragMoveHeader(data);
|
||||||
}
|
}
|
||||||
|
else if (_isDragOverHeader)
|
||||||
|
{
|
||||||
|
OnDragLeaveHeader();
|
||||||
|
}
|
||||||
_isDragOverHeader = isDragOverHeader;
|
_isDragOverHeader = isDragOverHeader;
|
||||||
|
|
||||||
if (result == DragDropEffect.None)
|
if (result == DragDropEffect.None)
|
||||||
@@ -1005,7 +1023,8 @@ namespace FlaxEditor.GUI.Tree
|
|||||||
// Check if no children handled that event
|
// Check if no children handled that event
|
||||||
if (result == DragDropEffect.None)
|
if (result == DragDropEffect.None)
|
||||||
{
|
{
|
||||||
UpdateDrawPositioning(ref location);
|
UpdateDragPositioning(ref location);
|
||||||
|
_dragEndFrame = Engine.FrameCount;
|
||||||
|
|
||||||
// Check if mouse is over header
|
// Check if mouse is over header
|
||||||
if (TestHeaderHit(ref location))
|
if (TestHeaderHit(ref location))
|
||||||
@@ -1016,9 +1035,7 @@ namespace FlaxEditor.GUI.Tree
|
|||||||
|
|
||||||
// Clear cache
|
// Clear cache
|
||||||
_isDragOverHeader = false;
|
_isDragOverHeader = false;
|
||||||
_dragOverMode = DragItemPositioning.None;
|
ClearDragPositioning();
|
||||||
if (ParentTree != null)
|
|
||||||
ParentTree.DraggedOverNode = null;
|
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -1032,7 +1049,7 @@ namespace FlaxEditor.GUI.Tree
|
|||||||
_isDragOverHeader = false;
|
_isDragOverHeader = false;
|
||||||
OnDragLeaveHeader();
|
OnDragLeaveHeader();
|
||||||
}
|
}
|
||||||
_dragOverMode = DragItemPositioning.None;
|
ClearDragPositioning();
|
||||||
|
|
||||||
base.OnDragLeave();
|
base.OnDragLeave();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -263,6 +263,8 @@ namespace FlaxEditor.Gizmo
|
|||||||
// Note: because selection may contain objects and their children we have to split them and get only parents.
|
// Note: because selection may contain objects and their children we have to split them and get only parents.
|
||||||
// Later during transformation we apply translation/scale/rotation only on them (children inherit transformations)
|
// Later during transformation we apply translation/scale/rotation only on them (children inherit transformations)
|
||||||
SceneGraphTools.BuildNodesParents(_selection, _selectionParents);
|
SceneGraphTools.BuildNodesParents(_selection, _selectionParents);
|
||||||
|
|
||||||
|
base.OnSelectionChanged(newSelection);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
|||||||
@@ -437,8 +437,6 @@ namespace FlaxEditor.Gizmo
|
|||||||
{
|
{
|
||||||
case Mode.Translate:
|
case Mode.Translate:
|
||||||
UpdateTranslateScale();
|
UpdateTranslateScale();
|
||||||
if (Owner.SnapToVertex)
|
|
||||||
UpdateVertexSnapping();
|
|
||||||
break;
|
break;
|
||||||
case Mode.Scale:
|
case Mode.Scale:
|
||||||
UpdateTranslateScale();
|
UpdateTranslateScale();
|
||||||
@@ -447,6 +445,8 @@ namespace FlaxEditor.Gizmo
|
|||||||
UpdateRotate(dt);
|
UpdateRotate(dt);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
if (Owner.SnapToVertex)
|
||||||
|
UpdateVertexSnapping();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -553,41 +553,21 @@ namespace FlaxEditor.Gizmo
|
|||||||
for (int i = 0; i < SelectionCount; i++)
|
for (int i = 0; i < SelectionCount; i++)
|
||||||
{
|
{
|
||||||
var obj = GetSelectedObject(i);
|
var obj = GetSelectedObject(i);
|
||||||
if (obj.CanVertexSnap && obj.RayCastSelf(ref ray, out var distance, out _) && distance < closestDistance)
|
if (obj.RayCastSelf(ref ray, out var distance, out _) && distance < closestDistance)
|
||||||
{
|
{
|
||||||
closestDistance = distance;
|
closestDistance = distance;
|
||||||
closestObject = obj;
|
closestObject = obj;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (closestObject == null)
|
if (closestObject == null)
|
||||||
{
|
return; // ignore it if there is nothing under the mouse closestObject is only null if ray caster missed everything or Selection Count == 0
|
||||||
// Find the closest object in selection (in case ray didn't hit anything)
|
|
||||||
for (int i = 0; i < SelectionCount; i++)
|
|
||||||
{
|
|
||||||
var obj = GetSelectedObject(i);
|
|
||||||
if (obj.CanVertexSnap)
|
|
||||||
{
|
|
||||||
GetSelectedObjectsBounds(out var bounds, out _);
|
|
||||||
CollisionsHelper.ClosestPointBoxPoint(ref bounds, ref ray.Ray.Position, out var point);
|
|
||||||
var distance = Vector3.Distance(ref point, ref ray.Ray.Position);
|
|
||||||
if (distance < closestDistance)
|
|
||||||
{
|
|
||||||
closestDistance = distance;
|
|
||||||
closestObject = obj;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_vertexSnapObject = closestObject;
|
|
||||||
if (closestObject == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Find the closest vertex to bounding box point (collision detection approximation)
|
_vertexSnapObject = closestObject;
|
||||||
var closestPoint = ray.Ray.GetPoint(closestDistance);
|
if (!closestObject.OnVertexSnap(ref ray.Ray, closestDistance, out _vertexSnapPoint))
|
||||||
if (!closestObject.OnVertexSnap(ref closestPoint, out _vertexSnapPoint))
|
|
||||||
{
|
{
|
||||||
// Failed to get the closest point
|
// The OnVertexSnap is unimplemented or failed to get point return because there is nothing to do
|
||||||
_vertexSnapPoint = closestPoint;
|
_vertexSnapPoint = Vector3.Zero;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Transform back to the local space of the object to work when moving it
|
// Transform back to the local space of the object to work when moving it
|
||||||
@@ -619,12 +599,11 @@ namespace FlaxEditor.Gizmo
|
|||||||
for (int i = 0; i < SelectionCount; i++)
|
for (int i = 0; i < SelectionCount; i++)
|
||||||
rayCast.ExcludeObjects.Add(GetSelectedObject(i));
|
rayCast.ExcludeObjects.Add(GetSelectedObject(i));
|
||||||
var hit = Owner.SceneGraphRoot.RayCast(ref rayCast, out var distance, out var _);
|
var hit = Owner.SceneGraphRoot.RayCast(ref rayCast, out var distance, out var _);
|
||||||
if (hit != null && hit.CanVertexSnap)
|
if (hit != null)
|
||||||
{
|
{
|
||||||
var point = rayCast.Ray.GetPoint(distance);
|
if (hit.OnVertexSnap(ref rayCast.Ray, distance, out var pointSnapped)
|
||||||
if (hit.OnVertexSnap(ref point, out var pointSnapped)
|
|
||||||
//&& Vector3.Distance(point, pointSnapped) <= 25.0f
|
//&& Vector3.Distance(point, pointSnapped) <= 25.0f
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
_vertexSnapObjectTo = hit;
|
_vertexSnapObjectTo = hit;
|
||||||
_vertexSnapPointTo = hit.Transform.WorldToLocal(pointSnapped);
|
_vertexSnapPointTo = hit.Transform.WorldToLocal(pointSnapped);
|
||||||
@@ -712,5 +691,12 @@ namespace FlaxEditor.Gizmo
|
|||||||
protected virtual void OnDuplicate()
|
protected virtual void OnDuplicate()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void OnSelectionChanged(List<SceneGraphNode> newSelection)
|
||||||
|
{
|
||||||
|
EndVertexSnapping();
|
||||||
|
UpdateGizmoPosition();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -509,6 +509,21 @@ DEFINE_INTERNAL_CALL(bool) EditorInternal_CanSetToRoot(Prefab* prefab, Actor* ta
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DEFINE_INTERNAL_CALL(void) EditorInternal_GetPrefabNestedObject(Guid* prefabId, Guid* prefabObjectId, Guid* outPrefabId, Guid* outPrefabObjectId)
|
||||||
|
{
|
||||||
|
*outPrefabId = Guid::Empty;
|
||||||
|
*outPrefabObjectId = Guid::Empty;
|
||||||
|
const auto prefab = Content::Load<Prefab>(*prefabId);
|
||||||
|
if (!prefab)
|
||||||
|
return;
|
||||||
|
const ISerializable::DeserializeStream** prefabObjectDataPtr = prefab->ObjectsDataCache.TryGet(*prefabObjectId);
|
||||||
|
if (!prefabObjectDataPtr)
|
||||||
|
return;
|
||||||
|
const ISerializable::DeserializeStream& prefabObjectData = **prefabObjectDataPtr;
|
||||||
|
JsonTools::GetGuidIfValid(*outPrefabId, prefabObjectData, "PrefabID");
|
||||||
|
JsonTools::GetGuidIfValid(*outPrefabObjectId, prefabObjectData, "PrefabObjectID");
|
||||||
|
}
|
||||||
|
|
||||||
DEFINE_INTERNAL_CALL(float) EditorInternal_GetAnimationTime(AnimatedModel* animatedModel)
|
DEFINE_INTERNAL_CALL(float) EditorInternal_GetAnimationTime(AnimatedModel* animatedModel)
|
||||||
{
|
{
|
||||||
return animatedModel && animatedModel->GraphInstance.State.Count() == 1 ? animatedModel->GraphInstance.State[0].Animation.TimePosition : 0.0f;
|
return animatedModel && animatedModel->GraphInstance.State.Count() == 1 ? animatedModel->GraphInstance.State[0].Animation.TimePosition : 0.0f;
|
||||||
|
|||||||
@@ -278,13 +278,7 @@ void ManagedEditor::Update()
|
|||||||
void ManagedEditor::Exit()
|
void ManagedEditor::Exit()
|
||||||
{
|
{
|
||||||
if (WasExitCalled)
|
if (WasExitCalled)
|
||||||
{
|
|
||||||
// Ups xD
|
|
||||||
LOG(Warning, "Managed Editor exit called after exit or before init.");
|
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
// Set flag
|
|
||||||
WasExitCalled = true;
|
WasExitCalled = true;
|
||||||
|
|
||||||
// Skip if managed object is missing
|
// Skip if managed object is missing
|
||||||
|
|||||||
@@ -225,8 +225,15 @@ namespace FlaxEditor.Modules
|
|||||||
throw new ArgumentException("Missing prefab to apply.");
|
throw new ArgumentException("Missing prefab to apply.");
|
||||||
PrefabApplying?.Invoke(prefab, instance);
|
PrefabApplying?.Invoke(prefab, instance);
|
||||||
|
|
||||||
|
// When applying changes to prefab from actor in level ignore it's root transformation (see ActorEditor.ProcessDiff)
|
||||||
|
var originalTransform = instance.LocalTransform;
|
||||||
|
if (instance.IsPrefabRoot && instance.Scene != null)
|
||||||
|
instance.LocalTransform = prefab.GetDefaultInstance().Transform;
|
||||||
|
|
||||||
// Call backend
|
// Call backend
|
||||||
if (PrefabManager.Internal_ApplyAll(FlaxEngine.Object.GetUnmanagedPtr(instance)))
|
var failed = PrefabManager.Internal_ApplyAll(FlaxEngine.Object.GetUnmanagedPtr(instance));
|
||||||
|
instance.LocalTransform = originalTransform;
|
||||||
|
if (failed)
|
||||||
throw new Exception("Failed to apply the prefab. See log to learn more.");
|
throw new Exception("Failed to apply the prefab. See log to learn more.");
|
||||||
|
|
||||||
PrefabApplied?.Invoke(prefab, instance);
|
PrefabApplied?.Invoke(prefab, instance);
|
||||||
|
|||||||
@@ -60,13 +60,26 @@ namespace FlaxEditor.Modules
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void BulkScenesSelectUpdate(bool select = true)
|
||||||
|
{
|
||||||
|
// Blank list deselects all
|
||||||
|
Select(select ? Editor.Scene.Root.ChildNodes : new List<SceneGraphNode>());
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Selects all scenes.
|
/// Selects all scenes.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void SelectAllScenes()
|
public void SelectAllScenes()
|
||||||
{
|
{
|
||||||
// Select all scenes (linked to the root node)
|
BulkScenesSelectUpdate(true);
|
||||||
Select(Editor.Scene.Root.ChildNodes);
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Deselects all scenes.
|
||||||
|
/// </summary>
|
||||||
|
public void DeselectAllScenes()
|
||||||
|
{
|
||||||
|
BulkScenesSelectUpdate(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -320,7 +333,7 @@ namespace FlaxEditor.Modules
|
|||||||
actorNode.PostSpawn();
|
actorNode.PostSpawn();
|
||||||
|
|
||||||
// Create undo action
|
// Create undo action
|
||||||
IUndoAction action = new DeleteActorsAction(new List<SceneGraphNode>(1) { actorNode }, true);
|
IUndoAction action = new DeleteActorsAction(actorNode, true);
|
||||||
if (autoSelect)
|
if (autoSelect)
|
||||||
{
|
{
|
||||||
var before = Selection.ToArray();
|
var before = Selection.ToArray();
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ using System.IO;
|
|||||||
using FlaxEditor.SceneGraph;
|
using FlaxEditor.SceneGraph;
|
||||||
using FlaxEditor.SceneGraph.Actors;
|
using FlaxEditor.SceneGraph.Actors;
|
||||||
using FlaxEngine;
|
using FlaxEngine;
|
||||||
|
using FlaxEngine.GUI;
|
||||||
using Object = FlaxEngine.Object;
|
using Object = FlaxEngine.Object;
|
||||||
|
|
||||||
namespace FlaxEditor.Modules
|
namespace FlaxEditor.Modules
|
||||||
@@ -454,6 +455,41 @@ namespace FlaxEditor.Modules
|
|||||||
Profiler.EndEvent();
|
Profiler.EndEvent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Dictionary<ContainerControl, Float2> _uiRootSizes;
|
||||||
|
|
||||||
|
internal void OnSaveStart(ContainerControl uiRoot)
|
||||||
|
{
|
||||||
|
// Force viewport UI to have fixed size during scene/prefabs saving to result in stable data (less mess in version control diffs)
|
||||||
|
if (_uiRootSizes == null)
|
||||||
|
_uiRootSizes = new Dictionary<ContainerControl, Float2>();
|
||||||
|
_uiRootSizes[uiRoot] = uiRoot.Size;
|
||||||
|
uiRoot.Size = new Float2(1920, 1080);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void OnSaveEnd(ContainerControl uiRoot)
|
||||||
|
{
|
||||||
|
// Restore cached size of the UI root container
|
||||||
|
if (_uiRootSizes != null && _uiRootSizes.Remove(uiRoot, out var size))
|
||||||
|
{
|
||||||
|
uiRoot.Size = size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnSceneSaving(Scene scene, Guid sceneId)
|
||||||
|
{
|
||||||
|
OnSaveStart(RootControl.GameRoot);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnSceneSaved(Scene scene, Guid sceneId)
|
||||||
|
{
|
||||||
|
OnSaveEnd(RootControl.GameRoot);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnSceneSaveError(Scene scene, Guid sceneId)
|
||||||
|
{
|
||||||
|
OnSaveEnd(RootControl.GameRoot);
|
||||||
|
}
|
||||||
|
|
||||||
private void OnSceneLoaded(Scene scene, Guid sceneId)
|
private void OnSceneLoaded(Scene scene, Guid sceneId)
|
||||||
{
|
{
|
||||||
var startTime = DateTime.UtcNow;
|
var startTime = DateTime.UtcNow;
|
||||||
@@ -659,6 +695,9 @@ namespace FlaxEditor.Modules
|
|||||||
Root = new ScenesRootNode();
|
Root = new ScenesRootNode();
|
||||||
|
|
||||||
// Bind events
|
// Bind events
|
||||||
|
Level.SceneSaving += OnSceneSaving;
|
||||||
|
Level.SceneSaved += OnSceneSaved;
|
||||||
|
Level.SceneSaveError += OnSceneSaveError;
|
||||||
Level.SceneLoaded += OnSceneLoaded;
|
Level.SceneLoaded += OnSceneLoaded;
|
||||||
Level.SceneUnloading += OnSceneUnloading;
|
Level.SceneUnloading += OnSceneUnloading;
|
||||||
Level.ActorSpawned += OnActorSpawned;
|
Level.ActorSpawned += OnActorSpawned;
|
||||||
@@ -673,6 +712,9 @@ namespace FlaxEditor.Modules
|
|||||||
public override void OnExit()
|
public override void OnExit()
|
||||||
{
|
{
|
||||||
// Unbind events
|
// Unbind events
|
||||||
|
Level.SceneSaving -= OnSceneSaving;
|
||||||
|
Level.SceneSaved -= OnSceneSaved;
|
||||||
|
Level.SceneSaveError -= OnSceneSaveError;
|
||||||
Level.SceneLoaded -= OnSceneLoaded;
|
Level.SceneLoaded -= OnSceneLoaded;
|
||||||
Level.SceneUnloading -= OnSceneUnloading;
|
Level.SceneUnloading -= OnSceneUnloading;
|
||||||
Level.ActorSpawned -= OnActorSpawned;
|
Level.ActorSpawned -= OnActorSpawned;
|
||||||
|
|||||||
@@ -264,11 +264,14 @@ namespace FlaxEditor.Modules
|
|||||||
_enterPlayFocusedWindow = gameWin;
|
_enterPlayFocusedWindow = gameWin;
|
||||||
|
|
||||||
// Show Game widow if hidden
|
// Show Game widow if hidden
|
||||||
if (gameWin != null && gameWin.FocusOnPlay)
|
if (gameWin != null)
|
||||||
{
|
{
|
||||||
gameWin.FocusGameViewport();
|
if (gameWin.FocusOnPlay)
|
||||||
|
gameWin.FocusGameViewport();
|
||||||
|
gameWin.SetWindowMode(Editor.Options.Options.Interface.DefaultGameWindowMode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Editor.Log("[PlayMode] Enter");
|
Editor.Log("[PlayMode] Enter");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -54,6 +54,7 @@ namespace FlaxEditor.Modules
|
|||||||
private ContextMenuButton _menuEditDelete;
|
private ContextMenuButton _menuEditDelete;
|
||||||
private ContextMenuButton _menuEditDuplicate;
|
private ContextMenuButton _menuEditDuplicate;
|
||||||
private ContextMenuButton _menuEditSelectAll;
|
private ContextMenuButton _menuEditSelectAll;
|
||||||
|
private ContextMenuButton _menuEditDeselectAll;
|
||||||
private ContextMenuButton _menuEditFind;
|
private ContextMenuButton _menuEditFind;
|
||||||
private ContextMenuButton _menuSceneMoveActorToViewport;
|
private ContextMenuButton _menuSceneMoveActorToViewport;
|
||||||
private ContextMenuButton _menuSceneAlignActorWithViewport;
|
private ContextMenuButton _menuSceneAlignActorWithViewport;
|
||||||
@@ -554,6 +555,7 @@ namespace FlaxEditor.Modules
|
|||||||
_menuEditDuplicate = cm.AddButton("Duplicate", inputOptions.Duplicate, Editor.SceneEditing.Duplicate);
|
_menuEditDuplicate = cm.AddButton("Duplicate", inputOptions.Duplicate, Editor.SceneEditing.Duplicate);
|
||||||
cm.AddSeparator();
|
cm.AddSeparator();
|
||||||
_menuEditSelectAll = cm.AddButton("Select all", inputOptions.SelectAll, Editor.SceneEditing.SelectAllScenes);
|
_menuEditSelectAll = cm.AddButton("Select all", inputOptions.SelectAll, Editor.SceneEditing.SelectAllScenes);
|
||||||
|
_menuEditDeselectAll = cm.AddButton("Deselect all", inputOptions.DeselectAll, Editor.SceneEditing.DeselectAllScenes);
|
||||||
_menuCreateParentForSelectedActors = cm.AddButton("Create parent for selected actors", Editor.SceneEditing.CreateParentForSelectedActors);
|
_menuCreateParentForSelectedActors = cm.AddButton("Create parent for selected actors", Editor.SceneEditing.CreateParentForSelectedActors);
|
||||||
_menuEditFind = cm.AddButton("Find", inputOptions.Search, Editor.Windows.SceneWin.Search);
|
_menuEditFind = cm.AddButton("Find", inputOptions.Search, Editor.Windows.SceneWin.Search);
|
||||||
cm.AddSeparator();
|
cm.AddSeparator();
|
||||||
@@ -673,6 +675,7 @@ namespace FlaxEditor.Modules
|
|||||||
_menuEditDelete.ShortKeys = inputOptions.Delete.ToString();
|
_menuEditDelete.ShortKeys = inputOptions.Delete.ToString();
|
||||||
_menuEditDuplicate.ShortKeys = inputOptions.Duplicate.ToString();
|
_menuEditDuplicate.ShortKeys = inputOptions.Duplicate.ToString();
|
||||||
_menuEditSelectAll.ShortKeys = inputOptions.SelectAll.ToString();
|
_menuEditSelectAll.ShortKeys = inputOptions.SelectAll.ToString();
|
||||||
|
_menuEditDeselectAll.ShortKeys = inputOptions.DeselectAll.ToString();
|
||||||
_menuEditFind.ShortKeys = inputOptions.Search.ToString();
|
_menuEditFind.ShortKeys = inputOptions.Search.ToString();
|
||||||
_menuGamePlayGame.ShortKeys = inputOptions.Play.ToString();
|
_menuGamePlayGame.ShortKeys = inputOptions.Play.ToString();
|
||||||
_menuGamePlayCurrentScenes.ShortKeys = inputOptions.PlayCurrentScenes.ToString();
|
_menuGamePlayCurrentScenes.ShortKeys = inputOptions.PlayCurrentScenes.ToString();
|
||||||
@@ -739,6 +742,17 @@ namespace FlaxEditor.Modules
|
|||||||
playActionGroup.SelectedChanged = SetPlayAction;
|
playActionGroup.SelectedChanged = SetPlayAction;
|
||||||
Editor.Options.OptionsChanged += options => { playActionGroup.Selected = options.Interface.PlayButtonAction; };
|
Editor.Options.OptionsChanged += options => { playActionGroup.Selected = options.Interface.PlayButtonAction; };
|
||||||
|
|
||||||
|
var windowModesGroup = new ContextMenuSingleSelectGroup<InterfaceOptions.GameWindowMode>();
|
||||||
|
var windowTypeMenu = _toolStripPlay.ContextMenu.AddChildMenu("Game window mode");
|
||||||
|
windowModesGroup.AddItem("Docked", InterfaceOptions.GameWindowMode.Docked, null, "Shows the game window docked, inside the editor");
|
||||||
|
windowModesGroup.AddItem("Popup", InterfaceOptions.GameWindowMode.PopupWindow, null, "Shows the game window as a popup");
|
||||||
|
windowModesGroup.AddItem("Maximized", InterfaceOptions.GameWindowMode.MaximizedWindow, null, "Shows the game window maximized (Same as pressing F11)");
|
||||||
|
windowModesGroup.AddItem("Borderless", InterfaceOptions.GameWindowMode.BorderlessWindow, null, "Shows the game window borderless");
|
||||||
|
windowModesGroup.AddItemsToContextMenu(windowTypeMenu.ContextMenu);
|
||||||
|
windowModesGroup.Selected = Editor.Options.Options.Interface.DefaultGameWindowMode;
|
||||||
|
windowModesGroup.SelectedChanged = SetGameWindowMode;
|
||||||
|
Editor.Options.OptionsChanged += options => { windowModesGroup.Selected = options.Interface.DefaultGameWindowMode; };
|
||||||
|
|
||||||
_toolStripPause = (ToolStripButton)ToolStrip.AddButton(Editor.Icons.Pause64, Editor.Simulation.RequestResumeOrPause).LinkTooltip($"Pause/Resume game ({inputOptions.Pause})");
|
_toolStripPause = (ToolStripButton)ToolStrip.AddButton(Editor.Icons.Pause64, Editor.Simulation.RequestResumeOrPause).LinkTooltip($"Pause/Resume game ({inputOptions.Pause})");
|
||||||
_toolStripStep = (ToolStripButton)ToolStrip.AddButton(Editor.Icons.Skip64, Editor.Simulation.RequestPlayOneFrame).LinkTooltip("Step one frame in game");
|
_toolStripStep = (ToolStripButton)ToolStrip.AddButton(Editor.Icons.Skip64, Editor.Simulation.RequestPlayOneFrame).LinkTooltip("Step one frame in game");
|
||||||
|
|
||||||
@@ -871,6 +885,7 @@ namespace FlaxEditor.Modules
|
|||||||
_menuEditDelete.Enabled = hasSthSelected;
|
_menuEditDelete.Enabled = hasSthSelected;
|
||||||
_menuEditDuplicate.Enabled = hasSthSelected;
|
_menuEditDuplicate.Enabled = hasSthSelected;
|
||||||
_menuEditSelectAll.Enabled = Level.IsAnySceneLoaded;
|
_menuEditSelectAll.Enabled = Level.IsAnySceneLoaded;
|
||||||
|
_menuEditDeselectAll.Enabled = hasSthSelected;
|
||||||
|
|
||||||
control.PerformLayout();
|
control.PerformLayout();
|
||||||
}
|
}
|
||||||
@@ -1043,6 +1058,13 @@ namespace FlaxEditor.Modules
|
|||||||
Editor.Options.Apply(options);
|
Editor.Options.Apply(options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void SetGameWindowMode(InterfaceOptions.GameWindowMode newGameWindowMode)
|
||||||
|
{
|
||||||
|
var options = Editor.Options.Options;
|
||||||
|
options.Interface.DefaultGameWindowMode = newGameWindowMode;
|
||||||
|
Editor.Options.Apply(options);
|
||||||
|
}
|
||||||
|
|
||||||
private void OnMainWindowClosing()
|
private void OnMainWindowClosing()
|
||||||
{
|
{
|
||||||
// Clear UI references (GUI cannot be used after window closing)
|
// Clear UI references (GUI cannot be used after window closing)
|
||||||
|
|||||||
@@ -56,6 +56,10 @@ namespace FlaxEditor.Options
|
|||||||
[EditorDisplay("Common"), EditorOrder(190)]
|
[EditorDisplay("Common"), EditorOrder(190)]
|
||||||
public InputBinding SelectAll = new InputBinding(KeyboardKeys.A, KeyboardKeys.Control);
|
public InputBinding SelectAll = new InputBinding(KeyboardKeys.A, KeyboardKeys.Control);
|
||||||
|
|
||||||
|
[DefaultValue(typeof(InputBinding), "Ctrl+Shift+A")]
|
||||||
|
[EditorDisplay("Common"), EditorOrder(195)]
|
||||||
|
public InputBinding DeselectAll = new InputBinding(KeyboardKeys.A, KeyboardKeys.Shift, KeyboardKeys.Control);
|
||||||
|
|
||||||
[DefaultValue(typeof(InputBinding), "F")]
|
[DefaultValue(typeof(InputBinding), "F")]
|
||||||
[EditorDisplay("Common"), EditorOrder(200)]
|
[EditorDisplay("Common"), EditorOrder(200)]
|
||||||
public InputBinding FocusSelection = new InputBinding(KeyboardKeys.F);
|
public InputBinding FocusSelection = new InputBinding(KeyboardKeys.F);
|
||||||
|
|||||||
@@ -90,6 +90,32 @@ namespace FlaxEditor.Options
|
|||||||
PlayScenes,
|
PlayScenes,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Available window modes for the game window.
|
||||||
|
/// </summary>
|
||||||
|
public enum GameWindowMode
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Shows the game window docked, inside the editor.
|
||||||
|
/// </summary>
|
||||||
|
Docked,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Shows the game window as a popup.
|
||||||
|
/// </summary>
|
||||||
|
PopupWindow,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Shows the game window maximized. (Same as pressing F11)
|
||||||
|
/// </summary>
|
||||||
|
MaximizedWindow,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Shows the game window borderless.
|
||||||
|
/// </summary>
|
||||||
|
BorderlessWindow,
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the Editor User Interface scale. Applied to all UI elements, windows and text. Can be used to scale the interface up on a bigger display. Editor restart required.
|
/// Gets or sets the Editor User Interface scale. Applied to all UI elements, windows and text. Can be used to scale the interface up on a bigger display. Editor restart required.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -229,6 +255,13 @@ namespace FlaxEditor.Options
|
|||||||
[EditorDisplay("Play In-Editor", "Play Button Action"), EditorOrder(410)]
|
[EditorDisplay("Play In-Editor", "Play Button Action"), EditorOrder(410)]
|
||||||
public PlayAction PlayButtonAction { get; set; } = PlayAction.PlayScenes;
|
public PlayAction PlayButtonAction { get; set; } = PlayAction.PlayScenes;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating how the game window should be displayed when the game is launched.
|
||||||
|
/// </summary>
|
||||||
|
[DefaultValue(GameWindowMode.Docked)]
|
||||||
|
[EditorDisplay("Play In-Editor", "Game Window Mode"), EditorOrder(420), Tooltip("Determines how the game window is displayed when the game is launched.")]
|
||||||
|
public GameWindowMode DefaultGameWindowMode { get; set; } = GameWindowMode.Docked;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets a value indicating the number of game clients to launch when building and/or running cooked game.
|
/// Gets or sets a value indicating the number of game clients to launch when building and/or running cooked game.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -212,6 +212,14 @@ namespace FlaxEditor.Options
|
|||||||
string styleName = themeOptions.SelectedStyle;
|
string styleName = themeOptions.SelectedStyle;
|
||||||
if (styleName != ThemeOptions.DefaultName && styleName != ThemeOptions.LightDefault && themeOptions.Styles.TryGetValue(styleName, out var style) && style != null)
|
if (styleName != ThemeOptions.DefaultName && styleName != ThemeOptions.LightDefault && themeOptions.Styles.TryGetValue(styleName, out var style) && style != null)
|
||||||
{
|
{
|
||||||
|
// Setup defaults for newly added components that might be missing
|
||||||
|
if (style.Selection == Color.Transparent && style.SelectionBorder == Color.Transparent)
|
||||||
|
{
|
||||||
|
// [Deprecated on 6.03.2024, expires on 6.03.2026]
|
||||||
|
style.Selection = Color.Orange * 0.4f;
|
||||||
|
style.SelectionBorder = Color.Orange;
|
||||||
|
}
|
||||||
|
|
||||||
Style.Current = style;
|
Style.Current = style;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -258,6 +266,8 @@ namespace FlaxEditor.Options
|
|||||||
TextBoxBackgroundSelected = Color.FromBgra(0xFF3F3F46),
|
TextBoxBackgroundSelected = Color.FromBgra(0xFF3F3F46),
|
||||||
CollectionBackgroundColor = Color.FromBgra(0x14CCCCCC),
|
CollectionBackgroundColor = Color.FromBgra(0x14CCCCCC),
|
||||||
ProgressNormal = Color.FromBgra(0xFF0ad328),
|
ProgressNormal = Color.FromBgra(0xFF0ad328),
|
||||||
|
Selection = Color.Orange * 0.4f,
|
||||||
|
SelectionBorder = Color.Orange,
|
||||||
|
|
||||||
Statusbar = new Style.StatusbarStyle
|
Statusbar = new Style.StatusbarStyle
|
||||||
{
|
{
|
||||||
@@ -318,6 +328,8 @@ namespace FlaxEditor.Options
|
|||||||
TextBoxBackgroundSelected = new Color(0.73f, 0.73f, 0.80f, 1f),
|
TextBoxBackgroundSelected = new Color(0.73f, 0.73f, 0.80f, 1f),
|
||||||
CollectionBackgroundColor = new Color(0.85f, 0.85f, 0.88f, 1f),
|
CollectionBackgroundColor = new Color(0.85f, 0.85f, 0.88f, 1f),
|
||||||
ProgressNormal = new Color(0.03f, 0.65f, 0.12f, 1f),
|
ProgressNormal = new Color(0.03f, 0.65f, 0.12f, 1f),
|
||||||
|
Selection = Color.Orange * 0.4f,
|
||||||
|
SelectionBorder = Color.Orange,
|
||||||
|
|
||||||
// Fonts
|
// Fonts
|
||||||
FontTitle = options.Interface.TitleFont.GetFont(),
|
FontTitle = options.Interface.TitleFont.GetFont(),
|
||||||
|
|||||||
@@ -316,41 +316,7 @@ namespace FlaxEditor.SceneGraph
|
|||||||
{
|
{
|
||||||
base.OnParentChanged();
|
base.OnParentChanged();
|
||||||
|
|
||||||
// Update UI (special case if actor is spawned and added to existing scene tree)
|
_treeNode.OnParentChanged(_actor, parentNode as ActorNode);
|
||||||
var parentTreeNode = (parentNode as ActorNode)?.TreeNode;
|
|
||||||
if (parentTreeNode != null && !parentTreeNode.IsLayoutLocked)
|
|
||||||
{
|
|
||||||
parentTreeNode.IsLayoutLocked = true;
|
|
||||||
_treeNode.Parent = parentTreeNode;
|
|
||||||
_treeNode.IndexInParent = _actor.OrderInParent;
|
|
||||||
parentTreeNode.IsLayoutLocked = false;
|
|
||||||
|
|
||||||
// Skip UI update if node won't be in a view
|
|
||||||
if (parentTreeNode.IsCollapsed)
|
|
||||||
{
|
|
||||||
TreeNode.UnlockChildrenRecursive();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Try to perform layout at the level where it makes it the most performant (the least computations)
|
|
||||||
var tree = parentTreeNode.ParentTree;
|
|
||||||
if (tree != null)
|
|
||||||
{
|
|
||||||
if (tree.Parent is FlaxEngine.GUI.Panel treeParent)
|
|
||||||
treeParent.PerformLayout();
|
|
||||||
else
|
|
||||||
tree.PerformLayout();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
parentTreeNode.PerformLayout();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_treeNode.Parent = parentTreeNode;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
|||||||
@@ -1,5 +1,11 @@
|
|||||||
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
||||||
|
|
||||||
|
#if USE_LARGE_WORLDS
|
||||||
|
using Real = System.Double;
|
||||||
|
#else
|
||||||
|
using Real = System.Single;
|
||||||
|
#endif
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using FlaxEditor.Content;
|
using FlaxEditor.Content;
|
||||||
@@ -25,18 +31,20 @@ namespace FlaxEditor.SceneGraph.Actors
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override bool OnVertexSnap(ref Vector3 point, out Vector3 result)
|
public override bool OnVertexSnap(ref Ray ray, Real hitDistance, out Vector3 result)
|
||||||
{
|
{
|
||||||
result = point;
|
// Find the closest vertex to bounding box point (collision detection approximation)
|
||||||
|
result = ray.GetPoint(hitDistance);
|
||||||
var model = ((StaticModel)Actor).Model;
|
var model = ((StaticModel)Actor).Model;
|
||||||
if (model && !model.WaitForLoaded())
|
if (model && !model.WaitForLoaded())
|
||||||
{
|
{
|
||||||
// TODO: move to C++ and use cached vertex buffer internally inside the Mesh
|
// TODO: move to C++ and use cached vertex buffer internally inside the Mesh
|
||||||
if (_vertices == null)
|
if (_vertices == null)
|
||||||
_vertices = new();
|
_vertices = new();
|
||||||
var pointLocal = (Float3)Actor.Transform.WorldToLocal(point);
|
var pointLocal = (Float3)Actor.Transform.WorldToLocal(result);
|
||||||
var minDistance = float.MaxValue;
|
var minDistance = Real.MaxValue;
|
||||||
foreach (var lod in model.LODs)
|
var lodIndex = 0; // TODO: use LOD index based on the game view
|
||||||
|
var lod = model.LODs[lodIndex];
|
||||||
{
|
{
|
||||||
var hit = false;
|
var hit = false;
|
||||||
foreach (var mesh in lod.Meshes)
|
foreach (var mesh in lod.Meshes)
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using FlaxEditor.Actions;
|
||||||
using FlaxEditor.Content;
|
using FlaxEditor.Content;
|
||||||
using FlaxEditor.GUI;
|
using FlaxEditor.GUI;
|
||||||
using FlaxEditor.GUI.Drag;
|
using FlaxEditor.GUI.Drag;
|
||||||
@@ -14,7 +15,6 @@ using FlaxEditor.Windows.Assets;
|
|||||||
using FlaxEngine;
|
using FlaxEngine;
|
||||||
using FlaxEngine.GUI;
|
using FlaxEngine.GUI;
|
||||||
using FlaxEngine.Utilities;
|
using FlaxEngine.Utilities;
|
||||||
using Object = FlaxEngine.Object;
|
|
||||||
|
|
||||||
namespace FlaxEditor.SceneGraph.GUI
|
namespace FlaxEditor.SceneGraph.GUI
|
||||||
{
|
{
|
||||||
@@ -82,8 +82,51 @@ namespace FlaxEditor.SceneGraph.GUI
|
|||||||
UpdateText();
|
UpdateText();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal void OnParentChanged(Actor actor, ActorNode parentNode)
|
||||||
|
{
|
||||||
|
// Update cached value
|
||||||
|
_orderInParent = actor.OrderInParent;
|
||||||
|
|
||||||
|
// Update UI (special case if actor is spawned and added to existing scene tree)
|
||||||
|
var parentTreeNode = parentNode?.TreeNode;
|
||||||
|
if (parentTreeNode != null && !parentTreeNode.IsLayoutLocked)
|
||||||
|
{
|
||||||
|
parentTreeNode.IsLayoutLocked = true;
|
||||||
|
Parent = parentTreeNode;
|
||||||
|
IndexInParent = _orderInParent;
|
||||||
|
parentTreeNode.IsLayoutLocked = false;
|
||||||
|
|
||||||
|
// Skip UI update if node won't be in a view
|
||||||
|
if (parentTreeNode.IsCollapsed)
|
||||||
|
{
|
||||||
|
UnlockChildrenRecursive();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Try to perform layout at the level where it makes it the most performant (the least computations)
|
||||||
|
var tree = parentTreeNode.ParentTree;
|
||||||
|
if (tree != null)
|
||||||
|
{
|
||||||
|
if (tree.Parent is Panel treeParent)
|
||||||
|
treeParent.PerformLayout();
|
||||||
|
else
|
||||||
|
tree.PerformLayout();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
parentTreeNode.PerformLayout();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Parent = parentTreeNode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
internal void OnOrderInParentChanged()
|
internal void OnOrderInParentChanged()
|
||||||
{
|
{
|
||||||
|
// Use cached value to check if we need to update UI layout (and update siblings order at once)
|
||||||
if (Parent is ActorTreeNode parent)
|
if (Parent is ActorTreeNode parent)
|
||||||
{
|
{
|
||||||
var anyChanged = false;
|
var anyChanged = false;
|
||||||
@@ -419,134 +462,6 @@ namespace FlaxEditor.SceneGraph.GUI
|
|||||||
_dragHandlers.OnDragLeave();
|
_dragHandlers.OnDragLeave();
|
||||||
}
|
}
|
||||||
|
|
||||||
[Serializable]
|
|
||||||
private class ReparentAction : IUndoAction
|
|
||||||
{
|
|
||||||
[Serialize]
|
|
||||||
private Guid[] _ids;
|
|
||||||
|
|
||||||
[Serialize]
|
|
||||||
private int _actorsCount;
|
|
||||||
|
|
||||||
[Serialize]
|
|
||||||
private Guid[] _prefabIds;
|
|
||||||
|
|
||||||
[Serialize]
|
|
||||||
private Guid[] _prefabObjectIds;
|
|
||||||
|
|
||||||
public ReparentAction(Actor actor)
|
|
||||||
: this(new List<Actor> { actor })
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public ReparentAction(List<Actor> actors)
|
|
||||||
{
|
|
||||||
var allActors = new List<Actor>(Mathf.NextPowerOfTwo(actors.Count));
|
|
||||||
|
|
||||||
for (int i = 0; i < actors.Count; i++)
|
|
||||||
{
|
|
||||||
GetAllActors(allActors, actors[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
var allScripts = new List<Script>(allActors.Capacity);
|
|
||||||
GetAllScripts(allActors, allScripts);
|
|
||||||
|
|
||||||
int allCount = allActors.Count + allScripts.Count;
|
|
||||||
_actorsCount = allActors.Count;
|
|
||||||
_ids = new Guid[allCount];
|
|
||||||
_prefabIds = new Guid[allCount];
|
|
||||||
_prefabObjectIds = new Guid[allCount];
|
|
||||||
|
|
||||||
for (int i = 0; i < allActors.Count; i++)
|
|
||||||
{
|
|
||||||
_ids[i] = allActors[i].ID;
|
|
||||||
_prefabIds[i] = allActors[i].PrefabID;
|
|
||||||
_prefabObjectIds[i] = allActors[i].PrefabObjectID;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < allScripts.Count; i++)
|
|
||||||
{
|
|
||||||
int j = _actorsCount + i;
|
|
||||||
_ids[j] = allScripts[i].ID;
|
|
||||||
_prefabIds[j] = allScripts[i].PrefabID;
|
|
||||||
_prefabObjectIds[j] = allScripts[i].PrefabObjectID;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public ReparentAction(Script script)
|
|
||||||
{
|
|
||||||
_actorsCount = 0;
|
|
||||||
_ids = new Guid[] { script.ID };
|
|
||||||
_prefabIds = new Guid[] { script.PrefabID };
|
|
||||||
_prefabObjectIds = new Guid[] { script.PrefabObjectID };
|
|
||||||
}
|
|
||||||
|
|
||||||
private void GetAllActors(List<Actor> allActors, Actor actor)
|
|
||||||
{
|
|
||||||
allActors.Add(actor);
|
|
||||||
|
|
||||||
for (int i = 0; i < actor.ChildrenCount; i++)
|
|
||||||
{
|
|
||||||
var child = actor.GetChild(i);
|
|
||||||
if (!allActors.Contains(child))
|
|
||||||
{
|
|
||||||
GetAllActors(allActors, child);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void GetAllScripts(List<Actor> allActors, List<Script> allScripts)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < allActors.Count; i++)
|
|
||||||
{
|
|
||||||
var actor = allActors[i];
|
|
||||||
for (int j = 0; j < actor.ScriptsCount; j++)
|
|
||||||
{
|
|
||||||
allScripts.Add(actor.GetScript(j));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public string ActionString => string.Empty;
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public void Do()
|
|
||||||
{
|
|
||||||
// Note: prefab links are broken by the C++ backend on actor reparenting
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public void Undo()
|
|
||||||
{
|
|
||||||
// Restore links
|
|
||||||
for (int i = 0; i < _actorsCount; i++)
|
|
||||||
{
|
|
||||||
var actor = Object.Find<Actor>(ref _ids[i]);
|
|
||||||
if (actor != null && _prefabIds[i] != Guid.Empty)
|
|
||||||
{
|
|
||||||
Actor.Internal_LinkPrefab(Object.GetUnmanagedPtr(actor), ref _prefabIds[i], ref _prefabObjectIds[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (int i = _actorsCount; i < _ids.Length; i++)
|
|
||||||
{
|
|
||||||
var script = Object.Find<Script>(ref _ids[i]);
|
|
||||||
if (script != null && _prefabIds[i] != Guid.Empty)
|
|
||||||
{
|
|
||||||
Script.Internal_LinkPrefab(Object.GetUnmanagedPtr(script), ref _prefabIds[i], ref _prefabObjectIds[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
_ids = null;
|
|
||||||
_prefabIds = null;
|
|
||||||
_prefabObjectIds = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
protected override DragDropEffect OnDragDropHeader(DragData data)
|
protected override DragDropEffect OnDragDropHeader(DragData data)
|
||||||
{
|
{
|
||||||
@@ -593,46 +508,24 @@ namespace FlaxEditor.SceneGraph.GUI
|
|||||||
// Drag actors
|
// Drag actors
|
||||||
if (_dragActors != null && _dragActors.HasValidDrag)
|
if (_dragActors != null && _dragActors.HasValidDrag)
|
||||||
{
|
{
|
||||||
bool worldPositionLock = Root.GetKey(KeyboardKeys.Control) == false;
|
bool worldPositionsStays = Root.GetKey(KeyboardKeys.Control) == false;
|
||||||
var singleObject = _dragActors.Objects.Count == 1;
|
var objects = new SceneObject[_dragActors.Objects.Count];
|
||||||
if (singleObject)
|
for (int i = 0; i < objects.Length; i++)
|
||||||
{
|
objects[i] = _dragActors.Objects[i].Actor;
|
||||||
var targetActor = _dragActors.Objects[0].Actor;
|
var action = new ParentActorsAction(objects, newParent, newOrder, worldPositionsStays);
|
||||||
var customAction = targetActor.HasPrefabLink ? new ReparentAction(targetActor) : null;
|
ActorNode.Root.Undo?.AddAction(action);
|
||||||
using (new UndoBlock(ActorNode.Root.Undo, targetActor, "Change actor parent", customAction))
|
action.Do();
|
||||||
{
|
|
||||||
targetActor.SetParent(newParent, worldPositionLock, true);
|
|
||||||
targetActor.OrderInParent = newOrder;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var targetActors = _dragActors.Objects.ConvertAll(x => x.Actor);
|
|
||||||
var customAction = targetActors.Any(x => x.HasPrefabLink) ? new ReparentAction(targetActors) : null;
|
|
||||||
using (new UndoMultiBlock(ActorNode.Root.Undo, targetActors, "Change actors parent", customAction))
|
|
||||||
{
|
|
||||||
for (int i = 0; i < targetActors.Count; i++)
|
|
||||||
{
|
|
||||||
var targetActor = targetActors[i];
|
|
||||||
targetActor.SetParent(newParent, worldPositionLock, true);
|
|
||||||
targetActor.OrderInParent = newOrder;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
result = DragDropEffect.Move;
|
result = DragDropEffect.Move;
|
||||||
}
|
}
|
||||||
// Drag scripts
|
// Drag scripts
|
||||||
else if (_dragScripts != null && _dragScripts.HasValidDrag)
|
else if (_dragScripts != null && _dragScripts.HasValidDrag)
|
||||||
{
|
{
|
||||||
foreach (var script in _dragScripts.Objects)
|
var objects = new SceneObject[_dragScripts.Objects.Count];
|
||||||
{
|
for (int i = 0; i < objects.Length; i++)
|
||||||
var customAction = script.HasPrefabLink ? new ReparentAction(script) : null;
|
objects[i] = _dragScripts.Objects[i];
|
||||||
using (new UndoBlock(ActorNode.Root.Undo, script, "Change script parent", customAction))
|
var action = new ParentActorsAction(objects, newParent, newOrder);
|
||||||
{
|
ActorNode.Root.Undo?.AddAction(action);
|
||||||
script.SetParent(newParent, true);
|
action.Do();
|
||||||
}
|
|
||||||
}
|
|
||||||
Select();
|
Select();
|
||||||
result = DragDropEffect.Move;
|
result = DragDropEffect.Move;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,7 +41,11 @@ namespace FlaxEditor.SceneGraph
|
|||||||
protected SceneGraphNode(Guid id)
|
protected SceneGraphNode(Guid id)
|
||||||
{
|
{
|
||||||
ID = id;
|
ID = id;
|
||||||
SceneGraphFactory.Nodes.Add(id, this);
|
if (SceneGraphFactory.Nodes.TryGetValue(id, out var duplicate) && duplicate != null)
|
||||||
|
{
|
||||||
|
Editor.LogWarning($"Duplicated Scene Graph node with ID {FlaxEngine.Json.JsonSerializer.GetStringID(id)} of type '{duplicate.GetType().FullName}'");
|
||||||
|
}
|
||||||
|
SceneGraphFactory.Nodes[id] = this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -94,18 +98,6 @@ namespace FlaxEditor.SceneGraph
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public virtual bool CanTransform => true;
|
public virtual bool CanTransform => true;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets a value indicating whether this node can be used for the vertex snapping feature.
|
|
||||||
/// </summary>
|
|
||||||
public bool CanVertexSnap
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
var v = Vector3.Zero;
|
|
||||||
return OnVertexSnap(ref v, out _);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets a value indicating whether this <see cref="SceneGraphNode"/> is active.
|
/// Gets a value indicating whether this <see cref="SceneGraphNode"/> is active.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -365,14 +357,15 @@ namespace FlaxEditor.SceneGraph
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Performs the vertex snapping of a given point on the object surface that is closest to a given location.
|
/// Performs the vertex snapping for a given ray and hitDistance.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="point">The position to snap.</param>
|
/// <param name="ray">The ray to raycast.</param>
|
||||||
|
/// <param name="hitDistance">Hit distance from ray to object bounding box.</param>
|
||||||
/// <param name="result">The result point on the object mesh that is closest to the specified location.</param>
|
/// <param name="result">The result point on the object mesh that is closest to the specified location.</param>
|
||||||
/// <returns>True if got a valid result value, otherwise false (eg. if missing data or not initialized).</returns>
|
/// <returns>True if got a valid result value, otherwise false (eg. if missing data or not initialized).</returns>
|
||||||
public virtual bool OnVertexSnap(ref Vector3 point, out Vector3 result)
|
public virtual bool OnVertexSnap(ref Ray ray, Real hitDistance, out Vector3 result)
|
||||||
{
|
{
|
||||||
result = point;
|
result = Vector3.Zero;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -92,11 +92,12 @@ namespace FlaxEditor.SceneGraph
|
|||||||
|
|
||||||
private static void FillTree(SceneGraphNode node, List<SceneGraphNode> result)
|
private static void FillTree(SceneGraphNode node, List<SceneGraphNode> result)
|
||||||
{
|
{
|
||||||
result.AddRange(node.ChildNodes);
|
if (result.Contains(node))
|
||||||
for (int i = 0; i < node.ChildNodes.Count; i++)
|
return;
|
||||||
{
|
result.Add(node);
|
||||||
FillTree(node.ChildNodes[i], result);
|
var children = node.ChildNodes;
|
||||||
}
|
for (int i = 0; i < children.Count; i++)
|
||||||
|
FillTree(children[i], result);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -109,21 +110,9 @@ namespace FlaxEditor.SceneGraph
|
|||||||
{
|
{
|
||||||
if (nodes == null || result == null)
|
if (nodes == null || result == null)
|
||||||
throw new ArgumentNullException();
|
throw new ArgumentNullException();
|
||||||
|
|
||||||
result.Clear();
|
result.Clear();
|
||||||
|
|
||||||
for (var i = 0; i < nodes.Count; i++)
|
for (var i = 0; i < nodes.Count; i++)
|
||||||
{
|
FillTree(nodes[i], result);
|
||||||
var target = nodes[i];
|
|
||||||
|
|
||||||
// Check if has been already added
|
|
||||||
if (result.Contains(target))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// Add whole child tree to the results
|
|
||||||
result.Add(target);
|
|
||||||
FillTree(target, result);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -150,7 +139,6 @@ namespace FlaxEditor.SceneGraph
|
|||||||
if (node == null || result == null)
|
if (node == null || result == null)
|
||||||
throw new ArgumentNullException();
|
throw new ArgumentNullException();
|
||||||
result.Clear();
|
result.Clear();
|
||||||
result.Add(node);
|
|
||||||
FillTree(node, result);
|
FillTree(node, result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -602,7 +602,7 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
var dataB = (Guid)_node.Values[5 + i * 2];
|
var dataB = (Guid)_node.Values[5 + i * 2];
|
||||||
|
|
||||||
pointsAnims[i] = dataB;
|
pointsAnims[i] = dataB;
|
||||||
pointsLocations[i] = new Float2(dataA.X, 0.0f);
|
pointsLocations[i] = new Float2(Mathf.Clamp(dataA.X, rangeX.X, rangeX.Y), 0.0f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -681,6 +681,9 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
{
|
{
|
||||||
_animationX.Value = 0.0f;
|
_animationX.Value = 0.0f;
|
||||||
}
|
}
|
||||||
|
var ranges = (Float4)Values[0];
|
||||||
|
_animationX.MinValue = ranges.X;
|
||||||
|
_animationX.MaxValue = ranges.Y;
|
||||||
_animationXLabel.Enabled = isValid;
|
_animationXLabel.Enabled = isValid;
|
||||||
_animationX.Enabled = isValid;
|
_animationX.Enabled = isValid;
|
||||||
}
|
}
|
||||||
@@ -732,7 +735,7 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
var dataB = (Guid)_node.Values[5 + i * 2];
|
var dataB = (Guid)_node.Values[5 + i * 2];
|
||||||
|
|
||||||
pointsAnims[i] = dataB;
|
pointsAnims[i] = dataB;
|
||||||
pointsLocations[i] = new Float2(dataA.X, dataA.Y);
|
pointsLocations[i] = new Float2(Mathf.Clamp(dataA.X, rangeX.X, rangeX.Y), Mathf.Clamp(dataA.Y, rangeY.X, rangeY.Y));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -843,6 +846,11 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
_animationX.Value = 0.0f;
|
_animationX.Value = 0.0f;
|
||||||
_animationY.Value = 0.0f;
|
_animationY.Value = 0.0f;
|
||||||
}
|
}
|
||||||
|
var ranges = (Float4)Values[0];
|
||||||
|
_animationX.MinValue = ranges.X;
|
||||||
|
_animationX.MaxValue = ranges.Y;
|
||||||
|
_animationY.MinValue = ranges.Z;
|
||||||
|
_animationY.MaxValue = ranges.W;
|
||||||
_animationXLabel.Enabled = isValid;
|
_animationXLabel.Enabled = isValid;
|
||||||
_animationX.Enabled = isValid;
|
_animationX.Enabled = isValid;
|
||||||
_animationYLabel.Enabled = isValid;
|
_animationYLabel.Enabled = isValid;
|
||||||
|
|||||||
@@ -350,7 +350,7 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
|
|
||||||
// Paint Background
|
// Paint Background
|
||||||
if (_isSelected)
|
if (_isSelected)
|
||||||
Render2D.DrawRectangle(_textRect, Color.Orange);
|
Render2D.DrawRectangle(_textRect, style.SelectionBorder);
|
||||||
|
|
||||||
BackgroundColor = style.BackgroundNormal;
|
BackgroundColor = style.BackgroundNormal;
|
||||||
var dragAreaColor = BackgroundColor / 2.0f;
|
var dragAreaColor = BackgroundColor / 2.0f;
|
||||||
@@ -1078,7 +1078,7 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
|
|
||||||
// Paint Background
|
// Paint Background
|
||||||
if (_isSelected)
|
if (_isSelected)
|
||||||
Render2D.DrawRectangle(_textRect, Color.Orange);
|
Render2D.DrawRectangle(_textRect, style.SelectionBorder);
|
||||||
|
|
||||||
BackgroundColor = style.BackgroundNormal;
|
BackgroundColor = style.BackgroundNormal;
|
||||||
var dragAreaColor = BackgroundColor / 2.0f;
|
var dragAreaColor = BackgroundColor / 2.0f;
|
||||||
|
|||||||
@@ -239,6 +239,7 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
ConnectionsHints = ConnectionsHint.Numeric,
|
ConnectionsHints = ConnectionsHint.Numeric,
|
||||||
IndependentBoxes = new[] { 0, 1, 2 },
|
IndependentBoxes = new[] { 0, 1, 2 },
|
||||||
DependentBoxes = new[] { 3 },
|
DependentBoxes = new[] { 3 },
|
||||||
|
SortScore = -1, // Lower sort score to not go above Multiply node
|
||||||
DefaultValues = new object[]
|
DefaultValues = new object[]
|
||||||
{
|
{
|
||||||
1.0f,
|
1.0f,
|
||||||
|
|||||||
@@ -128,7 +128,7 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
Render2D.DrawSprite(Editor.Instance.Icons.DragBar12, _arrangeButtonRect, _arrangeButtonInUse ? Color.Orange : dragBarColor);
|
Render2D.DrawSprite(Editor.Instance.Icons.DragBar12, _arrangeButtonRect, _arrangeButtonInUse ? Color.Orange : dragBarColor);
|
||||||
if (_arrangeButtonInUse && ArrangeAreaCheck(out _, out var arrangeTargetRect))
|
if (_arrangeButtonInUse && ArrangeAreaCheck(out _, out var arrangeTargetRect))
|
||||||
{
|
{
|
||||||
Render2D.FillRectangle(arrangeTargetRect, Color.Orange * 0.8f);
|
Render2D.FillRectangle(arrangeTargetRect, style.Selection);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Disabled overlay
|
// Disabled overlay
|
||||||
|
|||||||
@@ -583,7 +583,7 @@ namespace FlaxEditor.Surface.ContextMenu
|
|||||||
|
|
||||||
// Check if surface has any parameters
|
// Check if surface has any parameters
|
||||||
var parameters = _parametersGetter?.Invoke();
|
var parameters = _parametersGetter?.Invoke();
|
||||||
int count = parameters?.Count(x => x.IsPublic) ?? 0;
|
int count = parameters?.Count ?? 0;
|
||||||
if (count > 0)
|
if (count > 0)
|
||||||
{
|
{
|
||||||
// TODO: cache the allocated memory to reduce dynamic allocations
|
// TODO: cache the allocated memory to reduce dynamic allocations
|
||||||
@@ -592,28 +592,6 @@ namespace FlaxEditor.Surface.ContextMenu
|
|||||||
var archetypes = new NodeArchetype[count];
|
var archetypes = new NodeArchetype[count];
|
||||||
int archetypeIndex = 0;
|
int archetypeIndex = 0;
|
||||||
|
|
||||||
// ReSharper disable once PossibleNullReferenceException
|
|
||||||
for (int i = 0; i < parameters.Count; i++)
|
|
||||||
{
|
|
||||||
var param = parameters[i];
|
|
||||||
if (!param.IsPublic)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
var node = (NodeArchetype)_parameterGetNodeArchetype.Clone();
|
|
||||||
node.Title = "Get " + param.Name;
|
|
||||||
node.DefaultValues[0] = param.ID;
|
|
||||||
archetypes[archetypeIndex++] = node;
|
|
||||||
|
|
||||||
if (_parameterSetNodeArchetype != null)
|
|
||||||
{
|
|
||||||
node = (NodeArchetype)_parameterSetNodeArchetype.Clone();
|
|
||||||
node.Title = "Set " + param.Name;
|
|
||||||
node.DefaultValues[0] = param.ID;
|
|
||||||
node.DefaultValues[1] = TypeUtils.GetDefaultValue(param.Type);
|
|
||||||
archetypes[archetypeIndex++] = node;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var groupArchetype = new GroupArchetype
|
var groupArchetype = new GroupArchetype
|
||||||
{
|
{
|
||||||
GroupID = 6,
|
GroupID = 6,
|
||||||
@@ -626,26 +604,39 @@ namespace FlaxEditor.Surface.ContextMenu
|
|||||||
group.ArrowImageOpened = new SpriteBrush(Style.Current.ArrowDown);
|
group.ArrowImageOpened = new SpriteBrush(Style.Current.ArrowDown);
|
||||||
group.ArrowImageClosed = new SpriteBrush(Style.Current.ArrowRight);
|
group.ArrowImageClosed = new SpriteBrush(Style.Current.ArrowRight);
|
||||||
group.Close(false);
|
group.Close(false);
|
||||||
archetypeIndex = 0;
|
|
||||||
|
// ReSharper disable once PossibleNullReferenceException
|
||||||
for (int i = 0; i < parameters.Count; i++)
|
for (int i = 0; i < parameters.Count; i++)
|
||||||
{
|
{
|
||||||
var param = parameters[i];
|
var param = parameters[i];
|
||||||
if (!param.IsPublic)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
var item = new VisjectCMItem(group, groupArchetype, archetypes[archetypeIndex++])
|
// Define Getter node and create CM item
|
||||||
|
var node = (NodeArchetype)_parameterGetNodeArchetype.Clone();
|
||||||
|
node.Title = "Get " + param.Name;
|
||||||
|
node.DefaultValues[0] = param.ID;
|
||||||
|
archetypes[archetypeIndex++] = node;
|
||||||
|
|
||||||
|
var item = new VisjectCMItem(group, groupArchetype, node)
|
||||||
{
|
{
|
||||||
Parent = group
|
Parent = group
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Define Setter node and create CM item if parameter has a setter
|
||||||
if (_parameterSetNodeArchetype != null)
|
if (_parameterSetNodeArchetype != null)
|
||||||
{
|
{
|
||||||
item = new VisjectCMItem(group, groupArchetype, archetypes[archetypeIndex++])
|
node = (NodeArchetype)_parameterSetNodeArchetype.Clone();
|
||||||
|
node.Title = "Set " + param.Name;
|
||||||
|
node.DefaultValues[0] = param.ID;
|
||||||
|
node.DefaultValues[1] = TypeUtils.GetDefaultValue(param.Type);
|
||||||
|
archetypes[archetypeIndex++] = node;
|
||||||
|
|
||||||
|
item = new VisjectCMItem(group, groupArchetype, node)
|
||||||
{
|
{
|
||||||
Parent = group
|
Parent = group
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
group.SortChildren();
|
group.SortChildren();
|
||||||
group.UnlockChildrenRecursive();
|
group.UnlockChildrenRecursive();
|
||||||
group.Parent = _groupsPanel;
|
group.Parent = _groupsPanel;
|
||||||
|
|||||||
@@ -181,7 +181,8 @@ namespace FlaxEditor.Surface
|
|||||||
if (_isResizing)
|
if (_isResizing)
|
||||||
{
|
{
|
||||||
// Draw overlay
|
// Draw overlay
|
||||||
Render2D.FillRectangle(_resizeButtonRect, Color.Orange * 0.3f);
|
Render2D.FillRectangle(_resizeButtonRect, style.Selection);
|
||||||
|
Render2D.DrawRectangle(_resizeButtonRect, style.SelectionBorder);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resize button
|
// Resize button
|
||||||
|
|||||||
@@ -96,9 +96,10 @@ namespace FlaxEditor.Surface
|
|||||||
/// <remarks>Called only when user is selecting nodes using rectangle tool.</remarks>
|
/// <remarks>Called only when user is selecting nodes using rectangle tool.</remarks>
|
||||||
protected virtual void DrawSelection()
|
protected virtual void DrawSelection()
|
||||||
{
|
{
|
||||||
|
var style = FlaxEngine.GUI.Style.Current;
|
||||||
var selectionRect = Rectangle.FromPoints(_leftMouseDownPos, _mousePos);
|
var selectionRect = Rectangle.FromPoints(_leftMouseDownPos, _mousePos);
|
||||||
Render2D.FillRectangle(selectionRect, Color.Orange * 0.4f);
|
Render2D.FillRectangle(selectionRect, style.Selection);
|
||||||
Render2D.DrawRectangle(selectionRect, Color.Orange);
|
Render2D.DrawRectangle(selectionRect, style.SelectionBorder);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -382,6 +382,7 @@ namespace FlaxEditor.Surface
|
|||||||
{
|
{
|
||||||
new InputActionsContainer.Binding(options => options.Delete, Delete),
|
new InputActionsContainer.Binding(options => options.Delete, Delete),
|
||||||
new InputActionsContainer.Binding(options => options.SelectAll, SelectAll),
|
new InputActionsContainer.Binding(options => options.SelectAll, SelectAll),
|
||||||
|
new InputActionsContainer.Binding(options => options.DeselectAll, DeselectAll),
|
||||||
new InputActionsContainer.Binding(options => options.Copy, Copy),
|
new InputActionsContainer.Binding(options => options.Copy, Copy),
|
||||||
new InputActionsContainer.Binding(options => options.Paste, Paste),
|
new InputActionsContainer.Binding(options => options.Paste, Paste),
|
||||||
new InputActionsContainer.Binding(options => options.Cut, Cut),
|
new InputActionsContainer.Binding(options => options.Cut, Cut),
|
||||||
@@ -611,17 +612,14 @@ namespace FlaxEditor.Surface
|
|||||||
_context.MarkAsModified(graphEdited);
|
_context.MarkAsModified(graphEdited);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
private void BulkSelectUpdate(bool select = true)
|
||||||
/// Selects all the nodes.
|
|
||||||
/// </summary>
|
|
||||||
public void SelectAll()
|
|
||||||
{
|
{
|
||||||
bool selectionChanged = false;
|
bool selectionChanged = false;
|
||||||
for (int i = 0; i < _rootControl.Children.Count; i++)
|
for (int i = 0; i < _rootControl.Children.Count; i++)
|
||||||
{
|
{
|
||||||
if (_rootControl.Children[i] is SurfaceControl control && !control.IsSelected)
|
if (_rootControl.Children[i] is SurfaceControl control && control.IsSelected != select)
|
||||||
{
|
{
|
||||||
control.IsSelected = true;
|
control.IsSelected = select;
|
||||||
selectionChanged = true;
|
selectionChanged = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -629,6 +627,22 @@ namespace FlaxEditor.Surface
|
|||||||
SelectionChanged?.Invoke();
|
SelectionChanged?.Invoke();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Selects all the nodes.
|
||||||
|
/// </summary>
|
||||||
|
public void SelectAll()
|
||||||
|
{
|
||||||
|
BulkSelectUpdate(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Deelects all the nodes.
|
||||||
|
/// </summary>
|
||||||
|
public void DeselectAll()
|
||||||
|
{
|
||||||
|
BulkSelectUpdate(false);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Clears the selection.
|
/// Clears the selection.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -17,6 +17,30 @@ namespace FlaxEditor.Actions
|
|||||||
[Serializable]
|
[Serializable]
|
||||||
sealed class BreakPrefabLinkAction : IUndoAction
|
sealed class BreakPrefabLinkAction : IUndoAction
|
||||||
{
|
{
|
||||||
|
private struct Item
|
||||||
|
{
|
||||||
|
public Guid ID;
|
||||||
|
public Guid PrefabID;
|
||||||
|
public Guid PrefabObjectID;
|
||||||
|
|
||||||
|
public unsafe Item(SceneObject obj, List<Item> nestedPrefabLinks)
|
||||||
|
{
|
||||||
|
ID = obj.ID;
|
||||||
|
PrefabID = obj.PrefabID;
|
||||||
|
PrefabObjectID = obj.PrefabObjectID;
|
||||||
|
if (nestedPrefabLinks != null)
|
||||||
|
{
|
||||||
|
// Check if this object comes from another nested prefab (to break link only from the top-level prefab)
|
||||||
|
Item nested;
|
||||||
|
nested.ID = ID;
|
||||||
|
fixed (Item* i = &this)
|
||||||
|
Editor.Internal_GetPrefabNestedObject(new IntPtr(&i->PrefabID), new IntPtr(&i->PrefabObjectID), new IntPtr(&nested.PrefabID), new IntPtr(&nested.PrefabObjectID));
|
||||||
|
if (nested.PrefabID != Guid.Empty && nested.PrefabObjectID != Guid.Empty)
|
||||||
|
nestedPrefabLinks.Add(nested);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[Serialize]
|
[Serialize]
|
||||||
private readonly bool _isBreak;
|
private readonly bool _isBreak;
|
||||||
|
|
||||||
@@ -24,25 +48,18 @@ namespace FlaxEditor.Actions
|
|||||||
private Guid _actorId;
|
private Guid _actorId;
|
||||||
|
|
||||||
[Serialize]
|
[Serialize]
|
||||||
private Guid _prefabId;
|
private List<Item> _items = new();
|
||||||
|
|
||||||
[Serialize]
|
private BreakPrefabLinkAction(bool isBreak, Guid actorId)
|
||||||
private Dictionary<Guid, Guid> _prefabObjectIds;
|
|
||||||
|
|
||||||
private BreakPrefabLinkAction(bool isBreak, Guid actorId, Guid prefabId)
|
|
||||||
{
|
{
|
||||||
_isBreak = isBreak;
|
_isBreak = isBreak;
|
||||||
_actorId = actorId;
|
_actorId = actorId;
|
||||||
_prefabId = prefabId;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private BreakPrefabLinkAction(bool isBreak, Actor actor)
|
private BreakPrefabLinkAction(bool isBreak, Actor actor)
|
||||||
{
|
{
|
||||||
_isBreak = isBreak;
|
_isBreak = isBreak;
|
||||||
_actorId = actor.ID;
|
_actorId = actor.ID;
|
||||||
_prefabId = actor.PrefabID;
|
|
||||||
|
|
||||||
_prefabObjectIds = new Dictionary<Guid, Guid>(1024);
|
|
||||||
CollectIds(actor);
|
CollectIds(actor);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,7 +72,7 @@ namespace FlaxEditor.Actions
|
|||||||
{
|
{
|
||||||
if (actor == null)
|
if (actor == null)
|
||||||
throw new ArgumentNullException(nameof(actor));
|
throw new ArgumentNullException(nameof(actor));
|
||||||
return new BreakPrefabLinkAction(true, actor.ID, Guid.Empty);
|
return new BreakPrefabLinkAction(true, actor.ID);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -96,53 +113,45 @@ namespace FlaxEditor.Actions
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
_prefabObjectIds.Clear();
|
_items.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DoLink()
|
private void DoLink()
|
||||||
{
|
{
|
||||||
if (_prefabObjectIds == null)
|
|
||||||
throw new Exception("Cannot link prefab. Missing objects Ids mapping.");
|
|
||||||
|
|
||||||
var actor = Object.Find<Actor>(ref _actorId);
|
var actor = Object.Find<Actor>(ref _actorId);
|
||||||
if (actor == null)
|
if (actor == null)
|
||||||
throw new Exception("Cannot link prefab. Missing actor.");
|
throw new Exception("Cannot link prefab. Missing actor.");
|
||||||
|
|
||||||
// Restore cached links
|
Link(_items);
|
||||||
foreach (var e in _prefabObjectIds)
|
Refresh(actor);
|
||||||
{
|
|
||||||
var objId = e.Key;
|
|
||||||
var prefabObjId = e.Value;
|
|
||||||
|
|
||||||
var obj = Object.Find<Object>(ref objId);
|
|
||||||
if (obj is Actor)
|
|
||||||
{
|
|
||||||
Actor.Internal_LinkPrefab(Object.GetUnmanagedPtr(obj), ref _prefabId, ref prefabObjId);
|
|
||||||
}
|
|
||||||
else if (obj is Script)
|
|
||||||
{
|
|
||||||
Script.Internal_LinkPrefab(Object.GetUnmanagedPtr(obj), ref _prefabId, ref prefabObjId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Editor.Instance.Scene.MarkSceneEdited(actor.Scene);
|
|
||||||
Editor.Instance.Windows.PropertiesWin.Presenter.BuildLayout();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CollectIds(Actor actor)
|
private void Link(List<Item> items)
|
||||||
{
|
{
|
||||||
_prefabObjectIds.Add(actor.ID, actor.PrefabObjectID);
|
for (int i = 0; i < items.Count; i++)
|
||||||
|
{
|
||||||
|
var item = items[i];
|
||||||
|
var obj = Object.Find<Object>(ref item.ID);
|
||||||
|
if (obj != null)
|
||||||
|
SceneObject.Internal_LinkPrefab(Object.GetUnmanagedPtr(obj), ref item.PrefabID, ref item.PrefabObjectID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CollectIds(Actor actor, List<Item> nestedPrefabLinks = null)
|
||||||
|
{
|
||||||
|
_items.Add(new Item(actor, nestedPrefabLinks));
|
||||||
|
|
||||||
for (int i = 0; i < actor.ChildrenCount; i++)
|
for (int i = 0; i < actor.ChildrenCount; i++)
|
||||||
{
|
CollectIds(actor.GetChild(i), nestedPrefabLinks);
|
||||||
CollectIds(actor.GetChild(i));
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < actor.ScriptsCount; i++)
|
for (int i = 0; i < actor.ScriptsCount; i++)
|
||||||
{
|
_items.Add(new Item(actor.GetScript(i), nestedPrefabLinks));
|
||||||
var script = actor.GetScript(i);
|
}
|
||||||
_prefabObjectIds.Add(script.ID, script.PrefabObjectID);
|
|
||||||
}
|
private void Refresh(Actor actor)
|
||||||
|
{
|
||||||
|
Editor.Instance.Scene.MarkSceneEdited(actor.Scene);
|
||||||
|
Editor.Instance.Windows.PropertiesWin.Presenter.BuildLayout();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DoBreak()
|
private void DoBreak()
|
||||||
@@ -153,18 +162,18 @@ namespace FlaxEditor.Actions
|
|||||||
if (!actor.HasPrefabLink)
|
if (!actor.HasPrefabLink)
|
||||||
throw new Exception("Cannot break missing prefab link.");
|
throw new Exception("Cannot break missing prefab link.");
|
||||||
|
|
||||||
if (_prefabObjectIds == null)
|
// Cache 'prev' state and extract any nested prefab instances to remain
|
||||||
_prefabObjectIds = new Dictionary<Guid, Guid>(1024);
|
_items.Clear();
|
||||||
else
|
var nestedPrefabLinks = new List<Item>();
|
||||||
_prefabObjectIds.Clear();
|
CollectIds(actor, nestedPrefabLinks);
|
||||||
CollectIds(actor);
|
|
||||||
|
|
||||||
_prefabId = actor.PrefabID;
|
|
||||||
|
|
||||||
|
// Break prefab linkage
|
||||||
actor.BreakPrefabLink();
|
actor.BreakPrefabLink();
|
||||||
|
|
||||||
Editor.Instance.Scene.MarkSceneEdited(actor.Scene);
|
// Restore prefab link for nested instances
|
||||||
Editor.Instance.Windows.PropertiesWin.Presenter.BuildLayout();
|
Link(nestedPrefabLinks);
|
||||||
|
|
||||||
|
Refresh(actor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using FlaxEditor.SceneGraph;
|
using FlaxEditor.SceneGraph;
|
||||||
using FlaxEditor.Scripting;
|
|
||||||
using FlaxEngine;
|
using FlaxEngine;
|
||||||
using FlaxEngine.Utilities;
|
using FlaxEngine.Utilities;
|
||||||
|
|
||||||
@@ -25,6 +24,9 @@ namespace FlaxEditor.Actions
|
|||||||
[Serialize]
|
[Serialize]
|
||||||
private Guid[] _nodeParentsIDs;
|
private Guid[] _nodeParentsIDs;
|
||||||
|
|
||||||
|
[Serialize]
|
||||||
|
private int[] _nodeParentsOrders;
|
||||||
|
|
||||||
[Serialize]
|
[Serialize]
|
||||||
private Guid[] _prefabIds;
|
private Guid[] _prefabIds;
|
||||||
|
|
||||||
@@ -43,12 +45,35 @@ namespace FlaxEditor.Actions
|
|||||||
[Serialize]
|
[Serialize]
|
||||||
protected List<SceneGraphNode> _nodeParents;
|
protected List<SceneGraphNode> _nodeParents;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="DeleteActorsAction"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="actor">The actor.</param>
|
||||||
|
/// <param name="isInverted">If set to <c>true</c> action will be inverted - instead of delete it will create actors.</param>
|
||||||
|
/// <param name="preserveOrder">If set to <c>true</c> action will be preserve actors order when performing undo.</param>
|
||||||
|
internal DeleteActorsAction(Actor actor, bool isInverted = false, bool preserveOrder = true)
|
||||||
|
: this(new List<SceneGraphNode>(1) { SceneGraphFactory.FindNode(actor.ID) }, isInverted, preserveOrder)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="DeleteActorsAction"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="node">The object.</param>
|
||||||
|
/// <param name="isInverted">If set to <c>true</c> action will be inverted - instead of delete it will create actors.</param>
|
||||||
|
/// <param name="preserveOrder">If set to <c>true</c> action will be preserve actors order when performing undo.</param>
|
||||||
|
internal DeleteActorsAction(SceneGraphNode node, bool isInverted = false, bool preserveOrder = true)
|
||||||
|
: this(new List<SceneGraphNode>(1) { node }, isInverted, preserveOrder)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="DeleteActorsAction"/> class.
|
/// Initializes a new instance of the <see cref="DeleteActorsAction"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="nodes">The objects.</param>
|
/// <param name="nodes">The objects.</param>
|
||||||
/// <param name="isInverted">If set to <c>true</c> action will be inverted - instead of delete it will be create actors.</param>
|
/// <param name="isInverted">If set to <c>true</c> action will be inverted - instead of delete it will create actors.</param>
|
||||||
internal DeleteActorsAction(List<SceneGraphNode> nodes, bool isInverted = false)
|
/// <param name="preserveOrder">If set to <c>true</c> action will be preserve actors order when performing undo.</param>
|
||||||
|
internal DeleteActorsAction(List<SceneGraphNode> nodes, bool isInverted = false, bool preserveOrder = true)
|
||||||
{
|
{
|
||||||
_isInverted = isInverted;
|
_isInverted = isInverted;
|
||||||
|
|
||||||
@@ -82,6 +107,12 @@ namespace FlaxEditor.Actions
|
|||||||
_nodeParentsIDs = new Guid[_nodeParents.Count];
|
_nodeParentsIDs = new Guid[_nodeParents.Count];
|
||||||
for (int i = 0; i < _nodeParentsIDs.Length; i++)
|
for (int i = 0; i < _nodeParentsIDs.Length; i++)
|
||||||
_nodeParentsIDs[i] = _nodeParents[i].ID;
|
_nodeParentsIDs[i] = _nodeParents[i].ID;
|
||||||
|
if (preserveOrder)
|
||||||
|
{
|
||||||
|
_nodeParentsOrders = new int[_nodeParents.Count];
|
||||||
|
for (int i = 0; i < _nodeParentsOrders.Length; i++)
|
||||||
|
_nodeParentsOrders[i] = _nodeParents[i].OrderInParent;
|
||||||
|
}
|
||||||
|
|
||||||
// Serialize actors
|
// Serialize actors
|
||||||
_actorsData = Actor.ToBytes(actors.ToArray());
|
_actorsData = Actor.ToBytes(actors.ToArray());
|
||||||
@@ -122,6 +153,7 @@ namespace FlaxEditor.Actions
|
|||||||
{
|
{
|
||||||
_actorsData = null;
|
_actorsData = null;
|
||||||
_nodeParentsIDs = null;
|
_nodeParentsIDs = null;
|
||||||
|
_nodeParentsOrders = null;
|
||||||
_prefabIds = null;
|
_prefabIds = null;
|
||||||
_prefabObjectIds = null;
|
_prefabObjectIds = null;
|
||||||
_nodeParents.Clear();
|
_nodeParents.Clear();
|
||||||
@@ -211,6 +243,8 @@ namespace FlaxEditor.Actions
|
|||||||
if (foundNode is ActorNode node)
|
if (foundNode is ActorNode node)
|
||||||
{
|
{
|
||||||
nodes.Add(node);
|
nodes.Add(node);
|
||||||
|
if (_nodeParentsOrders != null)
|
||||||
|
node.Actor.OrderInParent = _nodeParentsOrders[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
nodes.BuildNodesParents(_nodeParents);
|
nodes.BuildNodesParents(_nodeParents);
|
||||||
|
|||||||
199
Source/Editor/Undo/Actions/ParentActorsAction.cs
Normal file
199
Source/Editor/Undo/Actions/ParentActorsAction.cs
Normal file
@@ -0,0 +1,199 @@
|
|||||||
|
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using FlaxEngine;
|
||||||
|
using Object = FlaxEngine.Object;
|
||||||
|
|
||||||
|
namespace FlaxEditor.Actions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Implementation of <see cref="IUndoAction"/> used to change parent for <see cref="Actor"/> or <see cref="Script"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <seealso cref="FlaxEditor.IUndoAction" />
|
||||||
|
[Serializable]
|
||||||
|
class ParentActorsAction : IUndoAction
|
||||||
|
{
|
||||||
|
private struct Item
|
||||||
|
{
|
||||||
|
public Guid ID;
|
||||||
|
public Guid Parent;
|
||||||
|
public int OrderInParent;
|
||||||
|
public Transform LocalTransform;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Serialize]
|
||||||
|
private bool _worldPositionsStays;
|
||||||
|
|
||||||
|
[Serialize]
|
||||||
|
private Guid _newParent;
|
||||||
|
|
||||||
|
[Serialize]
|
||||||
|
private int _newOrder;
|
||||||
|
|
||||||
|
[Serialize]
|
||||||
|
private Item[] _items;
|
||||||
|
|
||||||
|
[Serialize]
|
||||||
|
private Guid[] _idsForPrefab;
|
||||||
|
|
||||||
|
[Serialize]
|
||||||
|
private Guid[] _prefabIds;
|
||||||
|
|
||||||
|
[Serialize]
|
||||||
|
private Guid[] _prefabObjectIds;
|
||||||
|
|
||||||
|
public ParentActorsAction(SceneObject[] objects, Actor newParent, int newOrder, bool worldPositionsStays = true)
|
||||||
|
{
|
||||||
|
// Sort source objects to provide deterministic behavior
|
||||||
|
Array.Sort(objects, SortObjects);
|
||||||
|
|
||||||
|
// Cache initial state for undo
|
||||||
|
_worldPositionsStays = worldPositionsStays;
|
||||||
|
_newParent = newParent.ID;
|
||||||
|
_newOrder = newOrder;
|
||||||
|
_items = new Item[objects.Length];
|
||||||
|
for (int i = 0; i < objects.Length; i++)
|
||||||
|
{
|
||||||
|
var obj = objects[i];
|
||||||
|
_items[i] = new Item
|
||||||
|
{
|
||||||
|
ID = obj.ID,
|
||||||
|
Parent = obj.Parent?.ID ?? Guid.Empty,
|
||||||
|
OrderInParent = obj.OrderInParent,
|
||||||
|
LocalTransform = obj is Actor actor ? actor.LocalTransform : Transform.Identity,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Collect all objects that have prefab links so they can be restored on undo
|
||||||
|
var prefabs = new List<SceneObject>();
|
||||||
|
for (int i = 0; i < objects.Length; i++)
|
||||||
|
GetAllPrefabs(prefabs, objects[i]);
|
||||||
|
if (prefabs.Count != 0)
|
||||||
|
{
|
||||||
|
// Cache ids of all objects
|
||||||
|
_idsForPrefab = new Guid[prefabs.Count];
|
||||||
|
_prefabIds = new Guid[prefabs.Count];
|
||||||
|
_prefabObjectIds = new Guid[prefabs.Count];
|
||||||
|
for (int i = 0; i < prefabs.Count; i++)
|
||||||
|
{
|
||||||
|
var obj = prefabs[i];
|
||||||
|
_idsForPrefab[i] = obj.ID;
|
||||||
|
_prefabIds[i] = obj.PrefabID;
|
||||||
|
_prefabObjectIds[i] = obj.PrefabObjectID;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int SortObjects(SceneObject a, SceneObject b)
|
||||||
|
{
|
||||||
|
// By parent
|
||||||
|
var aParent = Object.GetUnmanagedPtr(a.Parent);
|
||||||
|
var bParent = Object.GetUnmanagedPtr(b.Parent);
|
||||||
|
if (aParent == bParent)
|
||||||
|
{
|
||||||
|
// By index in parent
|
||||||
|
var aOrder = a.OrderInParent;
|
||||||
|
var bOrder = b.OrderInParent;
|
||||||
|
return aOrder.CompareTo(bOrder);
|
||||||
|
}
|
||||||
|
return aParent.CompareTo(bParent);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void GetAllPrefabs(List<SceneObject> result, SceneObject obj)
|
||||||
|
{
|
||||||
|
if (result.Contains(obj))
|
||||||
|
return;
|
||||||
|
if (obj.HasPrefabLink)
|
||||||
|
result.Add(obj);
|
||||||
|
if (obj is Actor actor)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < actor.ScriptsCount; i++)
|
||||||
|
GetAllPrefabs(result, actor.GetScript(i));
|
||||||
|
for (int i = 0; i < actor.ChildrenCount; i++)
|
||||||
|
GetAllPrefabs(result, actor.GetChild(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string ActionString => "Change parent";
|
||||||
|
|
||||||
|
public void Do()
|
||||||
|
{
|
||||||
|
// Perform action
|
||||||
|
var newParent = Object.Find<Actor>(ref _newParent);
|
||||||
|
if (newParent == null)
|
||||||
|
{
|
||||||
|
Editor.LogError("Missing actor to change objects parent.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var order = _newOrder;
|
||||||
|
var scenes = new HashSet<Scene> { newParent.Scene };
|
||||||
|
for (int i = 0; i < _items.Length; i++)
|
||||||
|
{
|
||||||
|
var item = _items[i];
|
||||||
|
var obj = Object.Find<SceneObject>(ref item.ID);
|
||||||
|
if (obj != null)
|
||||||
|
{
|
||||||
|
scenes.Add(obj.Parent.Scene);
|
||||||
|
if (obj is Actor actor)
|
||||||
|
actor.SetParent(newParent, _worldPositionsStays, true);
|
||||||
|
else
|
||||||
|
obj.Parent = newParent;
|
||||||
|
if (order != -1)
|
||||||
|
obj.OrderInParent = order++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prefab links are broken by the C++ backend on actor reparenting
|
||||||
|
|
||||||
|
// Mark scenes as edited
|
||||||
|
foreach (var scene in scenes)
|
||||||
|
Editor.Instance.Scene.MarkSceneEdited(scene);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Undo()
|
||||||
|
{
|
||||||
|
// Restore state
|
||||||
|
for (int i = 0; i < _items.Length; i++)
|
||||||
|
{
|
||||||
|
var item = _items[i];
|
||||||
|
var obj = Object.Find<SceneObject>(ref item.ID);
|
||||||
|
if (obj != null)
|
||||||
|
{
|
||||||
|
var parent = Object.Find<Actor>(ref item.Parent);
|
||||||
|
if (parent != null)
|
||||||
|
obj.Parent = parent;
|
||||||
|
if (obj is Actor actor)
|
||||||
|
actor.LocalTransform = item.LocalTransform;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (int j = 0; j < _items.Length; j++) // TODO: find a better way ensure the order is properly restored when moving back multiple objects
|
||||||
|
for (int i = 0; i < _items.Length; i++)
|
||||||
|
{
|
||||||
|
var item = _items[i];
|
||||||
|
var obj = Object.Find<SceneObject>(ref item.ID);
|
||||||
|
if (obj != null)
|
||||||
|
obj.OrderInParent = item.OrderInParent;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restore prefab links (if any was in use)
|
||||||
|
if (_idsForPrefab != null)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < _idsForPrefab.Length; i++)
|
||||||
|
{
|
||||||
|
var obj = Object.Find<SceneObject>(ref _idsForPrefab[i]);
|
||||||
|
if (obj != null && _prefabIds[i] != Guid.Empty)
|
||||||
|
SceneObject.Internal_LinkPrefab(Object.GetUnmanagedPtr(obj), ref _prefabIds[i], ref _prefabObjectIds[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_items = null;
|
||||||
|
_idsForPrefab = null;
|
||||||
|
_prefabIds = null;
|
||||||
|
_prefabObjectIds = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
|
||||||
using FlaxEditor.SceneGraph;
|
using FlaxEditor.SceneGraph;
|
||||||
using FlaxEngine;
|
using FlaxEngine;
|
||||||
|
|
||||||
@@ -165,7 +164,7 @@ namespace FlaxEditor.Actions
|
|||||||
var child = children[j];
|
var child = children[j];
|
||||||
if (child != actor && child.Name == name)
|
if (child != actor && child.Name == name)
|
||||||
{
|
{
|
||||||
string newName = Utilities.Utils.IncrementNameNumber(name, x => IsNameValid(x));
|
string newName = Utilities.Utils.IncrementNameNumber(name, IsNameValid);
|
||||||
foundNamesResults[newName] = true;
|
foundNamesResults[newName] = true;
|
||||||
actor.Name = newName;
|
actor.Name = newName;
|
||||||
// Multiple actors may have the same name, continue
|
// Multiple actors may have the same name, continue
|
||||||
|
|||||||
@@ -38,9 +38,6 @@ namespace FlaxEditor
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the undo operations stack.
|
/// Gets the undo operations stack.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value>
|
|
||||||
/// The undo operations stack.
|
|
||||||
/// </value>
|
|
||||||
public HistoryStack UndoOperationsStack { get; }
|
public HistoryStack UndoOperationsStack { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -138,6 +135,7 @@ namespace FlaxEditor
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
_snapshots.Add(snapshotInstance, new UndoInternal(snapshotInstance, actionString));
|
_snapshots.Add(snapshotInstance, new UndoInternal(snapshotInstance, actionString));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -152,9 +150,7 @@ namespace FlaxEditor
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
if (snapshotInstance == null)
|
if (snapshotInstance == null)
|
||||||
{
|
|
||||||
snapshotInstance = _snapshots.Last().Key;
|
snapshotInstance = _snapshots.Last().Key;
|
||||||
}
|
|
||||||
var action = _snapshots[snapshotInstance].End(snapshotInstance);
|
var action = _snapshots[snapshotInstance].End(snapshotInstance);
|
||||||
_snapshots.Remove(snapshotInstance);
|
_snapshots.Remove(snapshotInstance);
|
||||||
|
|
||||||
@@ -249,9 +245,7 @@ namespace FlaxEditor
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
if (snapshotInstance == null)
|
if (snapshotInstance == null)
|
||||||
{
|
|
||||||
snapshotInstance = (object[])_snapshots.Last().Key;
|
snapshotInstance = (object[])_snapshots.Last().Key;
|
||||||
}
|
|
||||||
var action = _snapshots[snapshotInstance].End(snapshotInstance);
|
var action = _snapshots[snapshotInstance].End(snapshotInstance);
|
||||||
_snapshots.Remove(snapshotInstance);
|
_snapshots.Remove(snapshotInstance);
|
||||||
|
|
||||||
@@ -325,12 +319,10 @@ namespace FlaxEditor
|
|||||||
{
|
{
|
||||||
if (action == null)
|
if (action == null)
|
||||||
throw new ArgumentNullException();
|
throw new ArgumentNullException();
|
||||||
|
|
||||||
if (!Enabled)
|
if (!Enabled)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
UndoOperationsStack.Push(action);
|
UndoOperationsStack.Push(action);
|
||||||
|
|
||||||
OnAction(action);
|
OnAction(action);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ using System.Globalization;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
@@ -20,6 +19,7 @@ using FlaxEditor.GUI.Input;
|
|||||||
using FlaxEditor.GUI.Tree;
|
using FlaxEditor.GUI.Tree;
|
||||||
using FlaxEditor.SceneGraph;
|
using FlaxEditor.SceneGraph;
|
||||||
using FlaxEngine;
|
using FlaxEngine;
|
||||||
|
using FlaxEngine.Json;
|
||||||
using FlaxEngine.GUI;
|
using FlaxEngine.GUI;
|
||||||
using FlaxEngine.Utilities;
|
using FlaxEngine.Utilities;
|
||||||
using FlaxEditor.Windows;
|
using FlaxEditor.Windows;
|
||||||
@@ -203,6 +203,22 @@ namespace FlaxEditor.Utilities
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Clones the value. handles non-value types (such as arrays) that need deep value cloning.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">The source value to clone.</param>
|
||||||
|
/// <returns>The duplicated value.</returns>
|
||||||
|
internal static object CloneValue(object value)
|
||||||
|
{
|
||||||
|
// For objects (eg. arrays) we need to clone them to prevent editing default/reference value within editor
|
||||||
|
if (value != null && (!value.GetType().IsValueType || !value.GetType().IsClass))
|
||||||
|
{
|
||||||
|
var json = JsonSerializer.Serialize(value);
|
||||||
|
value = JsonSerializer.Deserialize(json, value.GetType());
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The colors for the keyframes used by the curve editor.
|
/// The colors for the keyframes used by the curve editor.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -1289,6 +1305,7 @@ namespace FlaxEditor.Utilities
|
|||||||
inputActions.Add(options => options.Paste, Editor.Instance.SceneEditing.Paste);
|
inputActions.Add(options => options.Paste, Editor.Instance.SceneEditing.Paste);
|
||||||
inputActions.Add(options => options.Duplicate, Editor.Instance.SceneEditing.Duplicate);
|
inputActions.Add(options => options.Duplicate, Editor.Instance.SceneEditing.Duplicate);
|
||||||
inputActions.Add(options => options.SelectAll, Editor.Instance.SceneEditing.SelectAllScenes);
|
inputActions.Add(options => options.SelectAll, Editor.Instance.SceneEditing.SelectAllScenes);
|
||||||
|
inputActions.Add(options => options.DeselectAll, Editor.Instance.SceneEditing.DeselectAllScenes);
|
||||||
inputActions.Add(options => options.Delete, Editor.Instance.SceneEditing.Delete);
|
inputActions.Add(options => options.Delete, Editor.Instance.SceneEditing.Delete);
|
||||||
inputActions.Add(options => options.Search, () => Editor.Instance.Windows.SceneWin.Search());
|
inputActions.Add(options => options.Search, () => Editor.Instance.Windows.SceneWin.Search());
|
||||||
inputActions.Add(options => options.MoveActorToViewport, Editor.Instance.UI.MoveActorToViewport);
|
inputActions.Add(options => options.MoveActorToViewport, Editor.Instance.UI.MoveActorToViewport);
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
|
||||||
using FlaxEditor.Content;
|
using FlaxEditor.Content;
|
||||||
using FlaxEditor.Gizmo;
|
using FlaxEditor.Gizmo;
|
||||||
using FlaxEditor.GUI.ContextMenu;
|
using FlaxEditor.GUI.ContextMenu;
|
||||||
@@ -591,15 +590,32 @@ namespace FlaxEditor.Viewport
|
|||||||
base.Draw();
|
base.Draw();
|
||||||
|
|
||||||
// Selected UI controls outline
|
// Selected UI controls outline
|
||||||
|
bool drawAnySelectedControl = false;
|
||||||
|
// TODO: optimize this (eg. cache list of selected UIControl's when selection gets changed)
|
||||||
for (var i = 0; i < _window.Selection.Count; i++)
|
for (var i = 0; i < _window.Selection.Count; i++)
|
||||||
{
|
{
|
||||||
if (_window.Selection[i]?.EditableObject is UIControl controlActor && controlActor && controlActor.Control != null)
|
if (_window.Selection[i]?.EditableObject is UIControl controlActor && controlActor && controlActor.Control != null && controlActor.Control.VisibleInHierarchy && controlActor.Control.RootWindow != null)
|
||||||
{
|
{
|
||||||
|
if (!drawAnySelectedControl)
|
||||||
|
{
|
||||||
|
drawAnySelectedControl = true;
|
||||||
|
Render2D.PushTransform(ref _cachedTransform);
|
||||||
|
}
|
||||||
var control = controlActor.Control;
|
var control = controlActor.Control;
|
||||||
var bounds = Rectangle.FromPoints(control.PointToParent(this, Float2.Zero), control.PointToParent(this, control.Size));
|
var bounds = control.EditorBounds;
|
||||||
Render2D.DrawRectangle(bounds, Editor.Instance.Options.Options.Visual.SelectionOutlineColor0, Editor.Instance.Options.Options.Visual.UISelectionOutlineSize);
|
var p1 = control.PointToParent(this, bounds.UpperLeft);
|
||||||
|
var p2 = control.PointToParent(this, bounds.UpperRight);
|
||||||
|
var p3 = control.PointToParent(this, bounds.BottomLeft);
|
||||||
|
var p4 = control.PointToParent(this, bounds.BottomRight);
|
||||||
|
var min = Float2.Min(Float2.Min(p1, p2), Float2.Min(p3, p4));
|
||||||
|
var max = Float2.Max(Float2.Max(p1, p2), Float2.Max(p3, p4));
|
||||||
|
bounds = new Rectangle(min, Float2.Max(max - min, Float2.Zero));
|
||||||
|
var options = Editor.Instance.Options.Options.Visual;
|
||||||
|
Render2D.DrawRectangle(bounds, options.SelectionOutlineColor0, options.UISelectionOutlineSize);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (drawAnySelectedControl)
|
||||||
|
Render2D.PopTransform();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
@@ -799,6 +815,15 @@ namespace FlaxEditor.Viewport
|
|||||||
{
|
{
|
||||||
base.OnDebugDraw(context, ref renderContext);
|
base.OnDebugDraw(context, ref renderContext);
|
||||||
|
|
||||||
|
// Collect selected objects debug shapes again when DebugDraw is active with a custom context
|
||||||
|
_debugDrawData.Clear();
|
||||||
|
var selectedParents = TransformGizmo.SelectedParents;
|
||||||
|
for (int i = 0; i < selectedParents.Count; i++)
|
||||||
|
{
|
||||||
|
if (selectedParents[i].IsActiveInHierarchy)
|
||||||
|
selectedParents[i].OnDebugDraw(_debugDrawData);
|
||||||
|
}
|
||||||
|
|
||||||
unsafe
|
unsafe
|
||||||
{
|
{
|
||||||
fixed (IntPtr* actors = _debugDrawData.ActorsPtrs)
|
fixed (IntPtr* actors = _debugDrawData.ActorsPtrs)
|
||||||
|
|||||||
@@ -239,6 +239,8 @@ namespace FlaxEditor.Viewport.Previews
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void OnDestroy()
|
public override void OnDestroy()
|
||||||
{
|
{
|
||||||
|
if (IsDisposing)
|
||||||
|
return;
|
||||||
Object.Destroy(ref PreviewLight);
|
Object.Destroy(ref PreviewLight);
|
||||||
Object.Destroy(ref EnvProbe);
|
Object.Destroy(ref EnvProbe);
|
||||||
Object.Destroy(ref Sky);
|
Object.Destroy(ref Sky);
|
||||||
|
|||||||
@@ -184,6 +184,7 @@ namespace FlaxEditor.Viewport.Previews
|
|||||||
break;
|
break;
|
||||||
default: throw new ArgumentOutOfRangeException();
|
default: throw new ArgumentOutOfRangeException();
|
||||||
}
|
}
|
||||||
|
samplesPerIndex = Math.Max(samplesPerIndex, info.NumChannels);
|
||||||
const uint maxSamplesPerIndex = 64;
|
const uint maxSamplesPerIndex = 64;
|
||||||
uint samplesPerIndexDiff = Math.Max(1, samplesPerIndex / Math.Min(samplesPerIndex, maxSamplesPerIndex));
|
uint samplesPerIndexDiff = Math.Max(1, samplesPerIndex / Math.Min(samplesPerIndex, maxSamplesPerIndex));
|
||||||
|
|
||||||
|
|||||||
@@ -355,6 +355,8 @@ namespace FlaxEditor.Windows.Assets
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
Editor.Scene.OnSaveStart(_viewport);
|
||||||
|
|
||||||
// Simply update changes
|
// Simply update changes
|
||||||
Editor.Prefabs.ApplyAll(_viewport.Instance);
|
Editor.Prefabs.ApplyAll(_viewport.Instance);
|
||||||
|
|
||||||
@@ -371,6 +373,10 @@ namespace FlaxEditor.Windows.Assets
|
|||||||
|
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
Editor.Scene.OnSaveEnd(_viewport);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
|||||||
@@ -170,6 +170,8 @@ namespace FlaxEditor.Windows
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override bool OnKeyDown(KeyboardKeys key)
|
public override bool OnKeyDown(KeyboardKeys key)
|
||||||
{
|
{
|
||||||
|
InputOptions options = FlaxEditor.Editor.Instance.Options.Options.Input;
|
||||||
|
|
||||||
// Up
|
// Up
|
||||||
if (key == KeyboardKeys.ArrowUp)
|
if (key == KeyboardKeys.ArrowUp)
|
||||||
{
|
{
|
||||||
@@ -200,7 +202,7 @@ namespace FlaxEditor.Windows
|
|||||||
Open();
|
Open();
|
||||||
}
|
}
|
||||||
// Ctrl+C
|
// Ctrl+C
|
||||||
else if (key == KeyboardKeys.C && Root.GetKey(KeyboardKeys.Control))
|
else if (options.Copy.Process(this))
|
||||||
{
|
{
|
||||||
Copy();
|
Copy();
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ namespace FlaxEditor.Windows
|
|||||||
private bool _showGUI = true;
|
private bool _showGUI = true;
|
||||||
private bool _showDebugDraw = false;
|
private bool _showDebugDraw = false;
|
||||||
private bool _isMaximized = false, _isUnlockingMouse = false;
|
private bool _isMaximized = false, _isUnlockingMouse = false;
|
||||||
|
private bool _isFloating = false, _isBorderless = false;
|
||||||
private bool _cursorVisible = true;
|
private bool _cursorVisible = true;
|
||||||
private float _gameStartTime;
|
private float _gameStartTime;
|
||||||
private GUI.Docking.DockState _maximizeRestoreDockState;
|
private GUI.Docking.DockState _maximizeRestoreDockState;
|
||||||
@@ -68,7 +69,7 @@ namespace FlaxEditor.Windows
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets a value indicating whether game window is maximized (only in play mode).
|
/// Gets or sets a value indicating whether the game window is maximized (only in play mode).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private bool IsMaximized
|
private bool IsMaximized
|
||||||
{
|
{
|
||||||
@@ -78,20 +79,42 @@ namespace FlaxEditor.Windows
|
|||||||
if (_isMaximized == value)
|
if (_isMaximized == value)
|
||||||
return;
|
return;
|
||||||
_isMaximized = value;
|
_isMaximized = value;
|
||||||
|
if (value)
|
||||||
|
{
|
||||||
|
IsFloating = true;
|
||||||
|
var rootWindow = RootWindow;
|
||||||
|
rootWindow.Maximize();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
IsFloating = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether the game window is floating (popup, only in play mode).
|
||||||
|
/// </summary>
|
||||||
|
private bool IsFloating
|
||||||
|
{
|
||||||
|
get => _isFloating;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (_isFloating == value)
|
||||||
|
return;
|
||||||
|
_isFloating = value;
|
||||||
var rootWindow = RootWindow;
|
var rootWindow = RootWindow;
|
||||||
if (value)
|
if (value)
|
||||||
{
|
{
|
||||||
// Maximize
|
|
||||||
_maximizeRestoreDockTo = _dockedTo;
|
_maximizeRestoreDockTo = _dockedTo;
|
||||||
_maximizeRestoreDockState = _dockedTo.TryGetDockState(out _);
|
_maximizeRestoreDockState = _dockedTo.TryGetDockState(out _);
|
||||||
if (_maximizeRestoreDockState != GUI.Docking.DockState.Float)
|
if (_maximizeRestoreDockState != GUI.Docking.DockState.Float)
|
||||||
{
|
{
|
||||||
var monitorBounds = Platform.GetMonitorBounds(PointToScreen(Size * 0.5f));
|
var monitorBounds = Platform.GetMonitorBounds(PointToScreen(Size * 0.5f));
|
||||||
ShowFloating(monitorBounds.Location + new Float2(200, 200), Float2.Zero, WindowStartPosition.Manual);
|
var size = DefaultSize;
|
||||||
rootWindow = RootWindow;
|
var location = monitorBounds.Location + monitorBounds.Size * 0.5f - size * 0.5f;
|
||||||
|
ShowFloating(location, size, WindowStartPosition.Manual);
|
||||||
}
|
}
|
||||||
if (rootWindow != null && !rootWindow.IsMaximized)
|
|
||||||
rootWindow.Maximize();
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -105,6 +128,33 @@ namespace FlaxEditor.Windows
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether the game window is borderless (only in play mode).
|
||||||
|
/// </summary>
|
||||||
|
private bool IsBorderless
|
||||||
|
{
|
||||||
|
get => _isBorderless;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (_isBorderless == value)
|
||||||
|
return;
|
||||||
|
_isBorderless = value;
|
||||||
|
if (value)
|
||||||
|
{
|
||||||
|
IsFloating = true;
|
||||||
|
var rootWindow = RootWindow;
|
||||||
|
var monitorBounds = Platform.GetMonitorBounds(rootWindow.RootWindow.Window.ClientPosition);
|
||||||
|
rootWindow.Window.Position = monitorBounds.Location;
|
||||||
|
rootWindow.Window.SetBorderless(true);
|
||||||
|
rootWindow.Window.ClientSize = monitorBounds.Size;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
IsFloating = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets a value indicating whether center mouse position on window focus in play mode. Helps when working with games that lock mouse cursor.
|
/// Gets or sets a value indicating whether center mouse position on window focus in play mode. Helps when working with games that lock mouse cursor.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -318,6 +368,20 @@ namespace FlaxEditor.Windows
|
|||||||
InputActions.Add(options => options.TakeScreenshot, () => Screenshot.Capture(string.Empty));
|
InputActions.Add(options => options.TakeScreenshot, () => Screenshot.Capture(string.Empty));
|
||||||
InputActions.Add(options => options.DebuggerUnlockMouse, UnlockMouseInPlay);
|
InputActions.Add(options => options.DebuggerUnlockMouse, UnlockMouseInPlay);
|
||||||
InputActions.Add(options => options.ToggleFullscreen, () => { if (Editor.IsPlayMode) IsMaximized = !IsMaximized; });
|
InputActions.Add(options => options.ToggleFullscreen, () => { if (Editor.IsPlayMode) IsMaximized = !IsMaximized; });
|
||||||
|
InputActions.Add(options => options.Play, Editor.Instance.Simulation.DelegatePlayOrStopPlayInEditor);
|
||||||
|
InputActions.Add(options => options.Pause, Editor.Instance.Simulation.RequestResumeOrPause);
|
||||||
|
InputActions.Add(options => options.StepFrame, Editor.Instance.Simulation.RequestPlayOneFrame);
|
||||||
|
InputActions.Add(options => options.ProfilerStartStop, () =>
|
||||||
|
{
|
||||||
|
bool recording = !Editor.Instance.Windows.ProfilerWin.LiveRecording;
|
||||||
|
Editor.Instance.Windows.ProfilerWin.LiveRecording = recording;
|
||||||
|
Editor.Instance.UI.AddStatusMessage($"Profiling {(recording ? "started" : "stopped")}.");
|
||||||
|
});
|
||||||
|
InputActions.Add(options => options.ProfilerClear, () =>
|
||||||
|
{
|
||||||
|
Editor.Instance.Windows.ProfilerWin.Clear();
|
||||||
|
Editor.Instance.UI.AddStatusMessage($"Profiling results cleared.");
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ChangeViewportRatio(ViewportScaleOptions v)
|
private void ChangeViewportRatio(ViewportScaleOptions v)
|
||||||
@@ -474,7 +538,9 @@ namespace FlaxEditor.Windows
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void OnPlayEnd()
|
public override void OnPlayEnd()
|
||||||
{
|
{
|
||||||
|
IsFloating = false;
|
||||||
IsMaximized = false;
|
IsMaximized = false;
|
||||||
|
IsBorderless = false;
|
||||||
Cursor = CursorType.Default;
|
Cursor = CursorType.Default;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -856,7 +922,7 @@ namespace FlaxEditor.Windows
|
|||||||
var selection = Editor.SceneEditing.Selection;
|
var selection = Editor.SceneEditing.Selection;
|
||||||
for (var i = 0; i < selection.Count; i++)
|
for (var i = 0; i < selection.Count; i++)
|
||||||
{
|
{
|
||||||
if (selection[i].EditableObject is UIControl controlActor && controlActor && controlActor.Control != null)
|
if (selection[i].EditableObject is UIControl controlActor && controlActor && controlActor.Control != null && controlActor.Control.VisibleInHierarchy && controlActor.Control.RootWindow != null)
|
||||||
{
|
{
|
||||||
if (!drawAnySelectedControl)
|
if (!drawAnySelectedControl)
|
||||||
{
|
{
|
||||||
@@ -902,7 +968,7 @@ namespace FlaxEditor.Windows
|
|||||||
if (animTime < 0)
|
if (animTime < 0)
|
||||||
{
|
{
|
||||||
float alpha = Mathf.Saturate(-animTime / fadeOutTime);
|
float alpha = Mathf.Saturate(-animTime / fadeOutTime);
|
||||||
Render2D.DrawRectangle(new Rectangle(new Float2(4), Size - 8), Color.Orange * alpha);
|
Render2D.DrawRectangle(new Rectangle(new Float2(4), Size - 8), style.SelectionBorder * alpha);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add overlay during debugger breakpoint hang
|
// Add overlay during debugger breakpoint hang
|
||||||
@@ -956,6 +1022,29 @@ namespace FlaxEditor.Windows
|
|||||||
Focus();
|
Focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Apply the selected window mode to the game window.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="mode"></param>
|
||||||
|
public void SetWindowMode(InterfaceOptions.GameWindowMode mode)
|
||||||
|
{
|
||||||
|
switch (mode)
|
||||||
|
{
|
||||||
|
case InterfaceOptions.GameWindowMode.Docked:
|
||||||
|
break;
|
||||||
|
case InterfaceOptions.GameWindowMode.PopupWindow:
|
||||||
|
IsFloating = true;
|
||||||
|
break;
|
||||||
|
case InterfaceOptions.GameWindowMode.MaximizedWindow:
|
||||||
|
IsMaximized = true;
|
||||||
|
break;
|
||||||
|
case InterfaceOptions.GameWindowMode.BorderlessWindow:
|
||||||
|
IsBorderless = true;
|
||||||
|
break;
|
||||||
|
default: throw new ArgumentOutOfRangeException(nameof(mode), mode, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Takes the screenshot of the current viewport.
|
/// Takes the screenshot of the current viewport.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ using FlaxEditor.GUI.ContextMenu;
|
|||||||
using FlaxEditor.GUI.Docking;
|
using FlaxEditor.GUI.Docking;
|
||||||
using FlaxEditor.GUI.Input;
|
using FlaxEditor.GUI.Input;
|
||||||
using FlaxEditor.GUI.Tree;
|
using FlaxEditor.GUI.Tree;
|
||||||
|
using FlaxEditor.Options;
|
||||||
using FlaxEditor.Scripting;
|
using FlaxEditor.Scripting;
|
||||||
using FlaxEditor.Surface;
|
using FlaxEditor.Surface;
|
||||||
using FlaxEditor.Windows;
|
using FlaxEditor.Windows;
|
||||||
@@ -142,6 +143,7 @@ namespace FlaxEngine.Windows.Search
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override bool OnKeyDown(KeyboardKeys key)
|
public override bool OnKeyDown(KeyboardKeys key)
|
||||||
{
|
{
|
||||||
|
InputOptions options = FlaxEditor.Editor.Instance.Options.Options.Input;
|
||||||
if (IsFocused)
|
if (IsFocused)
|
||||||
{
|
{
|
||||||
if (key == KeyboardKeys.Return && Navigate != null)
|
if (key == KeyboardKeys.Return && Navigate != null)
|
||||||
@@ -149,7 +151,7 @@ namespace FlaxEngine.Windows.Search
|
|||||||
Navigate.Invoke(this);
|
Navigate.Invoke(this);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (key == KeyboardKeys.C && Root.GetKey(KeyboardKeys.Control))
|
if (options.Copy.Process(this))
|
||||||
{
|
{
|
||||||
Clipboard.Text = Text;
|
Clipboard.Text = Text;
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Behavior instance script that runs Behavior Tree execution.
|
/// Behavior instance script that runs Behavior Tree execution.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
API_CLASS() class FLAXENGINE_API Behavior : public Script
|
API_CLASS(Attributes="Category(\"Flax Engine\")") class FLAXENGINE_API Behavior : public Script
|
||||||
{
|
{
|
||||||
API_AUTO_SERIALIZATION();
|
API_AUTO_SERIALIZATION();
|
||||||
DECLARE_SCRIPTING_TYPE(Behavior);
|
DECLARE_SCRIPTING_TYPE(Behavior);
|
||||||
|
|||||||
@@ -706,6 +706,14 @@ bool BehaviorTreeKnowledgeValuesConditionalDecorator::CanUpdate(const BehaviorUp
|
|||||||
return BehaviorKnowledge::CompareValues((float)ValueA.Get(context.Knowledge), (float)ValueB.Get(context.Knowledge), Comparison);
|
return BehaviorKnowledge::CompareValues((float)ValueA.Get(context.Knowledge), (float)ValueB.Get(context.Knowledge), Comparison);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool BehaviorTreeKnowledgeBooleanDecorator::CanUpdate(const BehaviorUpdateContext& context)
|
||||||
|
{
|
||||||
|
Variant value = Value.Get(context.Knowledge);
|
||||||
|
bool result = (bool)value;
|
||||||
|
result ^= Invert;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
bool BehaviorTreeHasTagDecorator::CanUpdate(const BehaviorUpdateContext& context)
|
bool BehaviorTreeHasTagDecorator::CanUpdate(const BehaviorUpdateContext& context)
|
||||||
{
|
{
|
||||||
bool result = false;
|
bool result = false;
|
||||||
|
|||||||
@@ -445,6 +445,27 @@ public:
|
|||||||
bool CanUpdate(const BehaviorUpdateContext& context) override;
|
bool CanUpdate(const BehaviorUpdateContext& context) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks certain knowledge value to conditionally enter the node if the value is set (eg. not-null object reference or boolean value).
|
||||||
|
/// </summary>
|
||||||
|
API_CLASS(Sealed) class FLAXENGINE_API BehaviorTreeKnowledgeBooleanDecorator : public BehaviorTreeDecorator
|
||||||
|
{
|
||||||
|
DECLARE_SCRIPTING_TYPE_WITH_CONSTRUCTOR_IMPL(BehaviorTreeKnowledgeBooleanDecorator, BehaviorTreeDecorator);
|
||||||
|
API_AUTO_SERIALIZATION();
|
||||||
|
|
||||||
|
// The value from behavior's knowledge (blackboard, goal or sensor) to check if it's set (eg. not-null object reference or boolean value).
|
||||||
|
API_FIELD(Attributes="EditorOrder(0)")
|
||||||
|
BehaviorKnowledgeSelectorAny Value;
|
||||||
|
|
||||||
|
// If checked, the condition will be inverted.
|
||||||
|
API_FIELD(Attributes="EditorOrder(10)")
|
||||||
|
bool Invert = false;
|
||||||
|
|
||||||
|
public:
|
||||||
|
// [BehaviorTreeNode]
|
||||||
|
bool CanUpdate(const BehaviorUpdateContext& context) override;
|
||||||
|
};
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Checks if certain actor (from knowledge) has a given tag assigned.
|
/// Checks if certain actor (from knowledge) has a given tag assigned.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -900,5 +900,6 @@ private:
|
|||||||
Variant SampleState(AnimGraphContext& context, const AnimGraphNode* state);
|
Variant SampleState(AnimGraphContext& context, const AnimGraphNode* state);
|
||||||
void InitStateTransition(AnimGraphContext& context, AnimGraphInstanceData::StateMachineBucket& stateMachineBucket, AnimGraphStateTransition* transition = nullptr);
|
void InitStateTransition(AnimGraphContext& context, AnimGraphInstanceData::StateMachineBucket& stateMachineBucket, AnimGraphStateTransition* transition = nullptr);
|
||||||
AnimGraphStateTransition* UpdateStateTransitions(AnimGraphContext& context, const AnimGraphNode::StateMachineData& stateMachineData, AnimGraphNode* state, AnimGraphNode* ignoreState = nullptr);
|
AnimGraphStateTransition* UpdateStateTransitions(AnimGraphContext& context, const AnimGraphNode::StateMachineData& stateMachineData, AnimGraphNode* state, AnimGraphNode* ignoreState = nullptr);
|
||||||
|
AnimGraphStateTransition* UpdateStateTransitions(AnimGraphContext& context, const AnimGraphNode::StateMachineData& stateMachineData, const AnimGraphNode::StateBaseData& stateData, AnimGraphNode* state, AnimGraphNode* ignoreState = nullptr);
|
||||||
void UpdateStateTransitions(AnimGraphContext& context, const AnimGraphNode::StateMachineData& stateMachineData, AnimGraphInstanceData::StateMachineBucket& stateMachineBucket, const AnimGraphNode::StateBaseData& stateData);
|
void UpdateStateTransitions(AnimGraphContext& context, const AnimGraphNode::StateMachineData& stateMachineData, AnimGraphInstanceData::StateMachineBucket& stateMachineBucket, const AnimGraphNode::StateBaseData& stateData);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -563,9 +563,13 @@ void AnimGraphExecutor::InitStateTransition(AnimGraphContext& context, AnimGraph
|
|||||||
}
|
}
|
||||||
|
|
||||||
AnimGraphStateTransition* AnimGraphExecutor::UpdateStateTransitions(AnimGraphContext& context, const AnimGraphNode::StateMachineData& stateMachineData, AnimGraphNode* state, AnimGraphNode* ignoreState)
|
AnimGraphStateTransition* AnimGraphExecutor::UpdateStateTransitions(AnimGraphContext& context, const AnimGraphNode::StateMachineData& stateMachineData, AnimGraphNode* state, AnimGraphNode* ignoreState)
|
||||||
|
{
|
||||||
|
return UpdateStateTransitions(context, stateMachineData, state->Data.State, state, ignoreState);
|
||||||
|
}
|
||||||
|
|
||||||
|
AnimGraphStateTransition* AnimGraphExecutor::UpdateStateTransitions(AnimGraphContext& context, const AnimGraphNode::StateMachineData& stateMachineData, const AnimGraphNode::StateBaseData& stateData, AnimGraphNode* state, AnimGraphNode* ignoreState)
|
||||||
{
|
{
|
||||||
int32 transitionIndex = 0;
|
int32 transitionIndex = 0;
|
||||||
const AnimGraphNode::StateBaseData& stateData = state->Data.State;
|
|
||||||
while (transitionIndex < ANIM_GRAPH_MAX_STATE_TRANSITIONS && stateData.Transitions[transitionIndex] != AnimGraphNode::StateData::InvalidTransitionIndex)
|
while (transitionIndex < ANIM_GRAPH_MAX_STATE_TRANSITIONS && stateData.Transitions[transitionIndex] != AnimGraphNode::StateData::InvalidTransitionIndex)
|
||||||
{
|
{
|
||||||
const uint16 idx = stateData.Transitions[transitionIndex];
|
const uint16 idx = stateData.Transitions[transitionIndex];
|
||||||
@@ -640,7 +644,7 @@ AnimGraphStateTransition* AnimGraphExecutor::UpdateStateTransitions(AnimGraphCon
|
|||||||
|
|
||||||
void AnimGraphExecutor::UpdateStateTransitions(AnimGraphContext& context, const AnimGraphNode::StateMachineData& stateMachineData, AnimGraphInstanceData::StateMachineBucket& stateMachineBucket, const AnimGraphNode::StateBaseData& stateData)
|
void AnimGraphExecutor::UpdateStateTransitions(AnimGraphContext& context, const AnimGraphNode::StateMachineData& stateMachineData, AnimGraphInstanceData::StateMachineBucket& stateMachineBucket, const AnimGraphNode::StateBaseData& stateData)
|
||||||
{
|
{
|
||||||
AnimGraphStateTransition* transition = UpdateStateTransitions(context, stateMachineData, stateMachineBucket.CurrentState);
|
AnimGraphStateTransition* transition = UpdateStateTransitions(context, stateMachineData, stateData, stateMachineBucket.CurrentState);
|
||||||
if (transition)
|
if (transition)
|
||||||
{
|
{
|
||||||
InitStateTransition(context, stateMachineBucket, transition);
|
InitStateTransition(context, stateMachineBucket, transition);
|
||||||
|
|||||||
@@ -281,10 +281,26 @@ Asset::LoadResult SceneAnimation::load()
|
|||||||
track.TrackStateIndex = TrackStatesCount++;
|
track.TrackStateIndex = TrackStatesCount++;
|
||||||
trackRuntime->PropertyName = stream.Move<char>(trackData->PropertyNameLength + 1);
|
trackRuntime->PropertyName = stream.Move<char>(trackData->PropertyNameLength + 1);
|
||||||
trackRuntime->PropertyTypeName = stream.Move<char>(trackData->PropertyTypeNameLength + 1);
|
trackRuntime->PropertyTypeName = stream.Move<char>(trackData->PropertyTypeNameLength + 1);
|
||||||
const int32 keyframesDataSize = trackData->KeyframesCount * (sizeof(float) + trackData->ValueSize);
|
int32 keyframesDataSize = trackData->KeyframesCount * (sizeof(float) + trackData->ValueSize);
|
||||||
|
if (trackData->ValueSize == 0)
|
||||||
|
{
|
||||||
|
// When using json data (from non-POD types) read the sum of all keyframes data
|
||||||
|
const int32 keyframesDataStart = stream.GetPosition();
|
||||||
|
for (int32 j = 0; j < trackData->KeyframesCount; j++)
|
||||||
|
{
|
||||||
|
stream.Move<float>(); // Time
|
||||||
|
int32 jsonLen;
|
||||||
|
stream.ReadInt32(&jsonLen);
|
||||||
|
stream.Move(jsonLen);
|
||||||
|
}
|
||||||
|
const int32 keyframesDataEnd = stream.GetPosition();
|
||||||
|
stream.SetPosition(keyframesDataStart);
|
||||||
|
keyframesDataSize = keyframesDataEnd - keyframesDataStart;
|
||||||
|
}
|
||||||
trackRuntime->ValueSize = trackData->ValueSize;
|
trackRuntime->ValueSize = trackData->ValueSize;
|
||||||
trackRuntime->KeyframesCount = trackData->KeyframesCount;
|
trackRuntime->KeyframesCount = trackData->KeyframesCount;
|
||||||
trackRuntime->Keyframes = stream.Move(keyframesDataSize);
|
trackRuntime->Keyframes = stream.Move(keyframesDataSize);
|
||||||
|
trackRuntime->KeyframesSize = keyframesDataSize;
|
||||||
needsParent = true;
|
needsParent = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -298,6 +314,7 @@ Asset::LoadResult SceneAnimation::load()
|
|||||||
trackRuntime->PropertyName = stream.Move<char>(trackData->PropertyNameLength + 1);
|
trackRuntime->PropertyName = stream.Move<char>(trackData->PropertyNameLength + 1);
|
||||||
trackRuntime->PropertyTypeName = stream.Move<char>(trackData->PropertyTypeNameLength + 1);
|
trackRuntime->PropertyTypeName = stream.Move<char>(trackData->PropertyTypeNameLength + 1);
|
||||||
const int32 keyframesDataSize = trackData->KeyframesCount * (sizeof(float) + trackData->ValueSize * 3);
|
const int32 keyframesDataSize = trackData->KeyframesCount * (sizeof(float) + trackData->ValueSize * 3);
|
||||||
|
ASSERT(trackData->ValueSize > 0);
|
||||||
trackRuntime->ValueSize = trackData->ValueSize;
|
trackRuntime->ValueSize = trackData->ValueSize;
|
||||||
trackRuntime->KeyframesCount = trackData->KeyframesCount;
|
trackRuntime->KeyframesCount = trackData->KeyframesCount;
|
||||||
trackRuntime->Keyframes = stream.Move(keyframesDataSize);
|
trackRuntime->Keyframes = stream.Move(keyframesDataSize);
|
||||||
@@ -375,6 +392,7 @@ Asset::LoadResult SceneAnimation::load()
|
|||||||
trackRuntime->PropertyName = stream.Move<char>(trackData->PropertyNameLength + 1);
|
trackRuntime->PropertyName = stream.Move<char>(trackData->PropertyNameLength + 1);
|
||||||
trackRuntime->PropertyTypeName = stream.Move<char>(trackData->PropertyTypeNameLength + 1);
|
trackRuntime->PropertyTypeName = stream.Move<char>(trackData->PropertyTypeNameLength + 1);
|
||||||
trackRuntime->ValueSize = trackData->ValueSize;
|
trackRuntime->ValueSize = trackData->ValueSize;
|
||||||
|
ASSERT(trackData->ValueSize > 0);
|
||||||
trackRuntime->KeyframesCount = trackData->KeyframesCount;
|
trackRuntime->KeyframesCount = trackData->KeyframesCount;
|
||||||
const auto keyframesTimes = (float*)((byte*)trackRuntime + sizeof(StringPropertyTrack::Runtime));
|
const auto keyframesTimes = (float*)((byte*)trackRuntime + sizeof(StringPropertyTrack::Runtime));
|
||||||
const auto keyframesLengths = (int32*)((byte*)keyframesTimes + sizeof(float) * trackData->KeyframesCount);
|
const auto keyframesLengths = (int32*)((byte*)keyframesTimes + sizeof(float) * trackData->KeyframesCount);
|
||||||
|
|||||||
@@ -289,6 +289,7 @@ public:
|
|||||||
/// The keyframes array (items count is KeyframesCount). Each keyframe is represented by pair of time (of type float) and the value data (of size ValueSize).
|
/// The keyframes array (items count is KeyframesCount). Each keyframe is represented by pair of time (of type float) and the value data (of size ValueSize).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void* Keyframes;
|
void* Keyframes;
|
||||||
|
int32 KeyframesSize;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
#include "Engine/Level/SceneObjectsFactory.h"
|
#include "Engine/Level/SceneObjectsFactory.h"
|
||||||
#include "Engine/Level/Actors/Camera.h"
|
#include "Engine/Level/Actors/Camera.h"
|
||||||
#include "Engine/Serialization/Serialization.h"
|
#include "Engine/Serialization/Serialization.h"
|
||||||
|
#include "Engine/Serialization/MemoryReadStream.h"
|
||||||
#include "Engine/Audio/AudioClip.h"
|
#include "Engine/Audio/AudioClip.h"
|
||||||
#include "Engine/Audio/AudioSource.h"
|
#include "Engine/Audio/AudioSource.h"
|
||||||
#include "Engine/Graphics/RenderTask.h"
|
#include "Engine/Graphics/RenderTask.h"
|
||||||
@@ -19,6 +20,7 @@
|
|||||||
#include "Engine/Scripting/ManagedCLR/MField.h"
|
#include "Engine/Scripting/ManagedCLR/MField.h"
|
||||||
#include "Engine/Scripting/ManagedCLR/MClass.h"
|
#include "Engine/Scripting/ManagedCLR/MClass.h"
|
||||||
#include "Engine/Scripting/ManagedCLR/MMethod.h"
|
#include "Engine/Scripting/ManagedCLR/MMethod.h"
|
||||||
|
#include "Engine/Scripting/Internal/ManagedSerialization.h"
|
||||||
|
|
||||||
// This could be Update, LateUpdate or FixedUpdate
|
// This could be Update, LateUpdate or FixedUpdate
|
||||||
#define UPDATE_POINT Update
|
#define UPDATE_POINT Update
|
||||||
@@ -370,47 +372,96 @@ bool SceneAnimationPlayer::TickPropertyTrack(int32 trackIndex, int32 stateIndexO
|
|||||||
case SceneAnimation::Track::Types::KeyframesProperty:
|
case SceneAnimation::Track::Types::KeyframesProperty:
|
||||||
case SceneAnimation::Track::Types::ObjectReferenceProperty:
|
case SceneAnimation::Track::Types::ObjectReferenceProperty:
|
||||||
{
|
{
|
||||||
const auto trackDataKeyframes = track.GetRuntimeData<SceneAnimation::KeyframesPropertyTrack::Runtime>();
|
const auto trackRuntime = track.GetRuntimeData<SceneAnimation::KeyframesPropertyTrack::Runtime>();
|
||||||
const int32 count = trackDataKeyframes->KeyframesCount;
|
const int32 count = trackRuntime->KeyframesCount;
|
||||||
if (count == 0)
|
if (count == 0)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Find the keyframe at time
|
// If size is 0 then track uses Json storage for keyframes data (variable memory length of keyframes), otherwise it's optimized simple data with O(1) access
|
||||||
int32 keyframeSize = sizeof(float) + trackDataKeyframes->ValueSize;
|
if (trackRuntime->ValueSize != 0)
|
||||||
#define GET_KEY_TIME(idx) *(float*)((byte*)trackDataKeyframes->Keyframes + keyframeSize * (idx))
|
|
||||||
const float keyTime = Math::Clamp(time, 0.0f, GET_KEY_TIME(count - 1));
|
|
||||||
int32 start = 0;
|
|
||||||
int32 searchLength = count;
|
|
||||||
while (searchLength > 0)
|
|
||||||
{
|
{
|
||||||
const int32 half = searchLength >> 1;
|
// Find the keyframe at time (binary search)
|
||||||
int32 mid = start + half;
|
int32 keyframeSize = sizeof(float) + trackRuntime->ValueSize;
|
||||||
if (keyTime < GET_KEY_TIME(mid))
|
#define GET_KEY_TIME(idx) *(float*)((byte*)trackRuntime->Keyframes + keyframeSize * (idx))
|
||||||
|
const float keyTime = Math::Clamp(time, 0.0f, GET_KEY_TIME(count - 1));
|
||||||
|
int32 start = 0;
|
||||||
|
int32 searchLength = count;
|
||||||
|
while (searchLength > 0)
|
||||||
{
|
{
|
||||||
searchLength = half;
|
const int32 half = searchLength >> 1;
|
||||||
|
int32 mid = start + half;
|
||||||
|
if (keyTime < GET_KEY_TIME(mid))
|
||||||
|
{
|
||||||
|
searchLength = half;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
start = mid + 1;
|
||||||
|
searchLength -= half + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int32 leftKey = Math::Max(0, start - 1);
|
||||||
|
#undef GET_KEY_TIME
|
||||||
|
|
||||||
|
// Return the value
|
||||||
|
void* value = (void*)((byte*)trackRuntime->Keyframes + keyframeSize * (leftKey) + sizeof(float));
|
||||||
|
if (track.Type == SceneAnimation::Track::Types::ObjectReferenceProperty)
|
||||||
|
{
|
||||||
|
// Object ref track uses Guid for object Id storage
|
||||||
|
Guid id = *(Guid*)value;
|
||||||
|
_objectsMapping.TryGet(id, id);
|
||||||
|
auto obj = Scripting::FindObject<ScriptingObject>(id);
|
||||||
|
value = obj ? obj->GetOrCreateManagedInstance() : nullptr;
|
||||||
|
*(void**)target = value;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
start = mid + 1;
|
// POD memory
|
||||||
searchLength -= half + 1;
|
Platform::MemoryCopy(target, value, trackRuntime->ValueSize);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
int32 leftKey = Math::Max(0, start - 1);
|
|
||||||
#undef GET_KEY_TIME
|
|
||||||
|
|
||||||
// Return the value
|
|
||||||
void* value = (void*)((byte*)trackDataKeyframes->Keyframes + keyframeSize * (leftKey) + sizeof(float));
|
|
||||||
if (track.Type == SceneAnimation::Track::Types::ObjectReferenceProperty)
|
|
||||||
{
|
|
||||||
Guid id = *(Guid*)value;
|
|
||||||
_objectsMapping.TryGet(id, id);
|
|
||||||
auto obj = Scripting::FindObject<ScriptingObject>(id);
|
|
||||||
value = obj ? obj->GetOrCreateManagedInstance() : nullptr;
|
|
||||||
*(void**)target = value;
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Platform::MemoryCopy(target, value, trackDataKeyframes->ValueSize);
|
// Clear pointer
|
||||||
|
*(void**)target = nullptr;
|
||||||
|
|
||||||
|
// Find the keyframe at time (linear search)
|
||||||
|
MemoryReadStream stream((byte*)trackRuntime->Keyframes, trackRuntime->KeyframesSize);
|
||||||
|
int32 prevKeyPos = sizeof(float);
|
||||||
|
int32 jsonLen;
|
||||||
|
for (int32 key = 0; key < count; key++)
|
||||||
|
{
|
||||||
|
float keyTime;
|
||||||
|
stream.ReadFloat(&keyTime);
|
||||||
|
if (keyTime > time)
|
||||||
|
break;
|
||||||
|
prevKeyPos = stream.GetPosition();
|
||||||
|
stream.ReadInt32(&jsonLen);
|
||||||
|
stream.Move(jsonLen);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read json text
|
||||||
|
stream.SetPosition(prevKeyPos);
|
||||||
|
stream.ReadInt32(&jsonLen);
|
||||||
|
const StringAnsiView json((const char*)stream.GetPositionHandle(), jsonLen);
|
||||||
|
|
||||||
|
// Create empty value of the keyframe type
|
||||||
|
const auto trackData = track.GetData<SceneAnimation::KeyframesPropertyTrack::Data>();
|
||||||
|
const StringAnsiView propertyTypeName(trackRuntime->PropertyTypeName, trackData->PropertyTypeNameLength);
|
||||||
|
MClass* klass = Scripting::FindClass(propertyTypeName);
|
||||||
|
if (!klass)
|
||||||
|
return false;
|
||||||
|
MObject* obj = MCore::Object::New(klass);
|
||||||
|
if (!obj)
|
||||||
|
return false;
|
||||||
|
if (!klass->IsValueType())
|
||||||
|
MCore::Object::Init(obj);
|
||||||
|
|
||||||
|
// Deserialize value from json
|
||||||
|
ManagedSerialization::Deserialize(json, obj);
|
||||||
|
|
||||||
|
// Set value
|
||||||
|
*(void**)target = obj;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -479,13 +530,13 @@ bool SceneAnimationPlayer::TickPropertyTrack(int32 trackIndex, int32 stateIndexO
|
|||||||
}
|
}
|
||||||
case SceneAnimation::Track::Types::StringProperty:
|
case SceneAnimation::Track::Types::StringProperty:
|
||||||
{
|
{
|
||||||
const auto trackDataKeyframes = track.GetRuntimeData<SceneAnimation::StringPropertyTrack::Runtime>();
|
const auto trackRuntime = track.GetRuntimeData<SceneAnimation::StringPropertyTrack::Runtime>();
|
||||||
const int32 count = trackDataKeyframes->KeyframesCount;
|
const int32 count = trackRuntime->KeyframesCount;
|
||||||
if (count == 0)
|
if (count == 0)
|
||||||
return false;
|
return false;
|
||||||
const auto keyframesTimes = (float*)((byte*)trackDataKeyframes + sizeof(SceneAnimation::StringPropertyTrack::Runtime));
|
const auto keyframesTimes = (float*)((byte*)trackRuntime + sizeof(SceneAnimation::StringPropertyTrack::Runtime));
|
||||||
const auto keyframesLengths = (int32*)((byte*)keyframesTimes + sizeof(float) * trackDataKeyframes->KeyframesCount);
|
const auto keyframesLengths = (int32*)((byte*)keyframesTimes + sizeof(float) * trackRuntime->KeyframesCount);
|
||||||
const auto keyframesValues = (Char**)((byte*)keyframesLengths + sizeof(int32) * trackDataKeyframes->KeyframesCount);
|
const auto keyframesValues = (Char**)((byte*)keyframesLengths + sizeof(int32) * trackRuntime->KeyframesCount);
|
||||||
|
|
||||||
// Find the keyframe at time
|
// Find the keyframe at time
|
||||||
#define GET_KEY_TIME(idx) keyframesTimes[idx]
|
#define GET_KEY_TIME(idx) keyframesTimes[idx]
|
||||||
@@ -522,7 +573,7 @@ bool SceneAnimationPlayer::TickPropertyTrack(int32 trackIndex, int32 stateIndexO
|
|||||||
auto& childTrack = anim->Tracks[childTrackIndex];
|
auto& childTrack = anim->Tracks[childTrackIndex];
|
||||||
if (childTrack.Disabled || childTrack.ParentIndex != trackIndex)
|
if (childTrack.Disabled || childTrack.ParentIndex != trackIndex)
|
||||||
continue;
|
continue;
|
||||||
const auto childTrackRuntimeData = childTrack.GetRuntimeData<SceneAnimation::PropertyTrack::Runtime>();
|
const auto childTrackRuntime = childTrack.GetRuntimeData<SceneAnimation::PropertyTrack::Runtime>();
|
||||||
auto& childTrackState = _tracks[stateIndexOffset + childTrack.TrackStateIndex];
|
auto& childTrackState = _tracks[stateIndexOffset + childTrack.TrackStateIndex];
|
||||||
|
|
||||||
// Cache field
|
// Cache field
|
||||||
@@ -532,7 +583,7 @@ bool SceneAnimationPlayer::TickPropertyTrack(int32 trackIndex, int32 stateIndexO
|
|||||||
if (!type)
|
if (!type)
|
||||||
continue;
|
continue;
|
||||||
MClass* mclass = MCore::Type::GetClass(type);
|
MClass* mclass = MCore::Type::GetClass(type);
|
||||||
childTrackState.Field = mclass->GetField(childTrackRuntimeData->PropertyName);
|
childTrackState.Field = mclass->GetField(childTrackRuntime->PropertyName);
|
||||||
if (!childTrackState.Field)
|
if (!childTrackState.Field)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -956,7 +1007,8 @@ void SceneAnimationPlayer::Tick(SceneAnimation* anim, float time, float dt, int3
|
|||||||
if (TickPropertyTrack(j, stateIndexOffset, anim, time, track, state, value))
|
if (TickPropertyTrack(j, stateIndexOffset, anim, time, track, state, value))
|
||||||
{
|
{
|
||||||
// Set the value
|
// Set the value
|
||||||
if (MCore::Type::IsPointer(valueType))
|
auto valueTypes = MCore::Type::GetType(valueType);
|
||||||
|
if (valueTypes == MTypes::Object || MCore::Type::IsPointer(valueType))
|
||||||
value = (void*)*(intptr*)value;
|
value = (void*)*(intptr*)value;
|
||||||
if (state.Property)
|
if (state.Property)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ uint64 RawDataAsset::GetMemoryUsage() const
|
|||||||
Locker.Lock();
|
Locker.Lock();
|
||||||
uint64 result = BinaryAsset::GetMemoryUsage();
|
uint64 result = BinaryAsset::GetMemoryUsage();
|
||||||
result += sizeof(RawDataAsset) - sizeof(BinaryAsset);
|
result += sizeof(RawDataAsset) - sizeof(BinaryAsset);
|
||||||
result += Data.Count();
|
result += Data.Capacity();
|
||||||
Locker.Unlock();
|
Locker.Unlock();
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -198,6 +198,12 @@ void ContentService::Dispose()
|
|||||||
Graphics::DisposeDevice();
|
Graphics::DisposeDevice();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
IAssetFactory::Collection& IAssetFactory::Get()
|
||||||
|
{
|
||||||
|
static Collection Factories(1024);
|
||||||
|
return Factories;
|
||||||
|
}
|
||||||
|
|
||||||
AssetsCache* Content::GetRegistry()
|
AssetsCache* Content::GetRegistry()
|
||||||
{
|
{
|
||||||
return &Cache;
|
return &Cache;
|
||||||
|
|||||||
@@ -20,11 +20,7 @@ public:
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the all registered assets factories. Key is asset typename, value is the factory object.
|
/// Gets the all registered assets factories. Key is asset typename, value is the factory object.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
static Collection& Get()
|
static Collection& Get();
|
||||||
{
|
|
||||||
static Collection Factories(1024);
|
|
||||||
return Factories;
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -943,6 +943,11 @@ void DebugDraw::DrawDirection(const Vector3& origin, const Vector3& direction, c
|
|||||||
DrawLine(origin, origin + direction, color, duration, depthTest);
|
DrawLine(origin, origin + direction, color, duration, depthTest);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DebugDraw::DrawRay(const Vector3& origin, const Vector3& direction, const Color& color, float duration, bool depthTest)
|
||||||
|
{
|
||||||
|
DrawLine(origin, origin + direction, color, duration, depthTest);
|
||||||
|
}
|
||||||
|
|
||||||
void DebugDraw::DrawRay(const Vector3& origin, const Vector3& direction, const Color& color, float length, float duration, bool depthTest)
|
void DebugDraw::DrawRay(const Vector3& origin, const Vector3& direction, const Color& color, float length, float duration, bool depthTest)
|
||||||
{
|
{
|
||||||
if (isnan(length) || isinf(length))
|
if (isnan(length) || isinf(length))
|
||||||
|
|||||||
@@ -87,6 +87,17 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw
|
|||||||
/// <param name="depthTest">If set to <c>true</c> depth test will be performed, otherwise depth will be ignored.</param>
|
/// <param name="depthTest">If set to <c>true</c> depth test will be performed, otherwise depth will be ignored.</param>
|
||||||
API_FUNCTION() static void DrawDirection(const Vector3& origin, const Vector3& direction, const Color& color = Color::White, float duration = 0.0f, bool depthTest = true);
|
API_FUNCTION() static void DrawDirection(const Vector3& origin, const Vector3& direction, const Color& color = Color::White, float duration = 0.0f, bool depthTest = true);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Draws the line in a direction.
|
||||||
|
/// [Deprecated in v1.8]
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="origin">The origin of the line.</param>
|
||||||
|
/// <param name="direction">The direction of the line.</param>
|
||||||
|
/// <param name="color">The color.</param>
|
||||||
|
/// <param name="duration">The duration (in seconds). Use 0 to draw it only once.</param>
|
||||||
|
/// <param name="depthTest">If set to <c>true</c> depth test will be performed, otherwise depth will be ignored.</param>
|
||||||
|
API_FUNCTION() DEPRECATED static void DrawRay(const Vector3& origin, const Vector3& direction, const Color& color = Color::White, float duration = 0.0f, bool depthTest = true);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Draws the line in a direction.
|
/// Draws the line in a direction.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ namespace FlaxEngine
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Virtual input axis binding. Helps with listening for a selected axis input.
|
/// Virtual input axis binding. Helps with listening for a selected axis input.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class InputAxis
|
public class InputAxis : IComparable, IComparable<InputAxis>
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The name of the axis to use. See <see cref="Input.AxisMappings"/>.
|
/// The name of the axis to use. See <see cref="Input.AxisMappings"/>.
|
||||||
@@ -47,13 +47,13 @@ namespace FlaxEngine
|
|||||||
Input.AxisValueChanged += Handler;
|
Input.AxisValueChanged += Handler;
|
||||||
Name = name;
|
Name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Handler(string name)
|
private void Handler(string name)
|
||||||
{
|
{
|
||||||
if (string.Equals(Name, name, StringComparison.OrdinalIgnoreCase))
|
if (string.Equals(Name, name, StringComparison.OrdinalIgnoreCase))
|
||||||
ValueChanged?.Invoke();
|
ValueChanged?.Invoke();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Finalizes an instance of the <see cref="InputAxis"/> class.
|
/// Finalizes an instance of the <see cref="InputAxis"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -61,7 +61,7 @@ namespace FlaxEngine
|
|||||||
{
|
{
|
||||||
Input.AxisValueChanged -= Handler;
|
Input.AxisValueChanged -= Handler;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Releases this object.
|
/// Releases this object.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -70,5 +70,35 @@ namespace FlaxEngine
|
|||||||
Input.AxisValueChanged -= Handler;
|
Input.AxisValueChanged -= Handler;
|
||||||
GC.SuppressFinalize(this);
|
GC.SuppressFinalize(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public int CompareTo(InputAxis other)
|
||||||
|
{
|
||||||
|
return string.Compare(Name, other.Name, StringComparison.Ordinal);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public int CompareTo(object obj)
|
||||||
|
{
|
||||||
|
return obj is InputAxis other ? CompareTo(other) : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override int GetHashCode()
|
||||||
|
{
|
||||||
|
return Name?.GetHashCode() ?? 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override bool Equals(object obj)
|
||||||
|
{
|
||||||
|
return obj is InputAxis other && string.Equals(Name, other.Name, StringComparison.Ordinal);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return Name;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ namespace FlaxEngine
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Virtual input action binding. Helps with listening for a selected input event.
|
/// Virtual input action binding. Helps with listening for a selected input event.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class InputEvent
|
public class InputEvent : IComparable, IComparable<InputEvent>
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The name of the action to use. See <see cref="Input.ActionMappings"/>.
|
/// The name of the action to use. See <see cref="Input.ActionMappings"/>.
|
||||||
@@ -21,7 +21,7 @@ namespace FlaxEngine
|
|||||||
public bool Active => Input.GetAction(Name);
|
public bool Active => Input.GetAction(Name);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns the event state. Use Use <see cref="Pressed"/>, <see cref="Pressing"/>, <see cref="Released"/> to catch events without active waiting.
|
/// Returns the event state. Use <see cref="Pressed"/>, <see cref="Pressing"/>, <see cref="Released"/> to catch events without active waiting.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public InputActionState State => Input.GetActionState(Name);
|
public InputActionState State => Input.GetActionState(Name);
|
||||||
|
|
||||||
@@ -35,12 +35,12 @@ namespace FlaxEngine
|
|||||||
/// Occurs when event is pressed (e.g. user pressed a key). Called before scripts update.
|
/// Occurs when event is pressed (e.g. user pressed a key). Called before scripts update.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public event Action Pressed;
|
public event Action Pressed;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Occurs when event is being pressing (e.g. user pressing a key). Called before scripts update.
|
/// Occurs when event is being pressing (e.g. user pressing a key). Called before scripts update.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public event Action Pressing;
|
public event Action Pressing;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Occurs when event is released (e.g. user releases a key). Called before scripts update.
|
/// Occurs when event is released (e.g. user releases a key). Called before scripts update.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -102,5 +102,35 @@ namespace FlaxEngine
|
|||||||
Input.ActionTriggered -= Handler;
|
Input.ActionTriggered -= Handler;
|
||||||
GC.SuppressFinalize(this);
|
GC.SuppressFinalize(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public int CompareTo(InputEvent other)
|
||||||
|
{
|
||||||
|
return string.Compare(Name, other.Name, StringComparison.Ordinal);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public int CompareTo(object obj)
|
||||||
|
{
|
||||||
|
return obj is InputEvent other ? CompareTo(other) : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override int GetHashCode()
|
||||||
|
{
|
||||||
|
return Name?.GetHashCode() ?? 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override bool Equals(object obj)
|
||||||
|
{
|
||||||
|
return obj is InputEvent other && string.Equals(Name, other.Name, StringComparison.Ordinal);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return Name;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ namespace FlaxEngine.Interop
|
|||||||
#endif
|
#endif
|
||||||
private static Dictionary<object, ManagedHandle> classAttributesCacheCollectible = new();
|
private static Dictionary<object, ManagedHandle> classAttributesCacheCollectible = new();
|
||||||
private static Dictionary<Assembly, ManagedHandle> assemblyHandles = new();
|
private static Dictionary<Assembly, ManagedHandle> assemblyHandles = new();
|
||||||
private static Dictionary<Type, int> _typeSizeCache = new();
|
private static ConcurrentDictionary<Type, int> _typeSizeCache = new();
|
||||||
|
|
||||||
private static Dictionary<string, IntPtr> loadedNativeLibraries = new();
|
private static Dictionary<string, IntPtr> loadedNativeLibraries = new();
|
||||||
internal static Dictionary<string, string> libraryPaths = new();
|
internal static Dictionary<string, string> libraryPaths = new();
|
||||||
@@ -1594,7 +1594,7 @@ namespace FlaxEngine.Interop
|
|||||||
private static IntPtr PinValue<T>(T value) where T : struct
|
private static IntPtr PinValue<T>(T value) where T : struct
|
||||||
{
|
{
|
||||||
// Store the converted value in unmanaged memory so it will not be relocated by the garbage collector.
|
// Store the converted value in unmanaged memory so it will not be relocated by the garbage collector.
|
||||||
int size = GetTypeSize(typeof(T));
|
int size = TypeHelpers<T>.MarshalSize;
|
||||||
uint index = Interlocked.Increment(ref pinnedAllocationsPointer) % (uint)pinnedAllocations.Length;
|
uint index = Interlocked.Increment(ref pinnedAllocationsPointer) % (uint)pinnedAllocations.Length;
|
||||||
ref (IntPtr ptr, int size) alloc = ref pinnedAllocations[index];
|
ref (IntPtr ptr, int size) alloc = ref pinnedAllocations[index];
|
||||||
if (alloc.size < size)
|
if (alloc.size < size)
|
||||||
@@ -1727,25 +1727,36 @@ namespace FlaxEngine.Interop
|
|||||||
return tuple;
|
return tuple;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static int GetTypeSize(Type type)
|
internal static class TypeHelpers<T>
|
||||||
{
|
{
|
||||||
if (!_typeSizeCache.TryGetValue(type, out var size))
|
public static readonly int MarshalSize;
|
||||||
|
static TypeHelpers()
|
||||||
{
|
{
|
||||||
|
Type type = typeof(T);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var marshalType = type;
|
var marshalType = type;
|
||||||
if (type.IsEnum)
|
if (type.IsEnum)
|
||||||
marshalType = type.GetEnumUnderlyingType();
|
marshalType = type.GetEnumUnderlyingType();
|
||||||
size = Marshal.SizeOf(marshalType);
|
MarshalSize = Marshal.SizeOf(marshalType);
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
// Workaround the issue where structure defined within generic type instance (eg. MyType<int>.MyStruct) fails to get size
|
// Workaround the issue where structure defined within generic type instance (eg. MyType<int>.MyStruct) fails to get size
|
||||||
// https://github.com/dotnet/runtime/issues/46426
|
// https://github.com/dotnet/runtime/issues/46426
|
||||||
var obj = Activator.CreateInstance(type);
|
var obj = RuntimeHelpers.GetUninitializedObject(type);
|
||||||
size = Marshal.SizeOf(obj);
|
MarshalSize = Marshal.SizeOf(obj);
|
||||||
}
|
}
|
||||||
_typeSizeCache.Add(type, size);
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static int GetTypeSize(Type type)
|
||||||
|
{
|
||||||
|
if (!_typeSizeCache.TryGetValue(type, out int size))
|
||||||
|
{
|
||||||
|
var marshalSizeField = typeof(TypeHelpers<>).MakeGenericType(type).GetField(nameof(TypeHelpers<int>.MarshalSize), BindingFlags.Static | BindingFlags.Public);
|
||||||
|
size = (int)marshalSizeField.GetValue(null);
|
||||||
|
_typeSizeCache.AddOrUpdate(type, size, (t, v) => size);
|
||||||
}
|
}
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1764,14 +1764,6 @@ bool Actor::FromBytes(const Span<byte>& data, Array<Actor*>& output, ISerializeM
|
|||||||
}
|
}
|
||||||
Scripting::ObjectsLookupIdMapping.Set(nullptr);
|
Scripting::ObjectsLookupIdMapping.Set(nullptr);
|
||||||
|
|
||||||
// Link objects
|
|
||||||
//for (int32 i = 0; i < objectsCount; i++)
|
|
||||||
{
|
|
||||||
//SceneObject* obj = sceneObjects->At(i);
|
|
||||||
// TODO: post load or post spawn?
|
|
||||||
//obj->PostLoad();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update objects order
|
// Update objects order
|
||||||
//for (int32 i = 0; i < objectsCount; i++)
|
//for (int32 i = 0; i < objectsCount; i++)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -390,7 +390,7 @@ namespace FlaxEngine
|
|||||||
}
|
}
|
||||||
|
|
||||||
#if FLAX_EDITOR
|
#if FLAX_EDITOR
|
||||||
internal bool ShowTransform => !(this is UIControl);
|
private bool ShowTransform => !(this is UIControl);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ void SceneObject::BreakPrefabLink()
|
|||||||
|
|
||||||
String SceneObject::GetNamePath(Char separatorChar) const
|
String SceneObject::GetNamePath(Char separatorChar) const
|
||||||
{
|
{
|
||||||
Array<String> names;
|
Array<StringView> names;
|
||||||
const Actor* a = dynamic_cast<const Actor*>(this);
|
const Actor* a = dynamic_cast<const Actor*>(this);
|
||||||
if (!a)
|
if (!a)
|
||||||
a = GetParent();
|
a = GetParent();
|
||||||
@@ -75,6 +75,8 @@ String SceneObject::GetNamePath(Char separatorChar) const
|
|||||||
int32 length = names.Count() - 1;
|
int32 length = names.Count() - 1;
|
||||||
for (int32 i = 0; i < names.Count(); i++)
|
for (int32 i = 0; i < names.Count(); i++)
|
||||||
length += names[i].Length();
|
length += names[i].Length();
|
||||||
|
if (length == 0)
|
||||||
|
return String::Empty;
|
||||||
String result;
|
String result;
|
||||||
result.ReserveSpace(length);
|
result.ReserveSpace(length);
|
||||||
Char* ptr = result.Get();
|
Char* ptr = result.Get();
|
||||||
|
|||||||
@@ -325,7 +325,7 @@ void SceneObjectsFactory::HandleObjectDeserializationError(const ISerializable::
|
|||||||
dummyScript->Data = MoveTemp(bufferStr);
|
dummyScript->Data = MoveTemp(bufferStr);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
LOG(Warning, "Parent actor of the missing object: {0}", parent->GetName());
|
LOG(Warning, "Parent actor of the missing object: '{0}' ({1})", parent->GetNamePath(), String(parent->GetType().Fullname));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
#include "SceneQuery.h"
|
#include "SceneQuery.h"
|
||||||
#include "Engine/Scripting/Script.h"
|
#include "Engine/Scripting/Script.h"
|
||||||
#include "Engine/Profiler/Profiler.h"
|
#include "Engine/Profiler/Profiler.h"
|
||||||
|
#include "Scripts/MissingScript.h"
|
||||||
|
|
||||||
Actor* SceneQuery::RaycastScene(const Ray& ray)
|
Actor* SceneQuery::RaycastScene(const Ray& ray)
|
||||||
{
|
{
|
||||||
@@ -51,6 +52,15 @@ bool GetAllSerializableSceneObjectsQuery(Actor* actor, Array<SceneObject*>& obje
|
|||||||
return false;
|
return false;
|
||||||
objects.Add(actor);
|
objects.Add(actor);
|
||||||
objects.Add(reinterpret_cast<SceneObject* const*>(actor->Scripts.Get()), actor->Scripts.Count());
|
objects.Add(reinterpret_cast<SceneObject* const*>(actor->Scripts.Get()), actor->Scripts.Count());
|
||||||
|
#if USE_EDITOR
|
||||||
|
// Skip saving Missing Script instances
|
||||||
|
for (int32 i = 0; i < actor->Scripts.Count(); i++)
|
||||||
|
{
|
||||||
|
const int32 idx = objects.Count() - i - 1;
|
||||||
|
if (objects.Get()[idx]->GetTypeHandle() == MissingScript::TypeInitializer)
|
||||||
|
objects.RemoveAtKeepOrder(idx);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,9 @@ int main(int argc, char* argv[])
|
|||||||
StringBuilder args;
|
StringBuilder args;
|
||||||
for (int i = 1; i < argc; i++)
|
for (int i = 1; i < argc; i++)
|
||||||
{
|
{
|
||||||
args.Append(argv[i]);
|
String arg;
|
||||||
|
arg.SetUTF8(argv[i], StringUtils::Length(argv[i]));
|
||||||
|
args.Append(arg);
|
||||||
|
|
||||||
if (i + 1 != argc)
|
if (i + 1 != argc)
|
||||||
args.Append(TEXT(' '));
|
args.Append(TEXT(' '));
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ bool NavCrowd::Init(float maxAgentRadius, int32 maxAgents, NavMeshRuntime* navMe
|
|||||||
{
|
{
|
||||||
if (!_crowd || !navMesh)
|
if (!_crowd || !navMesh)
|
||||||
return true;
|
return true;
|
||||||
|
PROFILE_CPU();
|
||||||
|
|
||||||
// This can happen on game start when no navmesh is loaded yet (eg. navmesh tiles data is during streaming) so wait for navmesh
|
// This can happen on game start when no navmesh is loaded yet (eg. navmesh tiles data is during streaming) so wait for navmesh
|
||||||
if (navMesh->GetNavMesh() == nullptr)
|
if (navMesh->GetNavMesh() == nullptr)
|
||||||
@@ -145,6 +146,7 @@ void NavCrowd::ResetAgentMove(int32 id)
|
|||||||
|
|
||||||
void NavCrowd::RemoveAgent(int32 id)
|
void NavCrowd::RemoveAgent(int32 id)
|
||||||
{
|
{
|
||||||
|
CHECK(id >= 0 && id < _crowd->getAgentCount());
|
||||||
_crowd->removeAgent(id);
|
_crowd->removeAgent(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
/// Actor script component that synchronizes the Transform over the network.
|
/// Actor script component that synchronizes the Transform over the network.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>Interpolation and prediction logic based on https://www.gabrielgambetta.com/client-server-game-architecture.html.</remarks>
|
/// <remarks>Interpolation and prediction logic based on https://www.gabrielgambetta.com/client-server-game-architecture.html.</remarks>
|
||||||
API_CLASS(Namespace="FlaxEngine.Networking") class FLAXENGINE_API NetworkTransform : public Script, public INetworkSerializable
|
API_CLASS(Namespace="FlaxEngine.Networking", Attributes="Category(\"Flax Engine\")") class FLAXENGINE_API NetworkTransform : public Script, public INetworkSerializable
|
||||||
{
|
{
|
||||||
API_AUTO_SERIALIZATION();
|
API_AUTO_SERIALIZATION();
|
||||||
DECLARE_SCRIPTING_TYPE(NetworkTransform);
|
DECLARE_SCRIPTING_TYPE(NetworkTransform);
|
||||||
|
|||||||
@@ -3469,7 +3469,8 @@ void* PhysicsBackend::CreateVehicle(WheeledVehicle* actor)
|
|||||||
Array<PxShape*, InlinedAllocation<8>> shapes;
|
Array<PxShape*, InlinedAllocation<8>> shapes;
|
||||||
shapes.Resize(actorPhysX->getNbShapes());
|
shapes.Resize(actorPhysX->getNbShapes());
|
||||||
actorPhysX->getShapes(shapes.Get(), shapes.Count(), 0);
|
actorPhysX->getShapes(shapes.Get(), shapes.Count(), 0);
|
||||||
const PxTransform centerOfMassOffset = actorPhysX->getCMassLocalPose();
|
PxTransform centerOfMassOffset = actorPhysX->getCMassLocalPose();
|
||||||
|
centerOfMassOffset.q = PxQuat(PxIdentity);
|
||||||
|
|
||||||
// Initialize wheels simulation data
|
// Initialize wheels simulation data
|
||||||
PxVec3 offsets[PX_MAX_NB_WHEELS];
|
PxVec3 offsets[PX_MAX_NB_WHEELS];
|
||||||
|
|||||||
@@ -366,7 +366,13 @@ void PlatformBase::Error(const Char* msg)
|
|||||||
#if PLATFORM_HAS_HEADLESS_MODE
|
#if PLATFORM_HAS_HEADLESS_MODE
|
||||||
if (CommandLine::Options.Headless)
|
if (CommandLine::Options.Headless)
|
||||||
{
|
{
|
||||||
|
#if PLATFORM_TEXT_IS_CHAR16
|
||||||
|
StringAnsi ansi(msg);
|
||||||
|
ansi += PLATFORM_LINE_TERMINATOR;
|
||||||
|
printf("Error: %s\n", ansi.Get());
|
||||||
|
#else
|
||||||
std::cout << "Error: " << msg << std::endl;
|
std::cout << "Error: " << msg << std::endl;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -28,8 +28,8 @@ const DateTime UnixEpoch(1970, 1, 1);
|
|||||||
|
|
||||||
bool LinuxFileSystem::ShowOpenFileDialog(Window* parentWindow, const StringView& initialDirectory, const StringView& filter, bool multiSelect, const StringView& title, Array<String, HeapAllocation>& filenames)
|
bool LinuxFileSystem::ShowOpenFileDialog(Window* parentWindow, const StringView& initialDirectory, const StringView& filter, bool multiSelect, const StringView& title, Array<String, HeapAllocation>& filenames)
|
||||||
{
|
{
|
||||||
const StringAsANSI<> initialDirectoryAnsi(*initialDirectory, initialDirectory.Length());
|
const StringAsUTF8<> initialDirectoryAnsi(*initialDirectory, initialDirectory.Length());
|
||||||
const StringAsANSI<> titleAnsi(*title, title.Length());
|
const StringAsUTF8<> titleAnsi(*title, title.Length());
|
||||||
const char* initDir = initialDirectory.HasChars() ? initialDirectoryAnsi.Get() : ".";
|
const char* initDir = initialDirectory.HasChars() ? initialDirectoryAnsi.Get() : ".";
|
||||||
String xdgCurrentDesktop;
|
String xdgCurrentDesktop;
|
||||||
StringBuilder fileFilter;
|
StringBuilder fileFilter;
|
||||||
@@ -113,7 +113,7 @@ bool LinuxFileSystem::ShowOpenFileDialog(Window* parentWindow, const StringView&
|
|||||||
|
|
||||||
bool LinuxFileSystem::ShowBrowseFolderDialog(Window* parentWindow, const StringView& initialDirectory, const StringView& title, String& path)
|
bool LinuxFileSystem::ShowBrowseFolderDialog(Window* parentWindow, const StringView& initialDirectory, const StringView& title, String& path)
|
||||||
{
|
{
|
||||||
const StringAsANSI<> titleAnsi(*title, title.Length());
|
const StringAsUTF8<> titleAnsi(*title, title.Length());
|
||||||
String xdgCurrentDesktop;
|
String xdgCurrentDesktop;
|
||||||
Platform::GetEnvironmentVariable(TEXT("XDG_CURRENT_DESKTOP"), xdgCurrentDesktop);
|
Platform::GetEnvironmentVariable(TEXT("XDG_CURRENT_DESKTOP"), xdgCurrentDesktop);
|
||||||
|
|
||||||
@@ -158,7 +158,7 @@ bool LinuxFileSystem::ShowBrowseFolderDialog(Window* parentWindow, const StringV
|
|||||||
|
|
||||||
bool LinuxFileSystem::ShowFileExplorer(const StringView& path)
|
bool LinuxFileSystem::ShowFileExplorer(const StringView& path)
|
||||||
{
|
{
|
||||||
const StringAsANSI<> pathAnsi(*path, path.Length());
|
const StringAsUTF8<> pathAnsi(*path, path.Length());
|
||||||
char cmd[2048];
|
char cmd[2048];
|
||||||
sprintf(cmd, "xdg-open %s &", pathAnsi.Get());
|
sprintf(cmd, "xdg-open %s &", pathAnsi.Get());
|
||||||
system(cmd);
|
system(cmd);
|
||||||
@@ -167,7 +167,7 @@ bool LinuxFileSystem::ShowFileExplorer(const StringView& path)
|
|||||||
|
|
||||||
bool LinuxFileSystem::CreateDirectory(const StringView& path)
|
bool LinuxFileSystem::CreateDirectory(const StringView& path)
|
||||||
{
|
{
|
||||||
const StringAsANSI<> pathAnsi(*path, path.Length());
|
const StringAsUTF8<> pathAnsi(*path, path.Length());
|
||||||
|
|
||||||
// Skip if already exists
|
// Skip if already exists
|
||||||
struct stat fileInfo;
|
struct stat fileInfo;
|
||||||
@@ -258,7 +258,7 @@ bool DeleteUnixPathTree(const char* path)
|
|||||||
|
|
||||||
bool LinuxFileSystem::DeleteDirectory(const String& path, bool deleteContents)
|
bool LinuxFileSystem::DeleteDirectory(const String& path, bool deleteContents)
|
||||||
{
|
{
|
||||||
const StringAsANSI<> pathANSI(*path, path.Length());
|
const StringAsUTF8<> pathANSI(*path, path.Length());
|
||||||
if (deleteContents)
|
if (deleteContents)
|
||||||
{
|
{
|
||||||
return DeleteUnixPathTree(pathANSI.Get());
|
return DeleteUnixPathTree(pathANSI.Get());
|
||||||
@@ -272,7 +272,7 @@ bool LinuxFileSystem::DeleteDirectory(const String& path, bool deleteContents)
|
|||||||
bool LinuxFileSystem::DirectoryExists(const StringView& path)
|
bool LinuxFileSystem::DirectoryExists(const StringView& path)
|
||||||
{
|
{
|
||||||
struct stat fileInfo;
|
struct stat fileInfo;
|
||||||
const StringAsANSI<> pathANSI(*path, path.Length());
|
const StringAsUTF8<> pathANSI(*path, path.Length());
|
||||||
if (stat(pathANSI.Get(), &fileInfo) != -1)
|
if (stat(pathANSI.Get(), &fileInfo) != -1)
|
||||||
{
|
{
|
||||||
return S_ISDIR(fileInfo.st_mode);
|
return S_ISDIR(fileInfo.st_mode);
|
||||||
@@ -282,8 +282,8 @@ bool LinuxFileSystem::DirectoryExists(const StringView& path)
|
|||||||
|
|
||||||
bool LinuxFileSystem::DirectoryGetFiles(Array<String>& results, const String& path, const Char* searchPattern, DirectorySearchOption option)
|
bool LinuxFileSystem::DirectoryGetFiles(Array<String>& results, const String& path, const Char* searchPattern, DirectorySearchOption option)
|
||||||
{
|
{
|
||||||
const StringAsANSI<> pathANSI(*path, path.Length());
|
const StringAsUTF8<> pathANSI(*path, path.Length());
|
||||||
const StringAsANSI<> searchPatternANSI(searchPattern);
|
const StringAsUTF8<> searchPatternANSI(searchPattern);
|
||||||
|
|
||||||
// Check if use only top directory
|
// Check if use only top directory
|
||||||
if (option == DirectorySearchOption::TopDirectoryOnly)
|
if (option == DirectorySearchOption::TopDirectoryOnly)
|
||||||
@@ -297,7 +297,7 @@ bool LinuxFileSystem::GetChildDirectories(Array<String>& results, const String&
|
|||||||
DIR* dir;
|
DIR* dir;
|
||||||
struct stat statPath, statEntry;
|
struct stat statPath, statEntry;
|
||||||
struct dirent* entry;
|
struct dirent* entry;
|
||||||
const StringAsANSI<> pathANSI(*directory, directory.Length());
|
const StringAsUTF8<> pathANSI(*directory, directory.Length());
|
||||||
const char* path = pathANSI.Get();
|
const char* path = pathANSI.Get();
|
||||||
|
|
||||||
// Stat for the path
|
// Stat for the path
|
||||||
@@ -353,7 +353,7 @@ bool LinuxFileSystem::GetChildDirectories(Array<String>& results, const String&
|
|||||||
bool LinuxFileSystem::FileExists(const StringView& path)
|
bool LinuxFileSystem::FileExists(const StringView& path)
|
||||||
{
|
{
|
||||||
struct stat fileInfo;
|
struct stat fileInfo;
|
||||||
const StringAsANSI<> pathANSI(*path, path.Length());
|
const StringAsUTF8<> pathANSI(*path, path.Length());
|
||||||
if (stat(pathANSI.Get(), &fileInfo) != -1)
|
if (stat(pathANSI.Get(), &fileInfo) != -1)
|
||||||
{
|
{
|
||||||
return S_ISREG(fileInfo.st_mode);
|
return S_ISREG(fileInfo.st_mode);
|
||||||
@@ -363,7 +363,7 @@ bool LinuxFileSystem::FileExists(const StringView& path)
|
|||||||
|
|
||||||
bool LinuxFileSystem::DeleteFile(const StringView& path)
|
bool LinuxFileSystem::DeleteFile(const StringView& path)
|
||||||
{
|
{
|
||||||
const StringAsANSI<> pathANSI(*path, path.Length());
|
const StringAsUTF8<> pathANSI(*path, path.Length());
|
||||||
return unlink(pathANSI.Get()) != 0;
|
return unlink(pathANSI.Get()) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -371,7 +371,7 @@ uint64 LinuxFileSystem::GetFileSize(const StringView& path)
|
|||||||
{
|
{
|
||||||
struct stat fileInfo;
|
struct stat fileInfo;
|
||||||
fileInfo.st_size = 0;
|
fileInfo.st_size = 0;
|
||||||
const StringAsANSI<> pathANSI(*path, path.Length());
|
const StringAsUTF8<> pathANSI(*path, path.Length());
|
||||||
if (stat(pathANSI.Get(), &fileInfo) != -1)
|
if (stat(pathANSI.Get(), &fileInfo) != -1)
|
||||||
{
|
{
|
||||||
// Check for directories
|
// Check for directories
|
||||||
@@ -385,7 +385,7 @@ uint64 LinuxFileSystem::GetFileSize(const StringView& path)
|
|||||||
|
|
||||||
bool LinuxFileSystem::IsReadOnly(const StringView& path)
|
bool LinuxFileSystem::IsReadOnly(const StringView& path)
|
||||||
{
|
{
|
||||||
const StringAsANSI<> pathANSI(*path, path.Length());
|
const StringAsUTF8<> pathANSI(*path, path.Length());
|
||||||
if (access(pathANSI.Get(), W_OK) == -1)
|
if (access(pathANSI.Get(), W_OK) == -1)
|
||||||
{
|
{
|
||||||
return errno == EACCES;
|
return errno == EACCES;
|
||||||
@@ -395,7 +395,7 @@ bool LinuxFileSystem::IsReadOnly(const StringView& path)
|
|||||||
|
|
||||||
bool LinuxFileSystem::SetReadOnly(const StringView& path, bool isReadOnly)
|
bool LinuxFileSystem::SetReadOnly(const StringView& path, bool isReadOnly)
|
||||||
{
|
{
|
||||||
const StringAsANSI<> pathANSI(*path, path.Length());
|
const StringAsUTF8<> pathANSI(*path, path.Length());
|
||||||
struct stat fileInfo;
|
struct stat fileInfo;
|
||||||
if (stat(pathANSI.Get(), &fileInfo) != -1)
|
if (stat(pathANSI.Get(), &fileInfo) != -1)
|
||||||
{
|
{
|
||||||
@@ -422,15 +422,15 @@ bool LinuxFileSystem::MoveFile(const StringView& dst, const StringView& src, boo
|
|||||||
|
|
||||||
if (overwrite)
|
if (overwrite)
|
||||||
{
|
{
|
||||||
unlink(StringAsANSI<>(*dst, dst.Length()).Get());
|
unlink(StringAsUTF8<>(*dst, dst.Length()).Get());
|
||||||
}
|
}
|
||||||
if (rename(StringAsANSI<>(*src, src.Length()).Get(), StringAsANSI<>(*dst, dst.Length()).Get()) != 0)
|
if (rename(StringAsUTF8<>(*src, src.Length()).Get(), StringAsUTF8<>(*dst, dst.Length()).Get()) != 0)
|
||||||
{
|
{
|
||||||
if (errno == EXDEV)
|
if (errno == EXDEV)
|
||||||
{
|
{
|
||||||
if (!CopyFile(dst, src))
|
if (!CopyFile(dst, src))
|
||||||
{
|
{
|
||||||
unlink(StringAsANSI<>(*src, src.Length()).Get());
|
unlink(StringAsUTF8<>(*src, src.Length()).Get());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -441,8 +441,8 @@ bool LinuxFileSystem::MoveFile(const StringView& dst, const StringView& src, boo
|
|||||||
|
|
||||||
bool LinuxFileSystem::CopyFile(const StringView& dst, const StringView& src)
|
bool LinuxFileSystem::CopyFile(const StringView& dst, const StringView& src)
|
||||||
{
|
{
|
||||||
const StringAsANSI<> srcANSI(*src, src.Length());
|
const StringAsUTF8<> srcANSI(*src, src.Length());
|
||||||
const StringAsANSI<> dstANSI(*dst, dst.Length());
|
const StringAsUTF8<> dstANSI(*dst, dst.Length());
|
||||||
|
|
||||||
int srcFile, dstFile;
|
int srcFile, dstFile;
|
||||||
char buffer[4096];
|
char buffer[4096];
|
||||||
@@ -752,7 +752,7 @@ bool LinuxFileSystem::getFilesFromDirectoryAll(Array<String>& results, const cha
|
|||||||
DateTime LinuxFileSystem::GetFileLastEditTime(const StringView& path)
|
DateTime LinuxFileSystem::GetFileLastEditTime(const StringView& path)
|
||||||
{
|
{
|
||||||
struct stat fileInfo;
|
struct stat fileInfo;
|
||||||
const StringAsANSI<> pathANSI(*path, path.Length());
|
const StringAsUTF8<> pathANSI(*path, path.Length());
|
||||||
if (stat(pathANSI.Get(), &fileInfo) == -1)
|
if (stat(pathANSI.Get(), &fileInfo) == -1)
|
||||||
{
|
{
|
||||||
return DateTime::MinValue();
|
return DateTime::MinValue();
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user