Merge remote-tracking branch 'origin/master' into 1.8
This commit is contained in:
292
Source/Editor/Content/AssetPickerValidator.cs
Normal file
292
Source/Editor/Content/AssetPickerValidator.cs
Normal file
@@ -0,0 +1,292 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using FlaxEditor.Scripting;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.Utilities;
|
||||
|
||||
namespace FlaxEditor.Content;
|
||||
|
||||
/// <summary>
|
||||
/// Manages and converts the selected content item to the appropriate types. Useful for drag operations.
|
||||
/// </summary>
|
||||
public class AssetPickerValidator : IContentItemOwner
|
||||
{
|
||||
private Asset _selected;
|
||||
private ContentItem _selectedItem;
|
||||
private ScriptType _type;
|
||||
private string _fileExtension;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the selected item.
|
||||
/// </summary>
|
||||
public ContentItem SelectedItem
|
||||
{
|
||||
get => _selectedItem;
|
||||
set
|
||||
{
|
||||
if (_selectedItem == value)
|
||||
return;
|
||||
if (value == null)
|
||||
{
|
||||
if (_selected == null && _selectedItem is SceneItem)
|
||||
{
|
||||
// Deselect scene reference
|
||||
_selectedItem.RemoveReference(this);
|
||||
_selectedItem = null;
|
||||
_selected = null;
|
||||
OnSelectedItemChanged();
|
||||
return;
|
||||
}
|
||||
|
||||
// Deselect
|
||||
_selectedItem?.RemoveReference(this);
|
||||
_selectedItem = null;
|
||||
_selected = null;
|
||||
OnSelectedItemChanged();
|
||||
}
|
||||
else if (value is SceneItem item)
|
||||
{
|
||||
if (_selectedItem == item)
|
||||
return;
|
||||
if (!IsValid(item))
|
||||
item = null;
|
||||
|
||||
// Change value to scene reference (cannot load asset because scene can be already loaded - duplicated ID issue)
|
||||
_selectedItem?.RemoveReference(this);
|
||||
_selectedItem = item;
|
||||
_selected = null;
|
||||
_selectedItem?.AddReference(this);
|
||||
OnSelectedItemChanged();
|
||||
}
|
||||
else if (value is AssetItem assetItem)
|
||||
{
|
||||
SelectedAsset = FlaxEngine.Content.LoadAsync(assetItem.ID);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Change value
|
||||
_selectedItem?.RemoveReference(this);
|
||||
_selectedItem = value;
|
||||
_selected = null;
|
||||
OnSelectedItemChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the selected asset identifier.
|
||||
/// </summary>
|
||||
public Guid SelectedID
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_selected != null)
|
||||
return _selected.ID;
|
||||
if (_selectedItem is AssetItem assetItem)
|
||||
return assetItem.ID;
|
||||
return Guid.Empty;
|
||||
}
|
||||
set => SelectedItem = Editor.Instance.ContentDatabase.FindAsset(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the selected content item path.
|
||||
/// </summary>
|
||||
public string SelectedPath
|
||||
{
|
||||
get
|
||||
{
|
||||
string path = _selectedItem?.Path ?? _selected?.Path;
|
||||
if (path != null)
|
||||
{
|
||||
// Convert into path relative to the project (cross-platform)
|
||||
var projectFolder = Globals.ProjectFolder;
|
||||
if (path.StartsWith(projectFolder))
|
||||
path = path.Substring(projectFolder.Length + 1);
|
||||
}
|
||||
return path;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (string.IsNullOrEmpty(value))
|
||||
{
|
||||
SelectedItem = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
var path = StringUtils.IsRelative(value) ? Path.Combine(Globals.ProjectFolder, value) : value;
|
||||
SelectedItem = Editor.Instance.ContentDatabase.Find(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the selected asset object.
|
||||
/// </summary>
|
||||
public Asset SelectedAsset
|
||||
{
|
||||
get => _selected;
|
||||
set
|
||||
{
|
||||
// Check if value won't change
|
||||
if (value == _selected)
|
||||
return;
|
||||
|
||||
// Find item from content database and check it
|
||||
var item = value ? Editor.Instance.ContentDatabase.FindAsset(value.ID) : null;
|
||||
if (item != null && !IsValid(item))
|
||||
item = null;
|
||||
|
||||
// Change value
|
||||
_selectedItem?.RemoveReference(this);
|
||||
_selectedItem = item;
|
||||
_selected = value;
|
||||
_selectedItem?.AddReference(this);
|
||||
OnSelectedItemChanged();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the assets types that this picker accepts (it supports types derived from the given type). Use <see cref="ScriptType.Null"/> for generic file picker.
|
||||
/// </summary>
|
||||
public ScriptType AssetType
|
||||
{
|
||||
get => _type;
|
||||
set
|
||||
{
|
||||
if (_type != value)
|
||||
{
|
||||
_type = value;
|
||||
|
||||
// Auto deselect if the current value is invalid
|
||||
if (_selectedItem != null && !IsValid(_selectedItem))
|
||||
SelectedItem = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the content items extensions filter. Null if unused.
|
||||
/// </summary>
|
||||
public string FileExtension
|
||||
{
|
||||
get => _fileExtension;
|
||||
set
|
||||
{
|
||||
if (_fileExtension != value)
|
||||
{
|
||||
_fileExtension = value;
|
||||
|
||||
// Auto deselect if the current value is invalid
|
||||
if (_selectedItem != null && !IsValid(_selectedItem))
|
||||
SelectedItem = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when selected item gets changed.
|
||||
/// </summary>
|
||||
public event Action SelectedItemChanged;
|
||||
|
||||
/// <summary>
|
||||
/// The custom callback for assets validation. Cane be used to implement a rule for assets to pick.
|
||||
/// </summary>
|
||||
public Func<ContentItem, bool> CheckValid;
|
||||
|
||||
/// <summary>
|
||||
/// Returns whether item is valid.
|
||||
/// </summary>
|
||||
/// <param name="item"></param>
|
||||
/// <returns></returns>
|
||||
public bool IsValid(ContentItem item)
|
||||
{
|
||||
if (_fileExtension != null && !item.Path.EndsWith(_fileExtension))
|
||||
return false;
|
||||
if (CheckValid != null && !CheckValid(item))
|
||||
return false;
|
||||
if (_type == ScriptType.Null)
|
||||
return true;
|
||||
|
||||
if (item is AssetItem assetItem)
|
||||
{
|
||||
// Faster path for binary items (in-built)
|
||||
if (assetItem is BinaryAssetItem binaryItem)
|
||||
return _type.IsAssignableFrom(new ScriptType(binaryItem.Type));
|
||||
|
||||
// Type filter
|
||||
var type = TypeUtils.GetType(assetItem.TypeName);
|
||||
if (_type.IsAssignableFrom(type))
|
||||
return true;
|
||||
|
||||
// Json assets can contain any type of the object defined by the C# type (data oriented design)
|
||||
if (assetItem is JsonAssetItem && (_type.Type == typeof(JsonAsset) || _type.Type == typeof(Asset)))
|
||||
return true;
|
||||
|
||||
// Special case for scene asset references
|
||||
if (_type.Type == typeof(SceneReference) && assetItem is SceneItem)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AssetPickerValidator"/> class.
|
||||
/// </summary>
|
||||
public AssetPickerValidator()
|
||||
: this(new ScriptType(typeof(Asset)))
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AssetPickerValidator"/> class.
|
||||
/// </summary>
|
||||
/// <param name="assetType">The assets types that this picker accepts.</param>
|
||||
public AssetPickerValidator(ScriptType assetType)
|
||||
{
|
||||
_type = assetType;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when selected item gets changed.
|
||||
/// </summary>
|
||||
protected virtual void OnSelectedItemChanged()
|
||||
{
|
||||
SelectedItemChanged?.Invoke();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void OnItemDeleted(ContentItem item)
|
||||
{
|
||||
// Deselect item
|
||||
SelectedItem = null;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void OnItemRenamed(ContentItem item)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void OnItemReimported(ContentItem item)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void OnItemDispose(ContentItem item)
|
||||
{
|
||||
// Deselect item
|
||||
SelectedItem = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Call to remove reference from the selected item.
|
||||
/// </summary>
|
||||
public void OnDestroy()
|
||||
{
|
||||
_selectedItem?.RemoveReference(this);
|
||||
_selectedItem = null;
|
||||
_selected = null;
|
||||
}
|
||||
}
|
||||
@@ -157,6 +157,12 @@ namespace FlaxEditor.CustomEditors
|
||||
var values = _values;
|
||||
var presenter = _presenter;
|
||||
var layout = _layout;
|
||||
if (layout.Editors.Count != 1)
|
||||
{
|
||||
// There are more editors using the same layout so rebuild parent editor to prevent removing others editors
|
||||
_parent?.RebuildLayout();
|
||||
return;
|
||||
}
|
||||
var control = layout.ContainerControl;
|
||||
var parent = _parent;
|
||||
var parentScrollV = (_presenter?.Panel.Parent as Panel)?.VScrollBar?.Value ?? -1;
|
||||
|
||||
@@ -34,7 +34,7 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
value = 0;
|
||||
|
||||
// If selected is single actor that has children, ask if apply layer to the sub objects as well
|
||||
if (Values.IsSingleObject && (int)Values[0] != value && ParentEditor.Values[0] is Actor actor && actor.HasChildren)
|
||||
if (Values.IsSingleObject && (int)Values[0] != value && ParentEditor.Values[0] is Actor actor && actor.HasChildren && !Editor.IsPlayMode)
|
||||
{
|
||||
var valueText = comboBox.SelectedItem;
|
||||
|
||||
|
||||
@@ -71,7 +71,7 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
{
|
||||
// Generic file picker
|
||||
assetType = ScriptType.Null;
|
||||
Picker.FileExtension = assetReference.TypeName;
|
||||
Picker.Validator.FileExtension = assetReference.TypeName;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -85,7 +85,7 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
}
|
||||
}
|
||||
|
||||
Picker.AssetType = assetType;
|
||||
Picker.Validator.AssetType = assetType;
|
||||
Picker.Height = height;
|
||||
Picker.SelectedItemChanged += OnSelectedItemChanged;
|
||||
}
|
||||
@@ -95,15 +95,15 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
if (_isRefreshing)
|
||||
return;
|
||||
if (typeof(AssetItem).IsAssignableFrom(_valueType.Type))
|
||||
SetValue(Picker.SelectedItem);
|
||||
SetValue(Picker.Validator.SelectedItem);
|
||||
else if (_valueType.Type == typeof(Guid))
|
||||
SetValue(Picker.SelectedID);
|
||||
SetValue(Picker.Validator.SelectedID);
|
||||
else if (_valueType.Type == typeof(SceneReference))
|
||||
SetValue(new SceneReference(Picker.SelectedID));
|
||||
SetValue(new SceneReference(Picker.Validator.SelectedID));
|
||||
else if (_valueType.Type == typeof(string))
|
||||
SetValue(Picker.SelectedPath);
|
||||
SetValue(Picker.Validator.SelectedPath);
|
||||
else
|
||||
SetValue(Picker.SelectedAsset);
|
||||
SetValue(Picker.Validator.SelectedAsset);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -115,15 +115,15 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
{
|
||||
_isRefreshing = true;
|
||||
if (Values[0] is AssetItem assetItem)
|
||||
Picker.SelectedItem = assetItem;
|
||||
Picker.Validator.SelectedItem = assetItem;
|
||||
else if (Values[0] is Guid guid)
|
||||
Picker.SelectedID = guid;
|
||||
Picker.Validator.SelectedID = guid;
|
||||
else if (Values[0] is SceneReference sceneAsset)
|
||||
Picker.SelectedItem = Editor.Instance.ContentDatabase.FindAsset(sceneAsset.ID);
|
||||
Picker.Validator.SelectedItem = Editor.Instance.ContentDatabase.FindAsset(sceneAsset.ID);
|
||||
else if (Values[0] is string path)
|
||||
Picker.SelectedPath = path;
|
||||
Picker.Validator.SelectedPath = path;
|
||||
else
|
||||
Picker.SelectedAsset = Values[0] as Asset;
|
||||
Picker.Validator.SelectedAsset = Values[0] as Asset;
|
||||
_isRefreshing = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,9 +3,12 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Linq;
|
||||
using FlaxEditor.Content;
|
||||
using FlaxEditor.CustomEditors.Elements;
|
||||
using FlaxEditor.CustomEditors.GUI;
|
||||
using FlaxEditor.GUI.ContextMenu;
|
||||
using FlaxEditor.GUI.Drag;
|
||||
using FlaxEditor.SceneGraph;
|
||||
using FlaxEditor.Scripting;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
@@ -135,14 +138,43 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
spacing = collection.Spacing;
|
||||
}
|
||||
|
||||
var dragArea = layout.CustomContainer<DragAreaControl>();
|
||||
dragArea.CustomControl.Editor = this;
|
||||
dragArea.CustomControl.ElementType = ElementType;
|
||||
|
||||
// Check for the AssetReferenceAttribute. In JSON assets, it can be used to filter
|
||||
// which scripts can be dragged over and dropped on this collection editor.
|
||||
var assetReference = (AssetReferenceAttribute)attributes?.FirstOrDefault(x => x is AssetReferenceAttribute);
|
||||
if (assetReference != null)
|
||||
{
|
||||
if (string.IsNullOrEmpty(assetReference.TypeName))
|
||||
{
|
||||
}
|
||||
else if (assetReference.TypeName.Length > 1 && assetReference.TypeName[0] == '.')
|
||||
{
|
||||
dragArea.CustomControl.ElementType = ScriptType.Null;
|
||||
dragArea.CustomControl.FileExtension = assetReference.TypeName;
|
||||
}
|
||||
else
|
||||
{
|
||||
var customType = TypeUtils.GetType(assetReference.TypeName);
|
||||
if (customType != ScriptType.Null)
|
||||
dragArea.CustomControl.ElementType = customType;
|
||||
else if (!Content.Settings.GameSettings.OptionalPlatformSettings.Contains(assetReference.TypeName))
|
||||
Debug.LogWarning(string.Format("Unknown asset type '{0}' to use for drag and drop filter.", assetReference.TypeName));
|
||||
else
|
||||
dragArea.CustomControl.ElementType = ScriptType.Void;
|
||||
}
|
||||
}
|
||||
|
||||
// Size
|
||||
if (_readOnly || (NotNullItems && size == 0))
|
||||
{
|
||||
layout.Label("Size", size.ToString());
|
||||
dragArea.Label("Size", size.ToString());
|
||||
}
|
||||
else
|
||||
{
|
||||
_size = layout.IntegerValue("Size");
|
||||
_size = dragArea.IntegerValue("Size");
|
||||
_size.IntValue.MinValue = 0;
|
||||
_size.IntValue.MaxValue = ushort.MaxValue;
|
||||
_size.IntValue.Value = size;
|
||||
@@ -152,7 +184,7 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
// Elements
|
||||
if (size > 0)
|
||||
{
|
||||
var panel = layout.VerticalPanel();
|
||||
var panel = dragArea.VerticalPanel();
|
||||
panel.Panel.BackgroundColor = _background;
|
||||
var elementType = ElementType;
|
||||
|
||||
@@ -212,37 +244,33 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
// Add/Remove buttons
|
||||
if (!_readOnly)
|
||||
{
|
||||
var area = layout.Space(20);
|
||||
var addButton = new Button(area.ContainerControl.Width - (16 + 16 + 2 + 2), 2, 16, 16)
|
||||
{
|
||||
Text = "+",
|
||||
TooltipText = "Add new item",
|
||||
AnchorPreset = AnchorPresets.TopRight,
|
||||
Parent = area.ContainerControl,
|
||||
Enabled = !NotNullItems || size > 0,
|
||||
};
|
||||
addButton.Clicked += () =>
|
||||
{
|
||||
if (IsSetBlocked)
|
||||
return;
|
||||
var panel = dragArea.HorizontalPanel();
|
||||
panel.Panel.Size = new Float2(0, 20);
|
||||
panel.Panel.Margin = new Margin(2);
|
||||
|
||||
Resize(Count + 1);
|
||||
};
|
||||
var removeButton = new Button(addButton.Right + 2, addButton.Y, 16, 16)
|
||||
{
|
||||
Text = "-",
|
||||
TooltipText = "Remove last item",
|
||||
AnchorPreset = AnchorPresets.TopRight,
|
||||
Parent = area.ContainerControl,
|
||||
Enabled = size > 0,
|
||||
};
|
||||
removeButton.Clicked += () =>
|
||||
var removeButton = panel.Button("-", "Remove last item");
|
||||
removeButton.Button.Size = new Float2(16, 16);
|
||||
removeButton.Button.Enabled = size > 0;
|
||||
removeButton.Button.AnchorPreset = AnchorPresets.TopRight;
|
||||
removeButton.Button.Clicked += () =>
|
||||
{
|
||||
if (IsSetBlocked)
|
||||
return;
|
||||
|
||||
Resize(Count - 1);
|
||||
};
|
||||
|
||||
var addButton = panel.Button("+", "Add new item");
|
||||
addButton.Button.Size = new Float2(16, 16);
|
||||
addButton.Button.Enabled = !NotNullItems || size > 0;
|
||||
addButton.Button.AnchorPreset = AnchorPresets.TopRight;
|
||||
addButton.Button.Clicked += () =>
|
||||
{
|
||||
if (IsSetBlocked)
|
||||
return;
|
||||
|
||||
Resize(Count + 1);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -369,5 +397,232 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
}
|
||||
return base.OnDirty(editor, value, token);
|
||||
}
|
||||
|
||||
private class DragAreaControl : VerticalPanel
|
||||
{
|
||||
private DragItems _dragItems;
|
||||
private DragActors _dragActors;
|
||||
private DragHandlers _dragHandlers;
|
||||
private AssetPickerValidator _pickerValidator;
|
||||
|
||||
public ScriptType ElementType
|
||||
{
|
||||
get => _pickerValidator?.AssetType ?? ScriptType.Null;
|
||||
set => _pickerValidator = new AssetPickerValidator(value);
|
||||
}
|
||||
|
||||
public CollectionEditor Editor { get; set; }
|
||||
|
||||
public string FileExtension
|
||||
{
|
||||
set => _pickerValidator.FileExtension = value;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Draw()
|
||||
{
|
||||
if (_dragHandlers is { HasValidDrag: true })
|
||||
{
|
||||
var area = new Rectangle(Float2.Zero, Size);
|
||||
Render2D.FillRectangle(area, Color.Orange * 0.5f);
|
||||
Render2D.DrawRectangle(area, Color.Black);
|
||||
}
|
||||
|
||||
base.Draw();
|
||||
}
|
||||
|
||||
public override void OnDestroy()
|
||||
{
|
||||
_pickerValidator.OnDestroy();
|
||||
}
|
||||
|
||||
private bool ValidateActors(ActorNode node)
|
||||
{
|
||||
return node.Actor.GetScript(ElementType.Type) || ElementType.Type.IsAssignableTo(typeof(Actor));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DragDropEffect OnDragEnter(ref Float2 location, DragData data)
|
||||
{
|
||||
var result = base.OnDragEnter(ref location, data);
|
||||
if (result != DragDropEffect.None)
|
||||
return result;
|
||||
|
||||
if (_dragHandlers == null)
|
||||
{
|
||||
_dragItems = new DragItems(_pickerValidator.IsValid);
|
||||
_dragActors = new DragActors(ValidateActors);
|
||||
_dragHandlers = new DragHandlers
|
||||
{
|
||||
_dragActors,
|
||||
_dragItems
|
||||
};
|
||||
}
|
||||
return _dragHandlers.OnDragEnter(data);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DragDropEffect OnDragMove(ref Float2 location, DragData data)
|
||||
{
|
||||
var result = base.OnDragMove(ref location, data);
|
||||
if (result != DragDropEffect.None)
|
||||
return result;
|
||||
|
||||
return _dragHandlers.Effect;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnDragLeave()
|
||||
{
|
||||
_dragHandlers.OnDragLeave();
|
||||
|
||||
base.OnDragLeave();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DragDropEffect OnDragDrop(ref Float2 location, DragData data)
|
||||
{
|
||||
var result = base.OnDragDrop(ref location, data);
|
||||
if (result != DragDropEffect.None)
|
||||
{
|
||||
_dragHandlers.OnDragDrop(null);
|
||||
return result;
|
||||
}
|
||||
|
||||
if (_dragHandlers.HasValidDrag)
|
||||
{
|
||||
if (_dragItems.HasValidDrag)
|
||||
{
|
||||
var list = Editor.CloneValues();
|
||||
if (list == null)
|
||||
{
|
||||
if (Editor.Values.Type.IsArray)
|
||||
{
|
||||
list = TypeUtils.CreateArrayInstance(Editor.Values.Type.GetElementType(), 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
list = Editor.Values.Type.CreateInstance() as IList;
|
||||
}
|
||||
}
|
||||
if (list.IsFixedSize)
|
||||
{
|
||||
var oldSize = list.Count;
|
||||
var newSize = list.Count + _dragItems.Objects.Count;
|
||||
var type = Editor.Values.Type.GetElementType();
|
||||
var array = TypeUtils.CreateArrayInstance(type, newSize);
|
||||
list.CopyTo(array, 0);
|
||||
|
||||
for (var i = oldSize; i < newSize; i++)
|
||||
{
|
||||
var validator = new AssetPickerValidator
|
||||
{
|
||||
FileExtension = _pickerValidator.FileExtension,
|
||||
AssetType = _pickerValidator.AssetType,
|
||||
SelectedItem = _dragItems.Objects[i - oldSize],
|
||||
};
|
||||
|
||||
if (typeof(AssetItem).IsAssignableFrom(ElementType.Type))
|
||||
array.SetValue(validator.SelectedItem, i);
|
||||
else if (ElementType.Type == typeof(Guid))
|
||||
array.SetValue(validator.SelectedID, i);
|
||||
else if (ElementType.Type == typeof(SceneReference))
|
||||
array.SetValue(new SceneReference(validator.SelectedID), i);
|
||||
else if (ElementType.Type == typeof(string))
|
||||
array.SetValue(validator.SelectedPath, i);
|
||||
else
|
||||
array.SetValue(validator.SelectedAsset, i);
|
||||
|
||||
validator.OnDestroy();
|
||||
}
|
||||
Editor.SetValue(array);
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var item in _dragItems.Objects)
|
||||
{
|
||||
var validator = new AssetPickerValidator
|
||||
{
|
||||
FileExtension = _pickerValidator.FileExtension,
|
||||
AssetType = _pickerValidator.AssetType,
|
||||
SelectedItem = item,
|
||||
};
|
||||
|
||||
if (typeof(AssetItem).IsAssignableFrom(ElementType.Type))
|
||||
list.Add(validator.SelectedItem);
|
||||
else if (ElementType.Type == typeof(Guid))
|
||||
list.Add(validator.SelectedID);
|
||||
else if (ElementType.Type == typeof(SceneReference))
|
||||
list.Add(new SceneReference(validator.SelectedID));
|
||||
else if (ElementType.Type == typeof(string))
|
||||
list.Add(validator.SelectedPath);
|
||||
else
|
||||
list.Add(validator.SelectedAsset);
|
||||
|
||||
validator.OnDestroy();
|
||||
}
|
||||
Editor.SetValue(list);
|
||||
}
|
||||
}
|
||||
else if (_dragActors.HasValidDrag)
|
||||
{
|
||||
var list = Editor.CloneValues();
|
||||
if (list == null)
|
||||
{
|
||||
if (Editor.Values.Type.IsArray)
|
||||
{
|
||||
list = TypeUtils.CreateArrayInstance(Editor.Values.Type.GetElementType(), 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
list = Editor.Values.Type.CreateInstance() as IList;
|
||||
}
|
||||
}
|
||||
|
||||
if (list.IsFixedSize)
|
||||
{
|
||||
var oldSize = list.Count;
|
||||
var newSize = list.Count + _dragActors.Objects.Count;
|
||||
var type = Editor.Values.Type.GetElementType();
|
||||
var array = TypeUtils.CreateArrayInstance(type, newSize);
|
||||
list.CopyTo(array, 0);
|
||||
|
||||
for (var i = oldSize; i < newSize; i++)
|
||||
{
|
||||
var actor = _dragActors.Objects[i - oldSize].Actor;
|
||||
if (ElementType.Type.IsAssignableTo(typeof(Actor)))
|
||||
{
|
||||
array.SetValue(actor, i);
|
||||
}
|
||||
else
|
||||
{
|
||||
array.SetValue(actor.GetScript(ElementType.Type), i);
|
||||
}
|
||||
}
|
||||
Editor.SetValue(array);
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var actorNode in _dragActors.Objects)
|
||||
{
|
||||
if (ElementType.Type.IsAssignableTo(typeof(Actor)))
|
||||
{
|
||||
list.Add(actorNode.Actor);
|
||||
}
|
||||
else
|
||||
{
|
||||
list.Add(actorNode.Actor.GetScript(ElementType.Type));
|
||||
}
|
||||
}
|
||||
Editor.SetValue(list);
|
||||
}
|
||||
}
|
||||
|
||||
_dragHandlers.OnDragDrop(null);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,14 +72,14 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
return;
|
||||
_isRefreshing = true;
|
||||
var slots = _modelInstance.MaterialSlots;
|
||||
var material = _materialEditor.Picker.SelectedAsset as MaterialBase;
|
||||
var material = _materialEditor.Picker.Validator.SelectedAsset as MaterialBase;
|
||||
var defaultMaterial = GPUDevice.Instance.DefaultMaterial;
|
||||
var value = (ModelInstanceEntry)Values[0];
|
||||
var prevMaterial = value.Material;
|
||||
if (!material)
|
||||
{
|
||||
// Fallback to default material
|
||||
_materialEditor.Picker.SelectedAsset = defaultMaterial;
|
||||
_materialEditor.Picker.Validator.SelectedAsset = defaultMaterial;
|
||||
value.Material = defaultMaterial;
|
||||
}
|
||||
else if (material == slots[_entryIndex].Material)
|
||||
|
||||
@@ -5,6 +5,7 @@ using System.IO;
|
||||
using FlaxEditor.Content;
|
||||
using FlaxEditor.GUI.Drag;
|
||||
using FlaxEditor.Scripting;
|
||||
using FlaxEditor.Utilities;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
using FlaxEngine.Utilities;
|
||||
@@ -17,189 +18,21 @@ namespace FlaxEditor.GUI
|
||||
/// <seealso cref="Control" />
|
||||
/// <seealso cref="IContentItemOwner" />
|
||||
[HideInEditor]
|
||||
public class AssetPicker : Control, IContentItemOwner
|
||||
public class AssetPicker : Control
|
||||
{
|
||||
private const float DefaultIconSize = 64;
|
||||
private const float ButtonsOffset = 2;
|
||||
private const float ButtonsSize = 12;
|
||||
|
||||
private Asset _selected;
|
||||
private ContentItem _selectedItem;
|
||||
private ScriptType _type;
|
||||
private string _fileExtension;
|
||||
|
||||
private bool _isMouseDown;
|
||||
private Float2 _mouseDownPos;
|
||||
private Float2 _mousePos;
|
||||
private DragItems _dragOverElement;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the selected item.
|
||||
/// The asset validator. Used to ensure only appropriate items can be picked.
|
||||
/// </summary>
|
||||
public ContentItem SelectedItem
|
||||
{
|
||||
get => _selectedItem;
|
||||
set
|
||||
{
|
||||
if (_selectedItem == value)
|
||||
return;
|
||||
if (value == null)
|
||||
{
|
||||
if (_selected == null && _selectedItem is SceneItem)
|
||||
{
|
||||
// Deselect scene reference
|
||||
_selectedItem.RemoveReference(this);
|
||||
_selectedItem = null;
|
||||
_selected = null;
|
||||
OnSelectedItemChanged();
|
||||
return;
|
||||
}
|
||||
|
||||
// Deselect
|
||||
_selectedItem?.RemoveReference(this);
|
||||
_selectedItem = null;
|
||||
_selected = null;
|
||||
OnSelectedItemChanged();
|
||||
}
|
||||
else if (value is SceneItem item)
|
||||
{
|
||||
if (_selectedItem == item)
|
||||
return;
|
||||
if (!IsValid(item))
|
||||
item = null;
|
||||
|
||||
// Change value to scene reference (cannot load asset because scene can be already loaded - duplicated ID issue)
|
||||
_selectedItem?.RemoveReference(this);
|
||||
_selectedItem = item;
|
||||
_selected = null;
|
||||
_selectedItem?.AddReference(this);
|
||||
OnSelectedItemChanged();
|
||||
}
|
||||
else if (value is AssetItem assetItem)
|
||||
{
|
||||
SelectedAsset = FlaxEngine.Content.LoadAsync(assetItem.ID);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Change value
|
||||
_selectedItem?.RemoveReference(this);
|
||||
_selectedItem = value;
|
||||
_selected = null;
|
||||
OnSelectedItemChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the selected asset identifier.
|
||||
/// </summary>
|
||||
public Guid SelectedID
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_selected != null)
|
||||
return _selected.ID;
|
||||
if (_selectedItem is AssetItem assetItem)
|
||||
return assetItem.ID;
|
||||
return Guid.Empty;
|
||||
}
|
||||
set => SelectedItem = Editor.Instance.ContentDatabase.FindAsset(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the selected content item path.
|
||||
/// </summary>
|
||||
public string SelectedPath
|
||||
{
|
||||
get
|
||||
{
|
||||
string path = _selectedItem?.Path ?? _selected?.Path;
|
||||
if (path != null)
|
||||
{
|
||||
// Convert into path relative to the project (cross-platform)
|
||||
var projectFolder = Globals.ProjectFolder;
|
||||
if (path.StartsWith(projectFolder))
|
||||
path = path.Substring(projectFolder.Length + 1);
|
||||
}
|
||||
return path;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (string.IsNullOrEmpty(value))
|
||||
{
|
||||
SelectedItem = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
var path = StringUtils.IsRelative(value) ? Path.Combine(Globals.ProjectFolder, value) : value;
|
||||
SelectedItem = Editor.Instance.ContentDatabase.Find(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the selected asset object.
|
||||
/// </summary>
|
||||
public Asset SelectedAsset
|
||||
{
|
||||
get => _selected;
|
||||
set
|
||||
{
|
||||
// Check if value won't change
|
||||
if (value == _selected)
|
||||
return;
|
||||
|
||||
// Find item from content database and check it
|
||||
var item = value ? Editor.Instance.ContentDatabase.FindAsset(value.ID) : null;
|
||||
if (item != null && !IsValid(item))
|
||||
item = null;
|
||||
|
||||
// Change value
|
||||
_selectedItem?.RemoveReference(this);
|
||||
_selectedItem = item;
|
||||
_selected = value;
|
||||
_selectedItem?.AddReference(this);
|
||||
OnSelectedItemChanged();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the assets types that this picker accepts (it supports types derived from the given type). Use <see cref="ScriptType.Null"/> for generic file picker.
|
||||
/// </summary>
|
||||
public ScriptType AssetType
|
||||
{
|
||||
get => _type;
|
||||
set
|
||||
{
|
||||
if (_type != value)
|
||||
{
|
||||
_type = value;
|
||||
|
||||
// Auto deselect if the current value is invalid
|
||||
if (_selectedItem != null && !IsValid(_selectedItem))
|
||||
SelectedItem = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the content items extensions filter. Null if unused.
|
||||
/// </summary>
|
||||
public string FileExtension
|
||||
{
|
||||
get => _fileExtension;
|
||||
set
|
||||
{
|
||||
if (_fileExtension != value)
|
||||
{
|
||||
_fileExtension = value;
|
||||
|
||||
// Auto deselect if the current value is invalid
|
||||
if (_selectedItem != null && !IsValid(_selectedItem))
|
||||
SelectedItem = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
public AssetPickerValidator Validator { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when selected item gets changed.
|
||||
@@ -216,38 +49,6 @@ namespace FlaxEditor.GUI
|
||||
/// </summary>
|
||||
public bool CanEdit = true;
|
||||
|
||||
private bool IsValid(ContentItem item)
|
||||
{
|
||||
if (_fileExtension != null && !item.Path.EndsWith(_fileExtension))
|
||||
return false;
|
||||
if (CheckValid != null && !CheckValid(item))
|
||||
return false;
|
||||
if (_type == ScriptType.Null)
|
||||
return true;
|
||||
|
||||
if (item is AssetItem assetItem)
|
||||
{
|
||||
// Faster path for binary items (in-built)
|
||||
if (assetItem is BinaryAssetItem binaryItem)
|
||||
return _type.IsAssignableFrom(new ScriptType(binaryItem.Type));
|
||||
|
||||
// Type filter
|
||||
var type = TypeUtils.GetType(assetItem.TypeName);
|
||||
if (_type.IsAssignableFrom(type))
|
||||
return true;
|
||||
|
||||
// Json assets can contain any type of the object defined by the C# type (data oriented design)
|
||||
if (assetItem is JsonAssetItem && (_type.Type == typeof(JsonAsset) || _type.Type == typeof(Asset)))
|
||||
return true;
|
||||
|
||||
// Special case for scene asset references
|
||||
if (_type.Type == typeof(SceneReference) && assetItem is SceneItem)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AssetPicker"/> class.
|
||||
/// </summary>
|
||||
@@ -264,7 +65,8 @@ namespace FlaxEditor.GUI
|
||||
public AssetPicker(ScriptType assetType, Float2 location)
|
||||
: base(location, new Float2(DefaultIconSize + ButtonsOffset + ButtonsSize, DefaultIconSize))
|
||||
{
|
||||
_type = assetType;
|
||||
Validator = new AssetPickerValidator(assetType);
|
||||
Validator.SelectedItemChanged += OnSelectedItemChanged;
|
||||
_mousePos = Float2.Minimum;
|
||||
}
|
||||
|
||||
@@ -275,10 +77,10 @@ namespace FlaxEditor.GUI
|
||||
{
|
||||
// Update tooltip
|
||||
string tooltip;
|
||||
if (_selectedItem is AssetItem assetItem)
|
||||
if (Validator.SelectedItem is AssetItem assetItem)
|
||||
tooltip = assetItem.NamePath;
|
||||
else
|
||||
tooltip = SelectedPath;
|
||||
tooltip = Validator.SelectedPath;
|
||||
TooltipText = tooltip;
|
||||
|
||||
SelectedItemChanged?.Invoke();
|
||||
@@ -289,37 +91,13 @@ namespace FlaxEditor.GUI
|
||||
// Do the drag drop operation if has selected element
|
||||
if (new Rectangle(Float2.Zero, Size).Contains(ref _mouseDownPos))
|
||||
{
|
||||
if (_selected != null)
|
||||
DoDragDrop(DragAssets.GetDragData(_selected));
|
||||
else if (_selectedItem != null)
|
||||
DoDragDrop(DragItems.GetDragData(_selectedItem));
|
||||
if (Validator.SelectedAsset != null)
|
||||
DoDragDrop(DragAssets.GetDragData(Validator.SelectedAsset));
|
||||
else if (Validator.SelectedItem != null)
|
||||
DoDragDrop(DragItems.GetDragData(Validator.SelectedItem));
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void OnItemDeleted(ContentItem item)
|
||||
{
|
||||
// Deselect item
|
||||
SelectedItem = null;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void OnItemRenamed(ContentItem item)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void OnItemReimported(ContentItem item)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void OnItemDispose(ContentItem item)
|
||||
{
|
||||
// Deselect item
|
||||
SelectedItem = null;
|
||||
}
|
||||
|
||||
private Rectangle IconRect => new Rectangle(0, 0, Height, Height);
|
||||
|
||||
private Rectangle Button1Rect => new Rectangle(Height + ButtonsOffset, 0, ButtonsSize, ButtonsSize);
|
||||
@@ -341,10 +119,10 @@ namespace FlaxEditor.GUI
|
||||
if (CanEdit)
|
||||
Render2D.DrawSprite(style.ArrowDown, button1Rect, button1Rect.Contains(_mousePos) ? style.Foreground : style.ForegroundGrey);
|
||||
|
||||
if (_selectedItem != null)
|
||||
if (Validator.SelectedItem != null)
|
||||
{
|
||||
// Draw item preview
|
||||
_selectedItem.DrawThumbnail(ref iconRect);
|
||||
Validator.SelectedItem.DrawThumbnail(ref iconRect);
|
||||
|
||||
// Draw buttons
|
||||
if (CanEdit)
|
||||
@@ -363,7 +141,7 @@ namespace FlaxEditor.GUI
|
||||
{
|
||||
Render2D.DrawText(
|
||||
style.FontSmall,
|
||||
_selectedItem.ShortName,
|
||||
Validator.SelectedItem.ShortName,
|
||||
new Rectangle(button1Rect.Right + 2, 0, sizeForTextLeft, ButtonsSize),
|
||||
style.Foreground,
|
||||
TextAlignment.Near,
|
||||
@@ -371,7 +149,7 @@ namespace FlaxEditor.GUI
|
||||
}
|
||||
}
|
||||
// Check if has no item but has an asset (eg. virtual asset)
|
||||
else if (_selected)
|
||||
else if (Validator.SelectedAsset)
|
||||
{
|
||||
// Draw remove button
|
||||
Render2D.DrawSprite(style.Cross, button3Rect, button3Rect.Contains(_mousePos) ? style.Foreground : style.ForegroundGrey);
|
||||
@@ -380,8 +158,8 @@ namespace FlaxEditor.GUI
|
||||
float sizeForTextLeft = Width - button1Rect.Right;
|
||||
if (sizeForTextLeft > 30)
|
||||
{
|
||||
var name = _selected.GetType().Name;
|
||||
if (_selected.IsVirtual)
|
||||
var name = Validator.SelectedAsset.GetType().Name;
|
||||
if (Validator.SelectedAsset.IsVirtual)
|
||||
name += " (virtual)";
|
||||
Render2D.DrawText(
|
||||
style.FontSmall,
|
||||
@@ -407,9 +185,7 @@ namespace FlaxEditor.GUI
|
||||
/// <inheritdoc />
|
||||
public override void OnDestroy()
|
||||
{
|
||||
_selectedItem?.RemoveReference(this);
|
||||
_selectedItem = null;
|
||||
_selected = null;
|
||||
Validator.OnDestroy();
|
||||
|
||||
base.OnDestroy();
|
||||
}
|
||||
@@ -463,57 +239,57 @@ namespace FlaxEditor.GUI
|
||||
// Buttons logic
|
||||
if (!CanEdit)
|
||||
{
|
||||
if (Button1Rect.Contains(location) && _selectedItem != null)
|
||||
if (Button1Rect.Contains(location) && Validator.SelectedItem != null)
|
||||
{
|
||||
// Select asset
|
||||
Editor.Instance.Windows.ContentWin.Select(_selectedItem);
|
||||
Editor.Instance.Windows.ContentWin.Select(Validator.SelectedItem);
|
||||
}
|
||||
}
|
||||
else if (Button1Rect.Contains(location))
|
||||
{
|
||||
Focus();
|
||||
if (_type != ScriptType.Null)
|
||||
if (Validator.AssetType != ScriptType.Null)
|
||||
{
|
||||
// Show asset picker popup
|
||||
var popup = AssetSearchPopup.Show(this, Button1Rect.BottomLeft, IsValid, item =>
|
||||
var popup = AssetSearchPopup.Show(this, Button1Rect.BottomLeft, Validator.IsValid, item =>
|
||||
{
|
||||
SelectedItem = item;
|
||||
Validator.SelectedItem = item;
|
||||
RootWindow.Focus();
|
||||
Focus();
|
||||
});
|
||||
if (_selected != null)
|
||||
if (Validator.SelectedAsset != null)
|
||||
{
|
||||
var selectedAssetName = Path.GetFileNameWithoutExtension(_selected.Path);
|
||||
var selectedAssetName = Path.GetFileNameWithoutExtension(Validator.SelectedAsset.Path);
|
||||
popup.ScrollToAndHighlightItemByName(selectedAssetName);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Show content item picker popup
|
||||
var popup = ContentSearchPopup.Show(this, Button1Rect.BottomLeft, IsValid, item =>
|
||||
var popup = ContentSearchPopup.Show(this, Button1Rect.BottomLeft, Validator.IsValid, item =>
|
||||
{
|
||||
SelectedItem = item;
|
||||
Validator.SelectedItem = item;
|
||||
RootWindow.Focus();
|
||||
Focus();
|
||||
});
|
||||
if (_selectedItem != null)
|
||||
if (Validator.SelectedItem != null)
|
||||
{
|
||||
popup.ScrollToAndHighlightItemByName(_selectedItem.ShortName);
|
||||
popup.ScrollToAndHighlightItemByName(Validator.SelectedItem.ShortName);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (_selected != null || _selectedItem != null)
|
||||
else if (Validator.SelectedAsset != null || Validator.SelectedItem != null)
|
||||
{
|
||||
if (Button2Rect.Contains(location) && _selectedItem != null)
|
||||
if (Button2Rect.Contains(location) && Validator.SelectedItem != null)
|
||||
{
|
||||
// Select asset
|
||||
Editor.Instance.Windows.ContentWin.Select(_selectedItem);
|
||||
Editor.Instance.Windows.ContentWin.Select(Validator.SelectedItem);
|
||||
}
|
||||
else if (Button3Rect.Contains(location))
|
||||
{
|
||||
// Deselect asset
|
||||
Focus();
|
||||
SelectedItem = null;
|
||||
Validator.SelectedItem = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -540,10 +316,10 @@ namespace FlaxEditor.GUI
|
||||
{
|
||||
Focus();
|
||||
|
||||
if (_selectedItem != null && IconRect.Contains(location))
|
||||
if (Validator.SelectedItem != null && IconRect.Contains(location))
|
||||
{
|
||||
// Open it
|
||||
Editor.Instance.ContentEditing.Open(_selectedItem);
|
||||
Editor.Instance.ContentEditing.Open(Validator.SelectedItem);
|
||||
}
|
||||
|
||||
// Handled
|
||||
@@ -557,7 +333,7 @@ namespace FlaxEditor.GUI
|
||||
|
||||
// Check if drop asset
|
||||
if (_dragOverElement == null)
|
||||
_dragOverElement = new DragItems(IsValid);
|
||||
_dragOverElement = new DragItems(Validator.IsValid);
|
||||
if (CanEdit && _dragOverElement.OnDragEnter(data))
|
||||
{
|
||||
}
|
||||
@@ -590,7 +366,7 @@ namespace FlaxEditor.GUI
|
||||
if (CanEdit && _dragOverElement.HasValidDrag)
|
||||
{
|
||||
// Select element
|
||||
SelectedItem = _dragOverElement.Objects[0];
|
||||
Validator.SelectedItem = _dragOverElement.Objects[0];
|
||||
}
|
||||
|
||||
// Clear cache
|
||||
|
||||
@@ -39,6 +39,11 @@ namespace FlaxEditor.GUI.Dialogs
|
||||
/// </summary>
|
||||
public DialogResult Result => _result;
|
||||
|
||||
/// <summary>
|
||||
/// Returns the size of the dialog.
|
||||
/// </summary>
|
||||
public Float2 DialogSize => _dialogSize;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Dialog"/> class.
|
||||
/// </summary>
|
||||
|
||||
@@ -465,36 +465,47 @@ namespace FlaxEditor.GUI.Docking
|
||||
{
|
||||
if (Parent.Parent is SplitPanel splitter)
|
||||
{
|
||||
// Check if has any child panels
|
||||
var childPanel = new List<DockPanel>(_childPanels);
|
||||
for (int i = 0; i < childPanel.Count; i++)
|
||||
// Check if there is another nested dock panel inside this dock panel and extract it here
|
||||
var childPanels = _childPanels.ToArray();
|
||||
if (childPanels.Length != 0)
|
||||
{
|
||||
// Undock all tabs
|
||||
var panel = childPanel[i];
|
||||
int count = panel.TabsCount;
|
||||
while (count-- > 0)
|
||||
// Move tabs from child panels into this one
|
||||
DockWindow selectedTab = null;
|
||||
foreach (var childPanel in childPanels)
|
||||
{
|
||||
panel.GetTab(0).Close();
|
||||
var childPanelTabs = childPanel.Tabs.ToArray();
|
||||
for (var i = 0; i < childPanelTabs.Length; i++)
|
||||
{
|
||||
var childPanelTab = childPanelTabs[i];
|
||||
if (selectedTab == null && childPanelTab.IsSelected)
|
||||
selectedTab = childPanelTab;
|
||||
childPanel.UndockWindow(childPanelTab);
|
||||
AddTab(childPanelTab, false);
|
||||
}
|
||||
}
|
||||
if (selectedTab != null)
|
||||
SelectTab(selectedTab);
|
||||
}
|
||||
|
||||
// Unlink splitter
|
||||
var splitterParent = splitter.Parent;
|
||||
Assert.IsNotNull(splitterParent);
|
||||
splitter.Parent = null;
|
||||
|
||||
// Move controls from second split panel to the split panel parent
|
||||
var scrPanel = Parent == splitter.Panel2 ? splitter.Panel1 : splitter.Panel2;
|
||||
var srcPanelChildrenCount = scrPanel.ChildrenCount;
|
||||
for (int i = srcPanelChildrenCount - 1; i >= 0 && scrPanel.ChildrenCount > 0; i--)
|
||||
else
|
||||
{
|
||||
scrPanel.GetChild(i).Parent = splitterParent;
|
||||
}
|
||||
Assert.IsTrue(scrPanel.ChildrenCount == 0);
|
||||
Assert.IsTrue(splitterParent.ChildrenCount == srcPanelChildrenCount);
|
||||
// Unlink splitter
|
||||
var splitterParent = splitter.Parent;
|
||||
Assert.IsNotNull(splitterParent);
|
||||
splitter.Parent = null;
|
||||
|
||||
// Delete
|
||||
splitter.Dispose();
|
||||
// Move controls from second split panel to the split panel parent
|
||||
var scrPanel = Parent == splitter.Panel2 ? splitter.Panel1 : splitter.Panel2;
|
||||
var srcPanelChildrenCount = scrPanel.ChildrenCount;
|
||||
for (int i = srcPanelChildrenCount - 1; i >= 0 && scrPanel.ChildrenCount > 0; i--)
|
||||
{
|
||||
scrPanel.GetChild(i).Parent = splitterParent;
|
||||
}
|
||||
Assert.IsTrue(scrPanel.ChildrenCount == 0);
|
||||
Assert.IsTrue(splitterParent.ChildrenCount == srcPanelChildrenCount);
|
||||
|
||||
// Delete
|
||||
splitter.Dispose();
|
||||
}
|
||||
}
|
||||
else if (!IsMaster)
|
||||
{
|
||||
@@ -582,19 +593,17 @@ namespace FlaxEditor.GUI.Docking
|
||||
/// Adds the tab.
|
||||
/// </summary>
|
||||
/// <param name="window">The window to insert as a tab.</param>
|
||||
protected virtual void AddTab(DockWindow window)
|
||||
/// <param name="autoSelect">True if auto-select newly added tab.</param>
|
||||
protected virtual void AddTab(DockWindow window, bool autoSelect = true)
|
||||
{
|
||||
// Dock
|
||||
_tabs.Add(window);
|
||||
window.ParentDockPanel = this;
|
||||
|
||||
// Select tab
|
||||
SelectTab(window);
|
||||
if (autoSelect)
|
||||
SelectTab(window);
|
||||
}
|
||||
|
||||
private void CreateTabsProxy()
|
||||
{
|
||||
// Check if has no tabs proxy created
|
||||
if (_tabsProxy == null)
|
||||
{
|
||||
// Create proxy and make set simple full dock
|
||||
|
||||
@@ -72,7 +72,7 @@ namespace FlaxEditor.GUI.Docking
|
||||
settings.Size = size;
|
||||
settings.Position = location;
|
||||
settings.MinimumSize = new Float2(1);
|
||||
settings.MaximumSize = new Float2(4096);
|
||||
settings.MaximumSize = Float2.Zero; // Unlimited size
|
||||
settings.Fullscreen = false;
|
||||
settings.HasBorder = true;
|
||||
settings.SupportsTransparency = false;
|
||||
|
||||
@@ -13,6 +13,8 @@ namespace FlaxEditor.GUI.Tabs
|
||||
[HideInEditor]
|
||||
public class Tab : ContainerControl
|
||||
{
|
||||
internal Tabs _selectedInTabs;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the text.
|
||||
/// </summary>
|
||||
@@ -86,5 +88,25 @@ namespace FlaxEditor.GUI.Tabs
|
||||
{
|
||||
return new Tabs.TabHeader((Tabs)Parent, this);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnParentChangedInternal()
|
||||
{
|
||||
if (_selectedInTabs != null)
|
||||
_selectedInTabs.SelectedTab = null;
|
||||
|
||||
base.OnParentChangedInternal();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnDestroy()
|
||||
{
|
||||
if (IsDisposing)
|
||||
return;
|
||||
if (_selectedInTabs != null)
|
||||
_selectedInTabs.SelectedTab = null;
|
||||
|
||||
base.OnDestroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -263,7 +263,12 @@ namespace FlaxEditor.GUI.Tabs
|
||||
// Check if index will change
|
||||
if (_selectedIndex != index)
|
||||
{
|
||||
SelectedTab?.OnDeselected();
|
||||
var prev = SelectedTab;
|
||||
if (prev != null)
|
||||
{
|
||||
prev._selectedInTabs = null;
|
||||
prev.OnDeselected();
|
||||
}
|
||||
_selectedIndex = index;
|
||||
PerformLayout();
|
||||
OnSelectedTabChanged();
|
||||
@@ -342,8 +347,13 @@ namespace FlaxEditor.GUI.Tabs
|
||||
/// </summary>
|
||||
protected virtual void OnSelectedTabChanged()
|
||||
{
|
||||
var selectedTab = SelectedTab;
|
||||
SelectedTabChanged?.Invoke(this);
|
||||
SelectedTab?.OnSelected();
|
||||
if (selectedTab != null)
|
||||
{
|
||||
selectedTab._selectedInTabs = this;
|
||||
selectedTab.OnSelected();
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -39,7 +39,7 @@ namespace FlaxEditor.GUI.Timeline.Tracks
|
||||
if (AssetID == value?.ID)
|
||||
return;
|
||||
AssetID = value?.ID ?? Guid.Empty;
|
||||
_picker.SelectedAsset = value;
|
||||
_picker.Validator.SelectedAsset = value;
|
||||
OnAssetChanged();
|
||||
Timeline?.MarkAsEdited();
|
||||
}
|
||||
@@ -63,10 +63,10 @@ namespace FlaxEditor.GUI.Timeline.Tracks
|
||||
|
||||
private void OnPickerSelectedItemChanged()
|
||||
{
|
||||
if (Asset == (TAsset)_picker.SelectedAsset)
|
||||
if (Asset == (TAsset)_picker.Validator.SelectedAsset)
|
||||
return;
|
||||
using (new TrackUndoBlock(this))
|
||||
Asset = (TAsset)_picker.SelectedAsset;
|
||||
Asset = (TAsset)_picker.Validator.SelectedAsset;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -266,6 +266,19 @@ namespace FlaxEditor.Modules
|
||||
Editor.StateMachine.ChangingScenesState.LoadScene(sceneId, additive);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reload all loaded scenes.
|
||||
/// </summary>
|
||||
public void ReloadScenes()
|
||||
{
|
||||
foreach (var scene in Level.Scenes)
|
||||
{
|
||||
var sceneId = scene.ID;
|
||||
if (!Level.UnloadScene(scene))
|
||||
Level.LoadScene(sceneId);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Closes scene (async).
|
||||
/// </summary>
|
||||
|
||||
@@ -147,6 +147,17 @@ namespace FlaxEditor.Modules.SourceCodeEditing
|
||||
}
|
||||
if (key != null)
|
||||
xml.TryGetValue(key, out text);
|
||||
|
||||
// Customize tooltips for properties to be more human-readable in UI
|
||||
if (text != null && memberType.HasFlag(MemberTypes.Property) && text.StartsWith("Gets or sets ", StringComparison.Ordinal))
|
||||
{
|
||||
text = text.Substring(13);
|
||||
unsafe
|
||||
{
|
||||
fixed (char* e = text)
|
||||
e[0] = char.ToUpper(e[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -39,6 +39,7 @@ namespace FlaxEditor.Modules
|
||||
ContextMenuSingleSelectGroup<int> _numberOfClientsGroup = new ContextMenuSingleSelectGroup<int>();
|
||||
|
||||
private ContextMenuButton _menuFileSaveScenes;
|
||||
private ContextMenuButton _menuFileReloadScenes;
|
||||
private ContextMenuButton _menuFileCloseScenes;
|
||||
private ContextMenuButton _menuFileOpenScriptsProject;
|
||||
private ContextMenuButton _menuFileGenerateScriptsProjectFiles;
|
||||
@@ -470,13 +471,13 @@ namespace FlaxEditor.Modules
|
||||
// Place dialog nearby the target control
|
||||
var targetControlDesktopCenter = targetControl.PointToScreen(targetControl.Size * 0.5f);
|
||||
var desktopSize = Platform.GetMonitorBounds(targetControlDesktopCenter);
|
||||
var pos = targetControlDesktopCenter + new Float2(10.0f, -dialog.Height * 0.5f);
|
||||
var dialogEnd = pos + dialog.Size;
|
||||
var pos = targetControlDesktopCenter + new Float2(10.0f, -dialog.DialogSize.Y * 0.5f);
|
||||
var dialogEnd = pos + dialog.DialogSize;
|
||||
var desktopEnd = desktopSize.BottomRight - new Float2(10.0f);
|
||||
if (dialogEnd.X >= desktopEnd.X || dialogEnd.Y >= desktopEnd.Y)
|
||||
pos = targetControl.PointToScreen(Float2.Zero) - new Float2(10.0f + dialog.Width, dialog.Height);
|
||||
pos = targetControl.PointToScreen(Float2.Zero) - new Float2(10.0f + dialog.DialogSize.X, dialog.DialogSize.Y);
|
||||
var desktopBounds = Platform.VirtualDesktopBounds;
|
||||
pos = Float2.Clamp(pos, desktopBounds.UpperLeft, desktopBounds.BottomRight - dialog.Size);
|
||||
pos = Float2.Clamp(pos, desktopBounds.UpperLeft, desktopBounds.BottomRight - dialog.DialogSize);
|
||||
dialog.RootWindow.Window.Position = pos;
|
||||
|
||||
// Register for context menu (prevent auto-closing context menu when selecting color)
|
||||
@@ -527,6 +528,7 @@ namespace FlaxEditor.Modules
|
||||
_menuFileSaveAll = cm.AddButton("Save All", inputOptions.Save, Editor.SaveAll);
|
||||
_menuFileSaveScenes = cm.AddButton("Save scenes", inputOptions.SaveScenes, Editor.Scene.SaveScenes);
|
||||
_menuFileCloseScenes = cm.AddButton("Close scenes", inputOptions.CloseScenes, Editor.Scene.CloseAllScenes);
|
||||
_menuFileReloadScenes = cm.AddButton("Reload scenes", Editor.Scene.ReloadScenes);
|
||||
cm.AddSeparator();
|
||||
_menuFileOpenScriptsProject = cm.AddButton("Open scripts project", inputOptions.OpenScriptsProject, Editor.CodeEditing.OpenSolution);
|
||||
_menuFileGenerateScriptsProjectFiles = cm.AddButton("Generate scripts project files", inputOptions.GenerateScriptsProject, Editor.ProgressReporting.GenerateScriptsProjectFiles.RunAsync);
|
||||
@@ -830,6 +832,7 @@ namespace FlaxEditor.Modules
|
||||
|
||||
_menuFileSaveScenes.Enabled = hasOpenedScene;
|
||||
_menuFileCloseScenes.Enabled = hasOpenedScene;
|
||||
_menuFileReloadScenes.Enabled = hasOpenedScene;
|
||||
_menuFileGenerateScriptsProjectFiles.Enabled = !Editor.ProgressReporting.GenerateScriptsProjectFiles.IsActive;
|
||||
|
||||
c.PerformLayout();
|
||||
|
||||
@@ -171,9 +171,13 @@ namespace FlaxEditor.Modules
|
||||
var mainWindow = MainWindow;
|
||||
if (mainWindow)
|
||||
{
|
||||
var projectPath = Globals.ProjectFolder.Replace('/', '\\');
|
||||
var platformBit = Platform.Is64BitApp ? "64" : "32";
|
||||
var title = string.Format("Flax Editor - \'{0}\' ({1}-bit)", projectPath, platformBit);
|
||||
var projectPath = Globals.ProjectFolder;
|
||||
#if PLATFORM_WINDOWS
|
||||
projectPath = projectPath.Replace('/', '\\');
|
||||
#endif
|
||||
var engineVersion = Editor.EngineProject.Version;
|
||||
var engineVersionText = engineVersion.Revision > 0 ? $"{engineVersion.Major}.{engineVersion.Minor}.{engineVersion.Revision}" : $"{engineVersion.Major}.{engineVersion.Minor}";
|
||||
var title = $"Flax Editor {engineVersionText} - \'{projectPath}\'";
|
||||
mainWindow.Title = title;
|
||||
}
|
||||
}
|
||||
@@ -735,7 +739,6 @@ namespace FlaxEditor.Modules
|
||||
settings.Size = Platform.DesktopSize * 0.75f;
|
||||
settings.StartPosition = WindowStartPosition.CenterScreen;
|
||||
settings.ShowAfterFirstPaint = true;
|
||||
|
||||
#if PLATFORM_WINDOWS
|
||||
if (!Editor.Instance.Options.Options.Interface.UseNativeWindowSystem)
|
||||
{
|
||||
@@ -747,12 +750,9 @@ namespace FlaxEditor.Modules
|
||||
#elif PLATFORM_LINUX
|
||||
settings.HasBorder = false;
|
||||
#endif
|
||||
|
||||
MainWindow = Platform.CreateWindow(ref settings);
|
||||
|
||||
if (MainWindow == null)
|
||||
{
|
||||
// Error
|
||||
Editor.LogError("Failed to create editor main window!");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -76,6 +76,10 @@ namespace FlaxEditor.Options
|
||||
[EditorDisplay("Common"), EditorOrder(230)]
|
||||
public InputBinding RotateSelection = new InputBinding(KeyboardKeys.R);
|
||||
|
||||
[DefaultValue(typeof(InputBinding), "F11")]
|
||||
[EditorDisplay("Common"), EditorOrder(240)]
|
||||
public InputBinding ToggleFullscreen = new InputBinding(KeyboardKeys.F11);
|
||||
|
||||
#endregion
|
||||
|
||||
#region File
|
||||
@@ -208,16 +212,20 @@ namespace FlaxEditor.Options
|
||||
[EditorDisplay("Debugger", "Continue"), EditorOrder(810)]
|
||||
public InputBinding DebuggerContinue = new InputBinding(KeyboardKeys.F5);
|
||||
|
||||
[DefaultValue(typeof(InputBinding), "Shift+F11")]
|
||||
[EditorDisplay("Debugger", "Unlock mouse in Play Mode"), EditorOrder(820)]
|
||||
public InputBinding DebuggerUnlockMouse = new InputBinding(KeyboardKeys.F11, KeyboardKeys.Shift);
|
||||
|
||||
[DefaultValue(typeof(InputBinding), "F10")]
|
||||
[EditorDisplay("Debugger", "Step Over"), EditorOrder(820)]
|
||||
[EditorDisplay("Debugger", "Step Over"), EditorOrder(830)]
|
||||
public InputBinding DebuggerStepOver = new InputBinding(KeyboardKeys.F10);
|
||||
|
||||
[DefaultValue(typeof(InputBinding), "F11")]
|
||||
[EditorDisplay("Debugger", "Step Into"), EditorOrder(830)]
|
||||
[EditorDisplay("Debugger", "Step Into"), EditorOrder(840)]
|
||||
public InputBinding DebuggerStepInto = new InputBinding(KeyboardKeys.F11);
|
||||
|
||||
[DefaultValue(typeof(InputBinding), "Shift+F11")]
|
||||
[EditorDisplay("Debugger", "Step Out"), EditorOrder(840)]
|
||||
[EditorDisplay("Debugger", "Step Out"), EditorOrder(850)]
|
||||
public InputBinding DebuggerStepOut = new InputBinding(KeyboardKeys.F11, KeyboardKeys.Shift);
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -7,6 +7,7 @@ using Real = System.Single;
|
||||
#endif
|
||||
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
|
||||
namespace FlaxEditor.SceneGraph.Actors
|
||||
{
|
||||
@@ -30,6 +31,13 @@ namespace FlaxEditor.SceneGraph.Actors
|
||||
|
||||
// Rotate to match the space (GUI uses upper left corner as a root)
|
||||
Actor.LocalOrientation = Quaternion.Euler(0, -180, -180);
|
||||
var uiControl = new UIControl
|
||||
{
|
||||
Name = "Canvas Scalar",
|
||||
Transform = Actor.Transform,
|
||||
Control = new CanvasScaler()
|
||||
};
|
||||
Root.Spawn(uiControl, Actor);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -266,7 +266,7 @@ namespace FlaxEditor.SceneGraph.GUI
|
||||
/// <summary>
|
||||
/// Starts the actor renaming action.
|
||||
/// </summary>
|
||||
public void StartRenaming(EditorWindow window)
|
||||
public void StartRenaming(EditorWindow window, Panel treePanel = null)
|
||||
{
|
||||
// Block renaming during scripts reload
|
||||
if (Editor.Instance.ProgressReporting.CompileScripts.IsActive)
|
||||
@@ -281,7 +281,13 @@ namespace FlaxEditor.SceneGraph.GUI
|
||||
(window as PrefabWindow).ScrollingOnTreeView(false);
|
||||
|
||||
// Start renaming the actor
|
||||
var dialog = RenamePopup.Show(this, TextRect, _actorNode.Name, false);
|
||||
var rect = TextRect;
|
||||
if (treePanel != null)
|
||||
{
|
||||
treePanel.ScrollViewTo(this, true);
|
||||
rect.Size = new Float2(treePanel.Width - TextRect.Location.X, TextRect.Height);
|
||||
}
|
||||
var dialog = RenamePopup.Show(this, rect, _actorNode.Name, false);
|
||||
dialog.Renamed += OnRenamed;
|
||||
dialog.Closed += popup =>
|
||||
{
|
||||
|
||||
@@ -56,12 +56,14 @@ namespace FlaxEditor.States
|
||||
else if (Editor.Options.Options.General.ForceScriptCompilationOnStartup && !skipCompile)
|
||||
{
|
||||
// Generate project files when Cache is missing or was cleared previously
|
||||
if (!Directory.Exists(Path.Combine(Editor.GameProject?.ProjectFolderPath, "Cache", "Intermediate")) ||
|
||||
!Directory.Exists(Path.Combine(Editor.GameProject?.ProjectFolderPath, "Cache", "Projects")))
|
||||
var projectFolderPath = Editor.GameProject?.ProjectFolderPath;
|
||||
if (!Directory.Exists(Path.Combine(projectFolderPath, "Cache", "Intermediate")) ||
|
||||
!Directory.Exists(Path.Combine(projectFolderPath, "Cache", "Projects")))
|
||||
{
|
||||
var customArgs = Editor.Instance.CodeEditing.SelectedEditor.GenerateProjectCustomArgs;
|
||||
var customArgs = Editor.CodeEditing.SelectedEditor?.GenerateProjectCustomArgs;
|
||||
ScriptsBuilder.GenerateProject(customArgs);
|
||||
}
|
||||
|
||||
// Compile scripts before loading any scenes, then we load them and can open scenes
|
||||
ScriptsBuilder.Compile();
|
||||
}
|
||||
|
||||
@@ -465,7 +465,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
if (selectedIndex != -1)
|
||||
{
|
||||
var index = 5 + selectedIndex * 2;
|
||||
SetValue(index, _animationPicker.SelectedID);
|
||||
SetValue(index, _animationPicker.Validator.SelectedID);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -495,7 +495,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
{
|
||||
if (isValid)
|
||||
{
|
||||
_animationPicker.SelectedID = data1;
|
||||
_animationPicker.Validator.SelectedID = data1;
|
||||
_animationSpeed.Value = data0.W;
|
||||
|
||||
var path = string.Empty;
|
||||
@@ -505,7 +505,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
}
|
||||
else
|
||||
{
|
||||
_animationPicker.SelectedID = Guid.Empty;
|
||||
_animationPicker.Validator.SelectedID = Guid.Empty;
|
||||
_animationSpeed.Value = 1.0f;
|
||||
}
|
||||
_animationPicker.Enabled = isValid;
|
||||
|
||||
@@ -60,7 +60,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
Op1(1, "Bitwise NOT", "Negates the value using bitwise operation", new[] { "!", "~" }),
|
||||
Op2(2, "Bitwise AND", "Performs a bitwise conjunction on two values", new[] { "&" }),
|
||||
Op2(3, "Bitwise OR", "", new[] { "|" }),
|
||||
Op2(4, "Bitwise XOR", ""),
|
||||
Op2(4, "Bitwise XOR", "", new[] { "^" }),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
Op1(1, "Boolean NOT", "Negates the boolean value", new[] { "!", "~" }),
|
||||
Op2(2, "Boolean AND", "Performs a logical conjunction on two values", new[] { "&&" }),
|
||||
Op2(3, "Boolean OR", "Returns true if either (or both) of its operands is true", new[] { "||" }),
|
||||
Op2(4, "Boolean XOR", ""),
|
||||
Op2(4, "Boolean XOR", "", new [] { "^" } ),
|
||||
Op2(5, "Boolean NOR", ""),
|
||||
Op2(6, "Boolean NAND", ""),
|
||||
};
|
||||
|
||||
@@ -14,7 +14,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
[HideInEditor]
|
||||
public static class Comparisons
|
||||
{
|
||||
private static NodeArchetype Op(ushort id, string title, string desc)
|
||||
private static NodeArchetype Op(ushort id, string title, string desc, string[] altTitles = null)
|
||||
{
|
||||
return new NodeArchetype
|
||||
{
|
||||
@@ -22,6 +22,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
Title = title,
|
||||
Description = desc,
|
||||
Flags = NodeFlags.AllGraphs,
|
||||
AlternativeTitles = altTitles,
|
||||
ConnectionsHints = ConnectionsHint.Value,
|
||||
Size = new Float2(100, 40),
|
||||
IndependentBoxes = new[]
|
||||
@@ -170,12 +171,12 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
/// </summary>
|
||||
public static NodeArchetype[] Nodes =
|
||||
{
|
||||
Op(1, "==", "Determines whether two values are equal"),
|
||||
Op(2, "!=", "Determines whether two values are not equal"),
|
||||
Op(3, ">", "Determines whether the first value is greater than the other"),
|
||||
Op(4, "<", "Determines whether the first value is less than the other"),
|
||||
Op(5, "<=", "Determines whether the first value is less or equal to the other"),
|
||||
Op(6, ">=", "Determines whether the first value is greater or equal to the other"),
|
||||
Op(1, "==", "Determines whether two values are equal", new[] { "equals" }),
|
||||
Op(2, "!=", "Determines whether two values are not equal", new[] { "not equals" }),
|
||||
Op(3, ">", "Determines whether the first value is greater than the other", new[] { "greater than", "larger than", "bigger than" }),
|
||||
Op(4, "<", "Determines whether the first value is less than the other", new[] { "less than", "smaller than" }),
|
||||
Op(5, "<=", "Determines whether the first value is less or equal to the other", new[] { "less equals than", "smaller equals than" }),
|
||||
Op(6, ">=", "Determines whether the first value is greater or equal to the other", new[] { "greater equals than", "larger equals than", "bigger equals than" }),
|
||||
new NodeArchetype
|
||||
{
|
||||
TypeID = 7,
|
||||
|
||||
@@ -7,11 +7,12 @@ using Real = System.Single;
|
||||
#endif
|
||||
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using System.Linq;
|
||||
using FlaxEditor.CustomEditors.Editors;
|
||||
using FlaxEditor.GUI;
|
||||
using FlaxEditor.Scripting;
|
||||
using FlaxEditor.Surface.Elements;
|
||||
using FlaxEditor.Surface.Undo;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
using FlaxEngine.Utilities;
|
||||
@@ -24,6 +25,109 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
[HideInEditor]
|
||||
public static class Constants
|
||||
{
|
||||
/// <summary>
|
||||
/// A special type of node that adds the functionality to convert nodes to parameters.
|
||||
/// </summary>
|
||||
internal class ConvertToParameterNode : SurfaceNode
|
||||
{
|
||||
private readonly ScriptType _type;
|
||||
private readonly Func<object[], object> _convertFunction;
|
||||
|
||||
/// <inheritdoc />
|
||||
public ConvertToParameterNode(uint id, VisjectSurfaceContext context, NodeArchetype nodeArch, GroupArchetype groupArch, ScriptType type, Func<object[], object> convertFunction = null)
|
||||
: base(id, context, nodeArch, groupArch)
|
||||
{
|
||||
_type = type;
|
||||
_convertFunction = convertFunction;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnShowSecondaryContextMenu(FlaxEditor.GUI.ContextMenu.ContextMenu menu, Float2 location)
|
||||
{
|
||||
base.OnShowSecondaryContextMenu(menu, location);
|
||||
|
||||
menu.AddSeparator();
|
||||
menu.AddButton("Convert to Parameter", OnConvertToParameter);
|
||||
}
|
||||
|
||||
private void OnConvertToParameter()
|
||||
{
|
||||
if (Surface.Owner is not IVisjectSurfaceWindow window)
|
||||
throw new Exception("Surface owner is not a Visject Surface Window");
|
||||
|
||||
Asset asset = Surface.Owner.SurfaceAsset;
|
||||
if (asset == null || !asset.IsLoaded)
|
||||
{
|
||||
Editor.LogError("Asset is null or not loaded");
|
||||
return;
|
||||
}
|
||||
|
||||
// Add parameter to editor
|
||||
var paramIndex = Surface.Parameters.Count;
|
||||
var initValue = _convertFunction == null ? Values[0] : _convertFunction.Invoke(Values);
|
||||
var paramAction = new AddRemoveParamAction
|
||||
{
|
||||
Window = window,
|
||||
IsAdd = true,
|
||||
Name = Utilities.Utils.IncrementNameNumber("New parameter", OnParameterRenameValidate),
|
||||
Type = _type,
|
||||
Index = paramIndex,
|
||||
InitValue = initValue,
|
||||
};
|
||||
paramAction.Do();
|
||||
Surface.AddBatchedUndoAction(paramAction);
|
||||
|
||||
// Spawn Get Parameter Node based on the added parameter
|
||||
Guid parameterGuid = Surface.Parameters[paramIndex].ID;
|
||||
bool undoEnabled = Surface.Undo.Enabled;
|
||||
Surface.Undo.Enabled = false;
|
||||
NodeArchetype arch = Surface.GetParameterGetterNodeArchetype(out var groupId);
|
||||
SurfaceNode node = Surface.Context.SpawnNode(groupId, arch.TypeID, Location, new object[] { parameterGuid });
|
||||
Surface.Undo.Enabled = undoEnabled;
|
||||
if (node is not Parameters.SurfaceNodeParamsGet getNode)
|
||||
throw new Exception("Node is not a ParamsGet node!");
|
||||
Surface.AddBatchedUndoAction(new AddRemoveNodeAction(getNode, true));
|
||||
|
||||
// Recreate connections of constant node
|
||||
// Constant nodes and parameter nodes should have the same ports, so we can just iterate through the connections
|
||||
var editConnectionsAction1 = new EditNodeConnections(Context, this);
|
||||
var editConnectionsAction2 = new EditNodeConnections(Context, node);
|
||||
var boxes = GetBoxes();
|
||||
for (int i = 0; i < boxes.Count; i++)
|
||||
{
|
||||
Box box = boxes[i];
|
||||
if (!box.HasAnyConnection)
|
||||
continue;
|
||||
if (!getNode.TryGetBox(box.ID, out Box paramBox))
|
||||
continue;
|
||||
|
||||
// Iterating backwards, because the CreateConnection method deletes entries from the box connections when target box IsSingle is set to true
|
||||
for (int k = box.Connections.Count - 1; k >= 0; k--)
|
||||
{
|
||||
Box connectedBox = box.Connections[k];
|
||||
paramBox.CreateConnection(connectedBox);
|
||||
}
|
||||
}
|
||||
editConnectionsAction1.End();
|
||||
editConnectionsAction2.End();
|
||||
Surface.AddBatchedUndoAction(editConnectionsAction1);
|
||||
Surface.AddBatchedUndoAction(editConnectionsAction2);
|
||||
|
||||
// Add undo actions and remove constant node
|
||||
var removeConstantAction = new AddRemoveNodeAction(this, false);
|
||||
Surface.AddBatchedUndoAction(removeConstantAction);
|
||||
removeConstantAction.Do();
|
||||
Surface.MarkAsEdited();
|
||||
}
|
||||
|
||||
private bool OnParameterRenameValidate(string value)
|
||||
{
|
||||
if (Surface.Owner is not IVisjectSurfaceWindow window)
|
||||
throw new Exception("Surface owner is not a Visject Surface Window");
|
||||
return !string.IsNullOrWhiteSpace(value) && window.VisjectSurface.Parameters.All(x => x.Name != value);
|
||||
}
|
||||
}
|
||||
|
||||
private class EnumNode : SurfaceNode
|
||||
{
|
||||
private EnumComboBox _picker;
|
||||
@@ -356,6 +460,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
{
|
||||
TypeID = 1,
|
||||
Title = "Bool",
|
||||
Create = (id, context, arch, groupArch) => new ConvertToParameterNode(id, context, arch, groupArch, new ScriptType(typeof(bool))),
|
||||
Description = "Constant boolean value",
|
||||
Flags = NodeFlags.AllGraphs,
|
||||
Size = new Float2(110, 20),
|
||||
@@ -388,6 +493,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
{
|
||||
TypeID = 2,
|
||||
Title = "Integer",
|
||||
Create = (id, context, arch, groupArch) => new ConvertToParameterNode(id, context, arch, groupArch, new ScriptType(typeof(int))),
|
||||
Description = "Constant integer value",
|
||||
Flags = NodeFlags.AllGraphs,
|
||||
Size = new Float2(110, 20),
|
||||
@@ -415,6 +521,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
{
|
||||
TypeID = 3,
|
||||
Title = "Float",
|
||||
Create = (id, context, arch, groupArch) => new ConvertToParameterNode(id, context, arch, groupArch, new ScriptType(typeof(float))),
|
||||
Description = "Constant floating point",
|
||||
Flags = NodeFlags.AllGraphs,
|
||||
Size = new Float2(110, 20),
|
||||
@@ -442,6 +549,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
{
|
||||
TypeID = 4,
|
||||
Title = "Float2",
|
||||
Create = (id, context, arch, groupArch) => new ConvertToParameterNode(id, context, arch, groupArch, new ScriptType(typeof(Float2))),
|
||||
Description = "Constant Float2",
|
||||
Flags = NodeFlags.AllGraphs,
|
||||
Size = new Float2(130, 60),
|
||||
@@ -472,6 +580,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
{
|
||||
TypeID = 5,
|
||||
Title = "Float3",
|
||||
Create = (id, context, arch, groupArch) => new ConvertToParameterNode(id, context, arch, groupArch, new ScriptType(typeof(Float3))),
|
||||
Description = "Constant Float3",
|
||||
Flags = NodeFlags.AllGraphs,
|
||||
Size = new Float2(130, 80),
|
||||
@@ -504,6 +613,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
{
|
||||
TypeID = 6,
|
||||
Title = "Float4",
|
||||
Create = (id, context, arch, groupArch) => new ConvertToParameterNode(id, context, arch, groupArch, new ScriptType(typeof(Float4))),
|
||||
Description = "Constant Float4",
|
||||
Flags = NodeFlags.AllGraphs,
|
||||
Size = new Float2(130, 100),
|
||||
@@ -538,6 +648,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
{
|
||||
TypeID = 7,
|
||||
Title = "Color",
|
||||
Create = (id, context, arch, groupArch) => new ConvertToParameterNode(id, context, arch, groupArch, new ScriptType(typeof(Color))),
|
||||
Description = "RGBA color",
|
||||
Flags = NodeFlags.AllGraphs,
|
||||
Size = new Float2(70, 100),
|
||||
@@ -570,6 +681,8 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
{
|
||||
TypeID = 8,
|
||||
Title = "Rotation",
|
||||
Create = (id, context, arch, groupArch) =>
|
||||
new ConvertToParameterNode(id, context, arch, groupArch, new ScriptType(typeof(Quaternion)), values => Quaternion.Euler((float)values[0], (float)values[1], (float)values[2])),
|
||||
Description = "Euler angle rotation",
|
||||
Flags = NodeFlags.AnimGraph | NodeFlags.VisualScriptGraph | NodeFlags.ParticleEmitterGraph,
|
||||
Size = new Float2(110, 60),
|
||||
@@ -594,6 +707,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
{
|
||||
TypeID = 9,
|
||||
Title = "String",
|
||||
Create = (id, context, arch, groupArch) => new ConvertToParameterNode(id, context, arch, groupArch, new ScriptType(typeof(string))),
|
||||
Description = "Text",
|
||||
Flags = NodeFlags.VisualScriptGraph | NodeFlags.AnimGraph,
|
||||
Size = new Float2(200, 20),
|
||||
@@ -644,6 +758,8 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
{
|
||||
TypeID = 12,
|
||||
Title = "Unsigned Integer",
|
||||
AlternativeTitles = new[] { "UInt", "U Int" },
|
||||
Create = (id, context, arch, groupArch) => new ConvertToParameterNode(id, context, arch, groupArch, new ScriptType(typeof(uint))),
|
||||
Description = "Constant unsigned integer value",
|
||||
Flags = NodeFlags.AllGraphs,
|
||||
Size = new Float2(170, 20),
|
||||
@@ -683,6 +799,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
{
|
||||
TypeID = 15,
|
||||
Title = "Double",
|
||||
Create = (id, context, arch, groupArch) => new ConvertToParameterNode(id, context, arch, groupArch, new ScriptType(typeof(double))),
|
||||
Description = "Constant floating point",
|
||||
Flags = NodeFlags.AllGraphs,
|
||||
Size = new Float2(110, 20),
|
||||
@@ -700,6 +817,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
{
|
||||
TypeID = 16,
|
||||
Title = "Vector2",
|
||||
Create = (id, context, arch, groupArch) => new ConvertToParameterNode(id, context, arch, groupArch, new ScriptType(typeof(Vector2))),
|
||||
Description = "Constant Vector2",
|
||||
Flags = NodeFlags.AllGraphs,
|
||||
Size = new Float2(130, 60),
|
||||
@@ -720,6 +838,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
{
|
||||
TypeID = 17,
|
||||
Title = "Vector3",
|
||||
Create = (id, context, arch, groupArch) => new ConvertToParameterNode(id, context, arch, groupArch, new ScriptType(typeof(Vector3))),
|
||||
Description = "Constant Vector3",
|
||||
Flags = NodeFlags.AllGraphs,
|
||||
Size = new Float2(130, 80),
|
||||
@@ -742,6 +861,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
{
|
||||
TypeID = 18,
|
||||
Title = "Vector4",
|
||||
Create = (id, context, arch, groupArch) => new ConvertToParameterNode(id, context, arch, groupArch, new ScriptType(typeof(Vector4))),
|
||||
Description = "Constant Vector4",
|
||||
Flags = NodeFlags.AllGraphs,
|
||||
Size = new Float2(130, 100),
|
||||
|
||||
@@ -95,7 +95,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
|
||||
private void OnAssetPickerSelectedItemChanged()
|
||||
{
|
||||
SetValue(0, _assetPicker.SelectedID);
|
||||
SetValue(0, _assetPicker.Validator.SelectedID);
|
||||
}
|
||||
|
||||
private void TryRestoreConnections(Box box, Box[] prevBoxes, ref NodeElementArchetype arch)
|
||||
@@ -133,7 +133,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
var prevOutputs = _outputs;
|
||||
|
||||
// Extract function signature parameters (inputs and outputs packed)
|
||||
_asset = LoadSignature(_assetPicker.SelectedID, out var typeNames, out var names);
|
||||
_asset = LoadSignature(_assetPicker.Validator.SelectedID, out var typeNames, out var names);
|
||||
if (typeNames != null && names != null)
|
||||
{
|
||||
var types = new Type[typeNames.Length];
|
||||
@@ -174,7 +174,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
_outputs[i] = box;
|
||||
}
|
||||
|
||||
Title = _assetPicker.SelectedItem.ShortName;
|
||||
Title = _assetPicker.Validator.SelectedItem.ShortName;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -2470,6 +2470,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
Title = string.Empty,
|
||||
Description = "Overrides the base class method with custom implementation",
|
||||
Flags = NodeFlags.VisualScriptGraph | NodeFlags.NoSpawnViaGUI | NodeFlags.NoSpawnViaPaste,
|
||||
SortScore = 10,
|
||||
IsInputCompatible = MethodOverrideNode.IsInputCompatible,
|
||||
IsOutputCompatible = MethodOverrideNode.IsOutputCompatible,
|
||||
Size = new Float2(240, 60),
|
||||
|
||||
@@ -13,6 +13,11 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
public static class Math
|
||||
{
|
||||
private static NodeArchetype Op1(ushort id, string title, string desc, ConnectionsHint hints = ConnectionsHint.Numeric, Type type = null)
|
||||
{
|
||||
return Op1(id, title, desc, null, hints, type);
|
||||
}
|
||||
|
||||
private static NodeArchetype Op1(ushort id, string title, string desc, string[] altTitles, ConnectionsHint hints = ConnectionsHint.Numeric, Type type = null)
|
||||
{
|
||||
return new NodeArchetype
|
||||
{
|
||||
@@ -20,6 +25,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
Title = title,
|
||||
Description = desc,
|
||||
Flags = NodeFlags.AllGraphs,
|
||||
AlternativeTitles = altTitles,
|
||||
Size = new Float2(110, 20),
|
||||
DefaultType = new ScriptType(type),
|
||||
ConnectionsHints = hints,
|
||||
@@ -92,6 +98,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
{
|
||||
TypeID = 11,
|
||||
Title = "Length",
|
||||
AlternativeTitles = new[] { "Magnitude", "Mag" },
|
||||
Description = "Returns the length of A vector",
|
||||
Flags = NodeFlags.AllGraphs,
|
||||
Size = new Float2(110, 20),
|
||||
@@ -107,10 +114,10 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
Op1(13, "Round", "Rounds A to the nearest integer"),
|
||||
Op1(14, "Saturate", "Clamps A to the range [0, 1]"),
|
||||
Op1(15, "Sine", "Returns sine of A"),
|
||||
Op1(16, "Sqrt", "Returns square root of A"),
|
||||
Op1(16, "Sqrt", "Returns square root of A", new [] { "Square Root", "Square", "Root" }),
|
||||
Op1(17, "Tangent", "Returns tangent of A"),
|
||||
Op2(18, "Cross", "Returns the cross product of A and B", ConnectionsHint.None, typeof(Float3)),
|
||||
Op2(19, "Distance", "Returns a distance scalar between A and B", ConnectionsHint.Vector, null, typeof(float), false),
|
||||
Op2(19, "Distance", "Returns a distance scalar between A and B", new [] { "Magnitude", "Mag", "Length" }, ConnectionsHint.Vector, null, typeof(float), false),
|
||||
Op2(20, "Dot", "Returns the dot product of A and B", ConnectionsHint.Vector, null, typeof(float), false),
|
||||
Op2(21, "Max", "Selects the greater of A and B"),
|
||||
Op2(22, "Min", "Selects the lesser of A and B"),
|
||||
@@ -185,7 +192,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
}
|
||||
},
|
||||
//
|
||||
Op1(27, "Negate", "Returns opposite value"),
|
||||
Op1(27, "Negate", "Returns opposite value", new [] { "Invert" }),
|
||||
Op1(28, "One Minus", "Returns 1 - value"),
|
||||
//
|
||||
new NodeArchetype
|
||||
@@ -225,6 +232,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
{
|
||||
TypeID = 31,
|
||||
Title = "Mad",
|
||||
AlternativeTitles = new [] { "Multiply", "Add", "*+" },
|
||||
Description = "Performs value multiplication and addition at once",
|
||||
Flags = NodeFlags.AllGraphs,
|
||||
Size = new Float2(160, 60),
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
using System;
|
||||
using FlaxEditor.Content.Settings;
|
||||
using FlaxEditor.GUI;
|
||||
using FlaxEditor.Scripting;
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.Surface.Archetypes
|
||||
@@ -95,6 +96,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
{
|
||||
TypeID = 1,
|
||||
Title = "Texture",
|
||||
Create = (id, context, arch, groupArch) => new Constants.ConvertToParameterNode(id, context, arch, groupArch, new ScriptType(typeof(Texture))),
|
||||
Description = "Two dimensional texture object",
|
||||
Flags = NodeFlags.MaterialGraph,
|
||||
Size = new Float2(140, 120),
|
||||
@@ -131,6 +133,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
{
|
||||
TypeID = 3,
|
||||
Title = "Cube Texture",
|
||||
Create = (id, context, arch, groupArch) => new Constants.ConvertToParameterNode(id, context, arch, groupArch, new ScriptType(typeof(CubeTexture))),
|
||||
Description = "Set of 6 textures arranged in a cube",
|
||||
Flags = NodeFlags.MaterialGraph,
|
||||
Size = new Float2(140, 120),
|
||||
@@ -154,6 +157,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
{
|
||||
TypeID = 4,
|
||||
Title = "Normal Map",
|
||||
Create = (id, context, arch, groupArch) => new Constants.ConvertToParameterNode(id, context, arch, groupArch, new ScriptType(typeof(NormalMap))),
|
||||
Description = "Two dimensional texture object sampled as a normal map",
|
||||
Flags = NodeFlags.MaterialGraph,
|
||||
Size = new Float2(140, 120),
|
||||
|
||||
@@ -1483,7 +1483,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
{
|
||||
TypeID = 11,
|
||||
Title = "Comment",
|
||||
AlternativeTitles = new[] { "//" },
|
||||
AlternativeTitles = new[] { "//" , "Group" },
|
||||
TryParseText = (string filterText, out object[] data) =>
|
||||
{
|
||||
data = null;
|
||||
@@ -1510,6 +1510,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
"Comment", // Title
|
||||
new Color(1.0f, 1.0f, 1.0f, 0.2f), // Color
|
||||
new Float2(400.0f, 400.0f), // Size
|
||||
-1, // Order
|
||||
},
|
||||
},
|
||||
CurveNode<float>.GetArchetype(12, "Curve", typeof(float), 0.0f, 1.0f),
|
||||
@@ -1638,6 +1639,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
{
|
||||
TypeID = 22,
|
||||
Title = "As",
|
||||
AlternativeTitles = new [] { "Cast" },
|
||||
Create = (id, context, arch, groupArch) => new AsNode(id, context, arch, groupArch),
|
||||
Description = "Casts the object to a different type. Returns null if cast fails.",
|
||||
Flags = NodeFlags.VisualScriptGraph | NodeFlags.AnimGraph,
|
||||
|
||||
@@ -78,6 +78,7 @@ namespace FlaxEditor.Surface.ContextMenu
|
||||
if (!Visible)
|
||||
return;
|
||||
|
||||
SortScore += _archetype.SortScore;
|
||||
if (selectedBox != null && CanConnectTo(selectedBox))
|
||||
SortScore += 1;
|
||||
if (Data != null)
|
||||
|
||||
@@ -38,13 +38,13 @@ namespace FlaxEditor.Surface.Elements
|
||||
|
||||
private void OnNodeValuesChanged()
|
||||
{
|
||||
SelectedID = (Guid)ParentNode.Values[Archetype.ValueIndex];
|
||||
Validator.SelectedID = (Guid)ParentNode.Values[Archetype.ValueIndex];
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnSelectedItemChanged()
|
||||
{
|
||||
var selectedId = SelectedID;
|
||||
var selectedId = Validator.SelectedID;
|
||||
if (ParentNode != null && (Guid)ParentNode.Values[Archetype.ValueIndex] != selectedId)
|
||||
{
|
||||
ParentNode.SetValue(Archetype.ValueIndex, selectedId);
|
||||
|
||||
@@ -149,6 +149,11 @@ namespace FlaxEditor.Surface
|
||||
/// </summary>
|
||||
public object Tag;
|
||||
|
||||
/// <summary>
|
||||
/// Custom score value to use when sorting node archetypes in Editor. If positive (eg. 1, 2) can be used to add more importance for a specific node type.
|
||||
/// </summary>
|
||||
public float SortScore;
|
||||
|
||||
/// <summary>
|
||||
/// Default node values. This array supports types: bool, int, float, Vector2, Vector3, Vector4, Color, Rectangle, Guid, string, Matrix and byte[].
|
||||
/// </summary>
|
||||
@@ -204,14 +209,17 @@ namespace FlaxEditor.Surface
|
||||
Size = Size,
|
||||
Flags = Flags,
|
||||
Title = Title,
|
||||
Description = Title,
|
||||
SubTitle = SubTitle,
|
||||
Description = Description,
|
||||
AlternativeTitles = (string[])AlternativeTitles?.Clone(),
|
||||
Tag = Tag,
|
||||
SortScore = SortScore,
|
||||
DefaultValues = (object[])DefaultValues?.Clone(),
|
||||
DefaultType = DefaultType,
|
||||
ConnectionsHints = ConnectionsHints,
|
||||
IndependentBoxes = (int[])IndependentBoxes?.Clone(),
|
||||
DependentBoxes = (int[])DependentBoxes?.Clone(),
|
||||
DependentBoxFilter = DependentBoxFilter,
|
||||
Elements = (NodeElementArchetype[])Elements?.Clone(),
|
||||
TryParseText = TryParseText,
|
||||
};
|
||||
|
||||
@@ -93,7 +93,7 @@ namespace FlaxEditor.Surface
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override NodeArchetype GetParameterGetterNodeArchetype(out ushort groupId)
|
||||
protected internal override NodeArchetype GetParameterGetterNodeArchetype(out ushort groupId)
|
||||
{
|
||||
groupId = 6;
|
||||
return Archetypes.Parameters.Nodes[1];
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
using System;
|
||||
using FlaxEditor.GUI;
|
||||
using FlaxEditor.GUI.ContextMenu;
|
||||
using FlaxEditor.GUI.Input;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
@@ -52,6 +53,12 @@ namespace FlaxEditor.Surface
|
||||
set => SetValue(2, value, false);
|
||||
}
|
||||
|
||||
private int OrderValue
|
||||
{
|
||||
get => (int)Values[3];
|
||||
set => SetValue(3, value, false);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public SurfaceComment(uint id, VisjectSurfaceContext context, NodeArchetype nodeArch, GroupArchetype groupArch)
|
||||
: base(id, context, nodeArch, groupArch)
|
||||
@@ -67,6 +74,19 @@ namespace FlaxEditor.Surface
|
||||
Title = TitleValue;
|
||||
Color = ColorValue;
|
||||
Size = SizeValue;
|
||||
|
||||
// Order
|
||||
// Backwards compatibility - When opening with an older version send the old comments to the back
|
||||
if (Values.Length < 4)
|
||||
{
|
||||
if (IndexInParent > 0)
|
||||
IndexInParent = 0;
|
||||
OrderValue = IndexInParent;
|
||||
}
|
||||
else if(OrderValue != -1)
|
||||
{
|
||||
IndexInParent = OrderValue;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -76,6 +96,10 @@ namespace FlaxEditor.Surface
|
||||
|
||||
// Randomize color
|
||||
Color = ColorValue = Color.FromHSV(new Random().NextFloat(0, 360), 0.7f, 0.25f, 0.8f);
|
||||
|
||||
if(OrderValue == -1)
|
||||
OrderValue = Context.CommentCount - 1;
|
||||
IndexInParent = OrderValue;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -314,5 +338,38 @@ namespace FlaxEditor.Surface
|
||||
Color = ColorValue = color;
|
||||
Surface.MarkAsEdited(false);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnShowSecondaryContextMenu(FlaxEditor.GUI.ContextMenu.ContextMenu menu, Float2 location)
|
||||
{
|
||||
base.OnShowSecondaryContextMenu(menu, location);
|
||||
|
||||
menu.AddSeparator();
|
||||
ContextMenuChildMenu cmOrder = menu.AddChildMenu("Order");
|
||||
{
|
||||
cmOrder.ContextMenu.AddButton("Bring Forward", () =>
|
||||
{
|
||||
if(IndexInParent < Context.CommentCount-1)
|
||||
IndexInParent++;
|
||||
OrderValue = IndexInParent;
|
||||
});
|
||||
cmOrder.ContextMenu.AddButton("Bring to Front", () =>
|
||||
{
|
||||
IndexInParent = Context.CommentCount-1;
|
||||
OrderValue = IndexInParent;
|
||||
});
|
||||
cmOrder.ContextMenu.AddButton("Send Backward", () =>
|
||||
{
|
||||
if(IndexInParent > 0)
|
||||
IndexInParent--;
|
||||
OrderValue = IndexInParent;
|
||||
});
|
||||
cmOrder.ContextMenu.AddButton("Send to Back", () =>
|
||||
{
|
||||
IndexInParent = 0;
|
||||
OrderValue = IndexInParent;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
using System;
|
||||
using FlaxEditor.Scripting;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.Utilities;
|
||||
|
||||
namespace FlaxEditor.Surface
|
||||
{
|
||||
@@ -27,7 +26,7 @@ namespace FlaxEditor.Surface
|
||||
/// <summary>
|
||||
/// Parameter unique ID
|
||||
/// </summary>
|
||||
public Guid ID;
|
||||
public Guid ID = Guid.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Parameter name
|
||||
@@ -49,23 +48,5 @@ namespace FlaxEditor.Surface
|
||||
/// </summary>
|
||||
[NoSerialize, HideInEditor]
|
||||
public readonly SurfaceMeta Meta = new SurfaceMeta();
|
||||
|
||||
/// <summary>
|
||||
/// Creates the new parameter of the given type.
|
||||
/// </summary>
|
||||
/// <param name="type">The type.</param>
|
||||
/// <param name="name">The name.</param>
|
||||
/// <returns>The created parameter.</returns>
|
||||
public static SurfaceParameter Create(ScriptType type, string name)
|
||||
{
|
||||
return new SurfaceParameter
|
||||
{
|
||||
ID = Guid.NewGuid(),
|
||||
IsPublic = true,
|
||||
Name = name,
|
||||
Type = type,
|
||||
Value = TypeUtils.GetDefaultValue(type),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -151,7 +151,7 @@ namespace FlaxEditor.Surface
|
||||
/// </summary>
|
||||
/// <param name="groupId">The group ID.</param>
|
||||
/// <returns>The node archetype.</returns>
|
||||
protected virtual NodeArchetype GetParameterGetterNodeArchetype(out ushort groupId)
|
||||
protected internal virtual NodeArchetype GetParameterGetterNodeArchetype(out ushort groupId)
|
||||
{
|
||||
groupId = 6;
|
||||
return Archetypes.Parameters.Nodes[0];
|
||||
|
||||
@@ -716,7 +716,18 @@ namespace FlaxEditor.Surface
|
||||
return null;
|
||||
Rectangle surfaceArea = GetNodesBounds(selection).MakeExpanded(80.0f);
|
||||
|
||||
return _context.CreateComment(ref surfaceArea, string.IsNullOrEmpty(text) ? "Comment" : text, new Color(1.0f, 1.0f, 1.0f, 0.2f));
|
||||
// Order below other selected comments
|
||||
bool hasCommentsSelected = false;
|
||||
int lowestCommentOrder = int.MaxValue;
|
||||
for (int i = 0; i < selection.Count; i++)
|
||||
{
|
||||
if (selection[i] is not SurfaceComment || selection[i].IndexInParent >= lowestCommentOrder)
|
||||
continue;
|
||||
hasCommentsSelected = true;
|
||||
lowestCommentOrder = selection[i].IndexInParent;
|
||||
}
|
||||
|
||||
return _context.CreateComment(ref surfaceArea, string.IsNullOrEmpty(text) ? "Comment" : text, new Color(1.0f, 1.0f, 1.0f, 0.2f), hasCommentsSelected ? lowestCommentOrder : -1);
|
||||
}
|
||||
|
||||
private static Rectangle GetNodesBounds(List<SurfaceNode> nodes)
|
||||
|
||||
@@ -920,12 +920,6 @@ namespace FlaxEditor.Surface
|
||||
// Link control
|
||||
control.OnLoaded(action);
|
||||
control.Parent = RootControl;
|
||||
|
||||
if (control is SurfaceComment)
|
||||
{
|
||||
// Move comments to the background
|
||||
control.IndexInParent = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -85,6 +85,27 @@ namespace FlaxEditor.Surface
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the amount of surface comments
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is used as an alternative to <see cref="Comments"/>, if only the amount of comments is important.
|
||||
/// Is faster and doesn't allocate as much memory
|
||||
/// </remarks>
|
||||
public int CommentCount
|
||||
{
|
||||
get
|
||||
{
|
||||
int count = 0;
|
||||
for (int i = 0; i < RootControl.Children.Count; i++)
|
||||
{
|
||||
if (RootControl.Children[i] is SurfaceComment)
|
||||
count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this context is modified (needs saving and flushing with surface data context source).
|
||||
/// </summary>
|
||||
@@ -285,14 +306,16 @@ namespace FlaxEditor.Surface
|
||||
/// <param name="surfaceArea">The surface area to create comment.</param>
|
||||
/// <param name="title">The comment title.</param>
|
||||
/// <param name="color">The comment color.</param>
|
||||
/// <param name="customOrder">The comment order or -1 to use default.</param>
|
||||
/// <returns>The comment object</returns>
|
||||
public virtual SurfaceComment SpawnComment(ref Rectangle surfaceArea, string title, Color color)
|
||||
public virtual SurfaceComment SpawnComment(ref Rectangle surfaceArea, string title, Color color, int customOrder = -1)
|
||||
{
|
||||
var values = new object[]
|
||||
{
|
||||
title, // Title
|
||||
color, // Color
|
||||
surfaceArea.Size, // Size
|
||||
customOrder, // Order
|
||||
};
|
||||
return (SurfaceComment)SpawnNode(7, 11, surfaceArea.Location, values);
|
||||
}
|
||||
@@ -303,11 +326,12 @@ namespace FlaxEditor.Surface
|
||||
/// <param name="surfaceArea">The surface area to create comment.</param>
|
||||
/// <param name="title">The comment title.</param>
|
||||
/// <param name="color">The comment color.</param>
|
||||
/// <param name="customOrder">The comment order or -1 to use default.</param>
|
||||
/// <returns>The comment object</returns>
|
||||
public SurfaceComment CreateComment(ref Rectangle surfaceArea, string title, Color color)
|
||||
public SurfaceComment CreateComment(ref Rectangle surfaceArea, string title, Color color, int customOrder = -1)
|
||||
{
|
||||
// Create comment
|
||||
var comment = SpawnComment(ref surfaceArea, title, color);
|
||||
var comment = SpawnComment(ref surfaceArea, title, color, customOrder);
|
||||
if (comment == null)
|
||||
{
|
||||
Editor.LogWarning("Failed to create comment.");
|
||||
|
||||
@@ -17,6 +17,7 @@ using FlaxEditor.Viewport.Previews;
|
||||
using FlaxEditor.Windows.Assets;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
using FlaxEngine.Utilities;
|
||||
|
||||
namespace FlaxEditor.Surface
|
||||
{
|
||||
@@ -258,6 +259,11 @@ namespace FlaxEditor.Surface
|
||||
/// </summary>
|
||||
public IVisjectSurfaceWindow Window;
|
||||
|
||||
/// <summary>
|
||||
/// The identifier of the parameter. Empty to auto generate it.
|
||||
/// </summary>
|
||||
public Guid Id = Guid.NewGuid();
|
||||
|
||||
/// <summary>
|
||||
/// True if adding, false if removing parameter.
|
||||
/// </summary>
|
||||
@@ -278,6 +284,11 @@ namespace FlaxEditor.Surface
|
||||
/// </summary>
|
||||
public ScriptType Type;
|
||||
|
||||
/// <summary>
|
||||
/// The value to initialize the parameter with. Can be null to use default one for the parameter type.
|
||||
/// </summary>
|
||||
public object InitValue;
|
||||
|
||||
/// <inheritdoc />
|
||||
public string ActionString => IsAdd ? "Add parameter" : "Remove parameter";
|
||||
|
||||
@@ -304,7 +315,14 @@ namespace FlaxEditor.Surface
|
||||
var type = Type;
|
||||
if (IsAdd && type.Type == typeof(NormalMap))
|
||||
type = new ScriptType(typeof(Texture));
|
||||
var param = SurfaceParameter.Create(type, Name);
|
||||
var param = new SurfaceParameter
|
||||
{
|
||||
ID = Id,
|
||||
IsPublic = true,
|
||||
Name = Name,
|
||||
Type = type,
|
||||
Value = InitValue ?? TypeUtils.GetDefaultValue(type),
|
||||
};
|
||||
if (IsAdd && Type.Type == typeof(NormalMap))
|
||||
param.Value = FlaxEngine.Content.LoadAsyncInternal<Texture>("Engine/Textures/NormalTexture");
|
||||
Window.VisjectSurface.Parameters.Insert(Index, param);
|
||||
@@ -725,6 +743,8 @@ namespace FlaxEditor.Surface
|
||||
protected VisjectSurfaceWindow(Editor editor, AssetItem item, bool useTabs = false)
|
||||
: base(editor, item)
|
||||
{
|
||||
var inputOptions = Editor.Options.Options.Input;
|
||||
|
||||
// Undo
|
||||
_undo = new FlaxEditor.Undo();
|
||||
_undo.UndoDone += OnUndoRedo;
|
||||
@@ -775,10 +795,10 @@ namespace FlaxEditor.Surface
|
||||
// Toolstrip
|
||||
_saveButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Save64, Save).LinkTooltip("Save");
|
||||
_toolstrip.AddSeparator();
|
||||
_undoButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Undo64, _undo.PerformUndo).LinkTooltip("Undo (Ctrl+Z)");
|
||||
_redoButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Redo64, _undo.PerformRedo).LinkTooltip("Redo (Ctrl+Y)");
|
||||
_undoButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Undo64, _undo.PerformUndo).LinkTooltip($"Undo ({inputOptions.Undo})");
|
||||
_redoButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Redo64, _undo.PerformRedo).LinkTooltip($"Redo ({inputOptions.Redo})");
|
||||
_toolstrip.AddSeparator();
|
||||
_toolstrip.AddButton(Editor.Icons.Search64, Editor.ContentFinding.ShowSearch).LinkTooltip("Open content search tool (Ctrl+F)");
|
||||
_toolstrip.AddButton(Editor.Icons.Search64, Editor.ContentFinding.ShowSearch).LinkTooltip($"Open content search tool ({inputOptions.Search})");
|
||||
_toolstrip.AddButton(editor.Icons.CenterView64, ShowWholeGraph).LinkTooltip("Show whole graph");
|
||||
|
||||
// Setup input actions
|
||||
@@ -1058,7 +1078,6 @@ namespace FlaxEditor.Surface
|
||||
public virtual void OnParamRemoveUndo()
|
||||
{
|
||||
_refreshPropertiesOnLoad = true;
|
||||
//_propertiesEditor.BuildLayoutOnUpdate();
|
||||
_propertiesEditor.BuildLayout();
|
||||
}
|
||||
|
||||
|
||||
@@ -144,7 +144,7 @@ namespace FlaxEditor.Surface
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override NodeArchetype GetParameterGetterNodeArchetype(out ushort groupId)
|
||||
protected internal override NodeArchetype GetParameterGetterNodeArchetype(out ushort groupId)
|
||||
{
|
||||
groupId = 6;
|
||||
return Archetypes.Parameters.Nodes[2];
|
||||
|
||||
@@ -290,7 +290,7 @@ namespace FlaxEditor.Tools.Terrain
|
||||
|
||||
var patchCoord = Gizmo.SelectedPatchCoord;
|
||||
var chunkCoord = Gizmo.SelectedChunkCoord;
|
||||
var action = new EditChunkMaterialAction(CarveTab.SelectedTerrain, ref patchCoord, ref chunkCoord, _chunkOverrideMaterial.SelectedAsset as MaterialBase);
|
||||
var action = new EditChunkMaterialAction(CarveTab.SelectedTerrain, ref patchCoord, ref chunkCoord, _chunkOverrideMaterial.Validator.SelectedAsset as MaterialBase);
|
||||
action.Do();
|
||||
CarveTab.Editor.Undo.AddAction(action);
|
||||
}
|
||||
@@ -336,12 +336,12 @@ namespace FlaxEditor.Tools.Terrain
|
||||
_isUpdatingUI = true;
|
||||
if (terrain.HasPatch(ref patchCoord))
|
||||
{
|
||||
_chunkOverrideMaterial.SelectedAsset = terrain.GetChunkOverrideMaterial(ref patchCoord, ref chunkCoord);
|
||||
_chunkOverrideMaterial.Validator.SelectedAsset = terrain.GetChunkOverrideMaterial(ref patchCoord, ref chunkCoord);
|
||||
_chunkOverrideMaterial.Enabled = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
_chunkOverrideMaterial.SelectedAsset = null;
|
||||
_chunkOverrideMaterial.Validator.SelectedAsset = null;
|
||||
_chunkOverrideMaterial.Enabled = false;
|
||||
}
|
||||
_isUpdatingUI = false;
|
||||
|
||||
@@ -46,14 +46,14 @@ namespace FlaxEditor.Windows
|
||||
if (asset != null)
|
||||
{
|
||||
var path = asset.Path;
|
||||
picker.SelectedAsset = asset;
|
||||
picker.Validator.SelectedAsset = asset;
|
||||
Title = System.IO.Path.GetFileNameWithoutExtension(path);
|
||||
TooltipText = asset.TypeName + '\n' + path;
|
||||
}
|
||||
else
|
||||
{
|
||||
picker.SelectedID = AssetId;
|
||||
var assetItem = picker.SelectedItem as AssetItem;
|
||||
picker.Validator.SelectedID = AssetId;
|
||||
var assetItem = picker.Validator.SelectedItem as AssetItem;
|
||||
if (assetItem != null)
|
||||
{
|
||||
Title = assetItem.ShortName;
|
||||
|
||||
@@ -230,6 +230,8 @@ namespace FlaxEditor.Windows.Assets
|
||||
public AnimationWindow(Editor editor, AssetItem item)
|
||||
: base(editor, item)
|
||||
{
|
||||
var inputOptions = Editor.Options.Options.Input;
|
||||
|
||||
// Undo
|
||||
_undo = new Undo();
|
||||
_undo.UndoDone += OnUndoRedo;
|
||||
@@ -265,8 +267,8 @@ namespace FlaxEditor.Windows.Assets
|
||||
// Toolstrip
|
||||
_saveButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Save64, Save).LinkTooltip("Save");
|
||||
_toolstrip.AddSeparator();
|
||||
_undoButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Undo64, _undo.PerformUndo).LinkTooltip("Undo (Ctrl+Z)");
|
||||
_redoButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Redo64, _undo.PerformRedo).LinkTooltip("Redo (Ctrl+Y)");
|
||||
_undoButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Undo64, _undo.PerformUndo).LinkTooltip($"Undo ({inputOptions.Undo})");
|
||||
_redoButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Redo64, _undo.PerformRedo).LinkTooltip($"Redo ({inputOptions.Redo})");
|
||||
_toolstrip.AddSeparator();
|
||||
_toolstrip.AddButton(editor.Icons.Docs64, () => Platform.OpenUrl(Utilities.Constants.DocsUrl + "manual/animation/animation/index.html")).LinkTooltip("See documentation to learn more");
|
||||
|
||||
|
||||
@@ -130,6 +130,8 @@ namespace FlaxEditor.Windows.Assets
|
||||
public BehaviorTreeWindow(Editor editor, BinaryAssetItem item)
|
||||
: base(editor, item)
|
||||
{
|
||||
var inputOptions = Editor.Options.Options.Input;
|
||||
|
||||
// Undo
|
||||
_undo = new Undo();
|
||||
_undo.UndoDone += OnUndoRedo;
|
||||
@@ -172,10 +174,10 @@ namespace FlaxEditor.Windows.Assets
|
||||
// Toolstrip
|
||||
_saveButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Save64, Save).LinkTooltip("Save");
|
||||
_toolstrip.AddSeparator();
|
||||
_undoButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Undo64, _undo.PerformUndo).LinkTooltip("Undo (Ctrl+Z)");
|
||||
_redoButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Redo64, _undo.PerformRedo).LinkTooltip("Redo (Ctrl+Y)");
|
||||
_undoButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Undo64, _undo.PerformUndo).LinkTooltip($"Undo ({inputOptions.Undo})");
|
||||
_redoButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Redo64, _undo.PerformRedo).LinkTooltip($"Redo ({inputOptions.Redo})");
|
||||
_toolstrip.AddSeparator();
|
||||
_toolstrip.AddButton(Editor.Icons.Search64, Editor.ContentFinding.ShowSearch).LinkTooltip("Open content search tool (Ctrl+F)");
|
||||
_toolstrip.AddButton(Editor.Icons.Search64, Editor.ContentFinding.ShowSearch).LinkTooltip($"Open content search tool ({inputOptions.Search})");
|
||||
_toolstrip.AddButton(editor.Icons.CenterView64, _surface.ShowWholeGraph).LinkTooltip("Show whole graph");
|
||||
_toolstrip.AddButton(editor.Icons.Docs64, () => Platform.OpenUrl(Utilities.Constants.DocsUrl + "manual/scripting/ai/behavior-trees/index.html")).LinkTooltip("See documentation to learn more");
|
||||
|
||||
|
||||
@@ -395,6 +395,8 @@ namespace FlaxEditor.Windows.Assets
|
||||
public GameplayGlobalsWindow(Editor editor, AssetItem item)
|
||||
: base(editor, item)
|
||||
{
|
||||
var inputOptions = Editor.Options.Options.Input;
|
||||
|
||||
_undo = new Undo();
|
||||
_undo.ActionDone += OnUndo;
|
||||
_undo.UndoDone += OnUndo;
|
||||
@@ -411,10 +413,10 @@ namespace FlaxEditor.Windows.Assets
|
||||
_proxy = new PropertiesProxy();
|
||||
_propertiesEditor.Select(_proxy);
|
||||
|
||||
_saveButton = (ToolStripButton)_toolstrip.AddButton(editor.Icons.Save64, Save).LinkTooltip("Save asset");
|
||||
_saveButton = (ToolStripButton)_toolstrip.AddButton(editor.Icons.Save64, Save).LinkTooltip($"Save asset ({inputOptions.Save})");
|
||||
_toolstrip.AddSeparator();
|
||||
_undoButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Undo64, _undo.PerformUndo).LinkTooltip("Undo (Ctrl+Z)");
|
||||
_redoButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Redo64, _undo.PerformRedo).LinkTooltip("Redo (Ctrl+Y)");
|
||||
_undoButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Undo64, _undo.PerformUndo).LinkTooltip($"Undo ({inputOptions.Undo})");
|
||||
_redoButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Redo64, _undo.PerformRedo).LinkTooltip($"Redo ({inputOptions.Redo})");
|
||||
_toolstrip.AddSeparator();
|
||||
_resetButton = (ToolStripButton)_toolstrip.AddButton(editor.Icons.Rotate32, Reset).LinkTooltip("Resets the variables values to the default values");
|
||||
|
||||
|
||||
@@ -34,6 +34,8 @@ namespace FlaxEditor.Windows.Assets
|
||||
public JsonAssetWindow(Editor editor, AssetItem item)
|
||||
: base(editor, item)
|
||||
{
|
||||
var inputOptions = Editor.Options.Options.Input;
|
||||
|
||||
// Undo
|
||||
_undo = new Undo();
|
||||
_undo.UndoDone += OnUndoRedo;
|
||||
@@ -43,8 +45,8 @@ namespace FlaxEditor.Windows.Assets
|
||||
// Toolstrip
|
||||
_saveButton = (ToolStripButton)_toolstrip.AddButton(editor.Icons.Save64, Save).LinkTooltip("Save");
|
||||
_toolstrip.AddSeparator();
|
||||
_undoButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Undo64, _undo.PerformUndo).LinkTooltip("Undo (Ctrl+Z)");
|
||||
_redoButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Redo64, _undo.PerformRedo).LinkTooltip("Redo (Ctrl+Y)");
|
||||
_undoButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Undo64, _undo.PerformUndo).LinkTooltip($"Undo ({inputOptions.Undo})");
|
||||
_redoButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Redo64, _undo.PerformRedo).LinkTooltip($"Redo ({inputOptions.Redo})");
|
||||
|
||||
// Panel
|
||||
var panel = new Panel(ScrollBars.Vertical)
|
||||
|
||||
@@ -126,6 +126,8 @@ namespace FlaxEditor.Windows.Assets
|
||||
public LocalizedStringTableWindow(Editor editor, AssetItem item)
|
||||
: base(editor, item)
|
||||
{
|
||||
var inputOptions = Editor.Options.Options.Input;
|
||||
|
||||
// Undo
|
||||
_undo = new Undo();
|
||||
_undo.UndoDone += OnUndoRedo;
|
||||
@@ -135,8 +137,8 @@ namespace FlaxEditor.Windows.Assets
|
||||
// Toolstrip
|
||||
_saveButton = (ToolStripButton)_toolstrip.AddButton(editor.Icons.Save64, Save).LinkTooltip("Save");
|
||||
_toolstrip.AddSeparator();
|
||||
_undoButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Undo64, _undo.PerformUndo).LinkTooltip("Undo (Ctrl+Z)");
|
||||
_redoButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Redo64, _undo.PerformRedo).LinkTooltip("Redo (Ctrl+Y)");
|
||||
_undoButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Undo64, _undo.PerformUndo).LinkTooltip($"Undo ({inputOptions.Undo})");
|
||||
_redoButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Redo64, _undo.PerformRedo).LinkTooltip($"Redo ({inputOptions.Redo})");
|
||||
_toolstrip.AddSeparator();
|
||||
_toolstrip.AddButton(Editor.Icons.Up64, OnExport).LinkTooltip("Export localization table entries for translation to .pot file");
|
||||
|
||||
|
||||
@@ -375,6 +375,8 @@ namespace FlaxEditor.Windows.Assets
|
||||
public MaterialInstanceWindow(Editor editor, AssetItem item)
|
||||
: base(editor, item)
|
||||
{
|
||||
var inputOptions = Editor.Options.Options.Input;
|
||||
|
||||
// Undo
|
||||
_undo = new Undo();
|
||||
_undo.UndoDone += OnUndoRedo;
|
||||
@@ -384,8 +386,8 @@ namespace FlaxEditor.Windows.Assets
|
||||
// Toolstrip
|
||||
_saveButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Save64, Save).LinkTooltip("Save");
|
||||
_toolstrip.AddSeparator();
|
||||
_undoButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Undo64, _undo.PerformUndo).LinkTooltip("Undo (Ctrl+Z)");
|
||||
_redoButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Redo64, _undo.PerformRedo).LinkTooltip("Redo (Ctrl+Y)");
|
||||
_undoButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Undo64, _undo.PerformUndo).LinkTooltip($"Undo ({inputOptions.Undo})");
|
||||
_redoButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Redo64, _undo.PerformRedo).LinkTooltip($"Redo ({inputOptions.Redo})");
|
||||
_toolstrip.AddSeparator();
|
||||
_toolstrip.AddButton(Editor.Icons.Rotate64, OnRevertAllParameters).LinkTooltip("Revert all the parameters to the default values");
|
||||
_toolstrip.AddSeparator();
|
||||
|
||||
@@ -41,8 +41,6 @@ namespace FlaxEditor.Windows.Assets
|
||||
new ScriptType(typeof(Vector3)),
|
||||
new ScriptType(typeof(Vector4)),
|
||||
new ScriptType(typeof(Color)),
|
||||
new ScriptType(typeof(Quaternion)),
|
||||
new ScriptType(typeof(Transform)),
|
||||
new ScriptType(typeof(Matrix)),
|
||||
};
|
||||
|
||||
|
||||
@@ -306,6 +306,8 @@ namespace FlaxEditor.Windows.Assets
|
||||
public ParticleSystemWindow(Editor editor, AssetItem item)
|
||||
: base(editor, item)
|
||||
{
|
||||
var inputOptions = Editor.Options.Options.Input;
|
||||
|
||||
// Undo
|
||||
_undo = new Undo();
|
||||
_undo.UndoDone += OnUndoRedo;
|
||||
@@ -359,8 +361,8 @@ namespace FlaxEditor.Windows.Assets
|
||||
// Toolstrip
|
||||
_saveButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Save64, Save).LinkTooltip("Save");
|
||||
_toolstrip.AddSeparator();
|
||||
_undoButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Undo64, _undo.PerformUndo).LinkTooltip("Undo (Ctrl+Z)");
|
||||
_redoButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Redo64, _undo.PerformRedo).LinkTooltip("Redo (Ctrl+Y)");
|
||||
_undoButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Undo64, _undo.PerformUndo).LinkTooltip($"Undo ({inputOptions.Undo})");
|
||||
_redoButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Redo64, _undo.PerformRedo).LinkTooltip($"Redo ({inputOptions.Redo})");
|
||||
_toolstrip.AddSeparator();
|
||||
_toolstrip.AddButton(editor.Icons.Docs64, () => Platform.OpenUrl(Utilities.Constants.DocsUrl + "manual/particles/index.html")).LinkTooltip("See documentation to learn more");
|
||||
|
||||
|
||||
@@ -339,7 +339,7 @@ namespace FlaxEditor.Windows.Assets
|
||||
{
|
||||
if (selection.Count != 0)
|
||||
Select(actor);
|
||||
actor.TreeNode.StartRenaming(this);
|
||||
actor.TreeNode.StartRenaming(this, _treePanel);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -94,6 +94,8 @@ namespace FlaxEditor.Windows.Assets
|
||||
public PrefabWindow(Editor editor, AssetItem item)
|
||||
: base(editor, item)
|
||||
{
|
||||
var inputOptions = Editor.Options.Options.Input;
|
||||
|
||||
// Undo
|
||||
_undo = new Undo();
|
||||
_undo.UndoDone += OnUndoEvent;
|
||||
@@ -176,12 +178,12 @@ namespace FlaxEditor.Windows.Assets
|
||||
// Toolstrip
|
||||
_saveButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Save64, Save).LinkTooltip("Save");
|
||||
_toolstrip.AddSeparator();
|
||||
_toolStripUndo = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Undo64, _undo.PerformUndo).LinkTooltip("Undo (Ctrl+Z)");
|
||||
_toolStripRedo = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Redo64, _undo.PerformRedo).LinkTooltip("Redo (Ctrl+Y)");
|
||||
_toolStripUndo = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Undo64, _undo.PerformUndo).LinkTooltip($"Undo ({inputOptions.Undo})");
|
||||
_toolStripRedo = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Redo64, _undo.PerformRedo).LinkTooltip($"Redo ({inputOptions.Redo})");
|
||||
_toolstrip.AddSeparator();
|
||||
_toolStripTranslate = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Translate32, () => _viewport.TransformGizmo.ActiveMode = TransformGizmoBase.Mode.Translate).LinkTooltip("Change Gizmo tool mode to Translate (1)");
|
||||
_toolStripRotate = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Rotate32, () => _viewport.TransformGizmo.ActiveMode = TransformGizmoBase.Mode.Rotate).LinkTooltip("Change Gizmo tool mode to Rotate (2)");
|
||||
_toolStripScale = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Scale32, () => _viewport.TransformGizmo.ActiveMode = TransformGizmoBase.Mode.Scale).LinkTooltip("Change Gizmo tool mode to Scale (3)");
|
||||
_toolStripTranslate = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Translate32, () => _viewport.TransformGizmo.ActiveMode = TransformGizmoBase.Mode.Translate).LinkTooltip($"Change Gizmo tool mode to Translate ({inputOptions.TranslateMode})");
|
||||
_toolStripRotate = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Rotate32, () => _viewport.TransformGizmo.ActiveMode = TransformGizmoBase.Mode.Rotate).LinkTooltip($"Change Gizmo tool mode to Rotate ({inputOptions.RotateMode})");
|
||||
_toolStripScale = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Scale32, () => _viewport.TransformGizmo.ActiveMode = TransformGizmoBase.Mode.Scale).LinkTooltip($"Change Gizmo tool mode to Scale ({inputOptions.ScaleMode})");
|
||||
_toolstrip.AddSeparator();
|
||||
_toolStripLiveReload = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Refresh64, () => LiveReload = !LiveReload).SetChecked(true).SetAutoCheck(true).LinkTooltip("Live changes preview (applies prefab changes on modification by auto)");
|
||||
|
||||
|
||||
@@ -627,6 +627,8 @@ namespace FlaxEditor.Windows.Assets
|
||||
public SceneAnimationWindow(Editor editor, AssetItem item)
|
||||
: base(editor, item)
|
||||
{
|
||||
var inputOptions = Editor.Options.Options.Input;
|
||||
|
||||
// Undo
|
||||
_undo = new Undo();
|
||||
_undo.UndoDone += OnUndoRedo;
|
||||
@@ -652,8 +654,8 @@ namespace FlaxEditor.Windows.Assets
|
||||
// Toolstrip
|
||||
_saveButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Save64, Save).LinkTooltip("Save");
|
||||
_toolstrip.AddSeparator();
|
||||
_undoButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Undo64, _undo.PerformUndo).LinkTooltip("Undo (Ctrl+Z)");
|
||||
_redoButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Redo64, _undo.PerformRedo).LinkTooltip("Redo (Ctrl+Y)");
|
||||
_undoButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Undo64, _undo.PerformUndo).LinkTooltip($"Undo ({inputOptions.Undo})");
|
||||
_redoButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Redo64, _undo.PerformRedo).LinkTooltip($"Redo ({inputOptions.Redo})");
|
||||
_toolstrip.AddSeparator();
|
||||
_previewButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Refresh64, OnPreviewButtonClicked).SetAutoCheck(true).LinkTooltip("If checked, enables live-preview of the animation on a scene while editing");
|
||||
_renderButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Build64, OnRenderButtonClicked).LinkTooltip("Open the scene animation rendering utility...");
|
||||
|
||||
@@ -837,7 +837,7 @@ namespace FlaxEditor.Windows.Assets
|
||||
sourceAssetPicker.CheckValid = CheckSourceAssetValid;
|
||||
sourceAssetPicker.SelectedItemChanged += () =>
|
||||
{
|
||||
proxy.Setups.Add(sourceAssetPicker.SelectedAsset, new SetupProxy());
|
||||
proxy.Setups.Add(sourceAssetPicker.Validator.SelectedAsset, new SetupProxy());
|
||||
proxy.Window.MarkAsEdited();
|
||||
RebuildLayout();
|
||||
};
|
||||
@@ -856,7 +856,7 @@ namespace FlaxEditor.Windows.Assets
|
||||
|
||||
// Source asset picker
|
||||
var sourceAssetPicker = setupGroup.AddPropertyItem("Source Asset").Custom<AssetPicker>().CustomControl;
|
||||
sourceAssetPicker.SelectedAsset = sourceAsset;
|
||||
sourceAssetPicker.Validator.SelectedAsset = sourceAsset;
|
||||
sourceAssetPicker.CanEdit = false;
|
||||
sourceAssetPicker.Height = 48;
|
||||
|
||||
@@ -916,12 +916,12 @@ namespace FlaxEditor.Windows.Assets
|
||||
{
|
||||
// Show skeleton asset picker
|
||||
var sourceSkeletonPicker = setupGroup.AddPropertyItem("Skeleton", "Skinned model that contains a skeleton for this animation retargeting.").Custom<AssetPicker>().CustomControl;
|
||||
sourceSkeletonPicker.AssetType = new ScriptType(typeof(SkinnedModel));
|
||||
sourceSkeletonPicker.SelectedAsset = setup.Value.Skeleton;
|
||||
sourceSkeletonPicker.Validator.AssetType = new ScriptType(typeof(SkinnedModel));
|
||||
sourceSkeletonPicker.Validator.SelectedAsset = setup.Value.Skeleton;
|
||||
sourceSkeletonPicker.Height = 48;
|
||||
sourceSkeletonPicker.SelectedItemChanged += () =>
|
||||
{
|
||||
setup.Value.Skeleton = (SkinnedModel)sourceSkeletonPicker.SelectedAsset;
|
||||
setup.Value.Skeleton = (SkinnedModel)sourceSkeletonPicker.Validator.SelectedAsset;
|
||||
proxy.Window.MarkAsEdited();
|
||||
};
|
||||
}
|
||||
|
||||
@@ -61,6 +61,8 @@ namespace FlaxEditor.Windows.Assets
|
||||
protected VisjectFunctionSurfaceWindow(Editor editor, AssetItem item)
|
||||
: base(editor, item)
|
||||
{
|
||||
var inputOptions = Editor.Options.Options.Input;
|
||||
|
||||
// Undo
|
||||
_undo = new Undo();
|
||||
_undo.UndoDone += OnUndoRedo;
|
||||
@@ -70,10 +72,10 @@ namespace FlaxEditor.Windows.Assets
|
||||
// Toolstrip
|
||||
_saveButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Save64, Save).LinkTooltip("Save");
|
||||
_toolstrip.AddSeparator();
|
||||
_undoButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Undo64, _undo.PerformUndo).LinkTooltip("Undo (Ctrl+Z)");
|
||||
_redoButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Redo64, _undo.PerformRedo).LinkTooltip("Redo (Ctrl+Y)");
|
||||
_undoButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Undo64, _undo.PerformUndo).LinkTooltip($"Undo ({inputOptions.Undo})");
|
||||
_redoButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Redo64, _undo.PerformRedo).LinkTooltip($"Redo ({inputOptions.Redo})");
|
||||
_toolstrip.AddSeparator();
|
||||
_toolstrip.AddButton(Editor.Icons.Search64, Editor.ContentFinding.ShowSearch).LinkTooltip("Open content search tool (Ctrl+F)");
|
||||
_toolstrip.AddButton(Editor.Icons.Search64, Editor.ContentFinding.ShowSearch).LinkTooltip($"Open content search tool ({inputOptions.Search})");
|
||||
_toolstrip.AddButton(editor.Icons.CenterView64, ShowWholeGraph).LinkTooltip("Show whole graph");
|
||||
|
||||
// Panel
|
||||
|
||||
@@ -561,6 +561,7 @@ namespace FlaxEditor.Windows.Assets
|
||||
: base(editor, item)
|
||||
{
|
||||
var isPlayMode = Editor.IsPlayMode;
|
||||
var inputOptions = Editor.Options.Options.Input;
|
||||
|
||||
// Undo
|
||||
_undo = new Undo();
|
||||
@@ -598,21 +599,21 @@ namespace FlaxEditor.Windows.Assets
|
||||
// Toolstrip
|
||||
_saveButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Save64, Save).LinkTooltip("Save");
|
||||
_toolstrip.AddSeparator();
|
||||
_undoButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Undo64, _undo.PerformUndo).LinkTooltip("Undo (Ctrl+Z)");
|
||||
_redoButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Redo64, _undo.PerformRedo).LinkTooltip("Redo (Ctrl+Y)");
|
||||
_undoButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Undo64, _undo.PerformUndo).LinkTooltip($"Undo ({inputOptions.Undo})");
|
||||
_redoButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Redo64, _undo.PerformRedo).LinkTooltip($"Redo ({inputOptions.Redo})");
|
||||
_toolstrip.AddSeparator();
|
||||
_toolstrip.AddButton(Editor.Icons.Search64, Editor.ContentFinding.ShowSearch).LinkTooltip("Open content search tool (Ctrl+F)");
|
||||
_toolstrip.AddButton(Editor.Icons.Search64, Editor.ContentFinding.ShowSearch).LinkTooltip($"Open content search tool ({inputOptions.Search})");
|
||||
_toolstrip.AddButton(editor.Icons.CenterView64, ShowWholeGraph).LinkTooltip("Show whole graph");
|
||||
_toolstrip.AddSeparator();
|
||||
_toolstrip.AddButton(editor.Icons.Docs64, () => Platform.OpenUrl(Utilities.Constants.DocsUrl + "manual/scripting/visual/index.html")).LinkTooltip("See documentation to learn more");
|
||||
_debugToolstripControls = new[]
|
||||
{
|
||||
_toolstrip.AddSeparator(),
|
||||
_toolstrip.AddButton(editor.Icons.Play64, OnDebuggerContinue).LinkTooltip("Continue (F5)"),
|
||||
_toolstrip.AddButton(editor.Icons.Play64, OnDebuggerContinue).LinkTooltip($"Continue ({inputOptions.DebuggerContinue})"),
|
||||
_toolstrip.AddButton(editor.Icons.Search64, OnDebuggerNavigateToCurrentNode).LinkTooltip("Navigate to the current stack trace node"),
|
||||
_toolstrip.AddButton(editor.Icons.Right64, OnDebuggerStepOver).LinkTooltip("Step Over (F10)"),
|
||||
_toolstrip.AddButton(editor.Icons.Down64, OnDebuggerStepInto).LinkTooltip("Step Into (F11)"),
|
||||
_toolstrip.AddButton(editor.Icons.Up64, OnDebuggerStepOut).LinkTooltip("Step Out (Shift+F11)"),
|
||||
_toolstrip.AddButton(editor.Icons.Right64, OnDebuggerStepOver).LinkTooltip($"Step Over ({inputOptions.DebuggerStepOver})"),
|
||||
_toolstrip.AddButton(editor.Icons.Down64, OnDebuggerStepInto).LinkTooltip($"Step Into ({inputOptions.DebuggerStepInto})"),
|
||||
_toolstrip.AddButton(editor.Icons.Up64, OnDebuggerStepOut).LinkTooltip($"Step Out ({inputOptions.DebuggerStepOut})"),
|
||||
_toolstrip.AddButton(editor.Icons.Stop64, OnDebuggerStop).LinkTooltip("Stop debugging"),
|
||||
};
|
||||
foreach (var control in _debugToolstripControls)
|
||||
|
||||
@@ -187,12 +187,12 @@ namespace FlaxEditor.Windows
|
||||
continue;
|
||||
|
||||
// Get context proxy
|
||||
ContentProxy p;
|
||||
ContentProxy p = null;
|
||||
if (type.Type.IsSubclassOf(typeof(ContentProxy)))
|
||||
{
|
||||
p = Editor.ContentDatabase.Proxy.Find(x => x.GetType() == type.Type);
|
||||
}
|
||||
else
|
||||
else if (type.CanCreateInstance)
|
||||
{
|
||||
// User can use attribute to put their own assets into the content context menu
|
||||
var generic = typeof(SpawnableJsonAssetProxy<>).MakeGenericType(type.Type);
|
||||
|
||||
@@ -271,8 +271,6 @@ namespace FlaxEditor.Windows
|
||||
Title = "Game";
|
||||
AutoFocus = true;
|
||||
|
||||
FlaxEditor.Utilities.Utils.SetupCommonInputActions(this);
|
||||
|
||||
var task = MainRenderTask.Instance;
|
||||
|
||||
// Setup viewport
|
||||
@@ -304,6 +302,12 @@ namespace FlaxEditor.Windows
|
||||
// Link editor options
|
||||
Editor.Options.OptionsChanged += OnOptionsChanged;
|
||||
OnOptionsChanged(Editor.Options.Options);
|
||||
|
||||
InputActions.Add(options => options.TakeScreenshot, () => Screenshot.Capture(string.Empty));
|
||||
InputActions.Add(options => options.DebuggerUnlockMouse, UnlockMouseInPlay);
|
||||
InputActions.Add(options => options.ToggleFullscreen, () => { if (Editor.IsPlayMode) IsMaximized = !IsMaximized; });
|
||||
|
||||
FlaxEditor.Utilities.Utils.SetupCommonInputActions(this);
|
||||
}
|
||||
|
||||
private void ChangeViewportRatio(ViewportScaleOptions v)
|
||||
@@ -945,27 +949,6 @@ namespace FlaxEditor.Windows
|
||||
/// <inheritdoc />
|
||||
public override bool OnKeyDown(KeyboardKeys key)
|
||||
{
|
||||
switch (key)
|
||||
{
|
||||
case KeyboardKeys.F12:
|
||||
Screenshot.Capture(string.Empty);
|
||||
return true;
|
||||
case KeyboardKeys.F11:
|
||||
if (Root.GetKey(KeyboardKeys.Shift))
|
||||
{
|
||||
// Unlock mouse in game mode
|
||||
UnlockMouseInPlay();
|
||||
return true;
|
||||
}
|
||||
else if (Editor.IsPlayMode)
|
||||
{
|
||||
// Maximized game window toggle
|
||||
IsMaximized = !IsMaximized;
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Prevent closing the game window tab during a play session
|
||||
if (Editor.StateMachine.IsPlayMode && Editor.Options.Options.Input.CloseTab.Process(this, key))
|
||||
{
|
||||
|
||||
@@ -467,6 +467,7 @@ namespace FlaxEditor.Windows
|
||||
if (_isDirty)
|
||||
{
|
||||
_isDirty = false;
|
||||
var wasEmpty = _output.TextLength == 0;
|
||||
|
||||
// Cache fonts
|
||||
_output.DefaultStyle.Font.GetFont();
|
||||
@@ -589,7 +590,7 @@ namespace FlaxEditor.Windows
|
||||
// Update the output
|
||||
var cachedScrollValue = _vScroll.Value;
|
||||
var cachedSelection = _output.SelectionRange;
|
||||
var isBottomScroll = _vScroll.Value >= _vScroll.Maximum - 20.0f;
|
||||
var isBottomScroll = _vScroll.Value >= _vScroll.Maximum - 20.0f || wasEmpty;
|
||||
_output.Text = _textBuffer.ToString();
|
||||
_textBufferCount = _entries.Count;
|
||||
if (!_vScroll.IsThumbClicked)
|
||||
|
||||
@@ -142,7 +142,7 @@ namespace FlaxEditor.Windows
|
||||
{
|
||||
if (selection.Count != 0)
|
||||
Editor.SceneEditing.Select(actor);
|
||||
actor.TreeNode.StartRenaming(this);
|
||||
actor.TreeNode.StartRenaming(this, _sceneTreePanel);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -399,6 +399,8 @@ namespace FlaxEditor.Windows
|
||||
{
|
||||
Title = "Visual Script Debugger";
|
||||
|
||||
var inputOptions = editor.Options.Options.Input;
|
||||
|
||||
var toolstrip = new ToolStrip
|
||||
{
|
||||
Parent = this
|
||||
@@ -407,7 +409,7 @@ namespace FlaxEditor.Windows
|
||||
_debugToolstripControls = new[]
|
||||
{
|
||||
toolstrip.AddSeparator(),
|
||||
toolstrip.AddButton(editor.Icons.Play64, OnDebuggerContinue).LinkTooltip("Continue (F5)"),
|
||||
toolstrip.AddButton(editor.Icons.Play64, OnDebuggerContinue).LinkTooltip($"Continue ({inputOptions.DebuggerContinue})"),
|
||||
toolstrip.AddButton(editor.Icons.Search64, OnDebuggerNavigateToCurrentNode).LinkTooltip("Navigate to the current stack trace node"),
|
||||
toolstrip.AddButton(editor.Icons.Stop64, OnDebuggerStop).LinkTooltip("Stop debugging"),
|
||||
};
|
||||
|
||||
@@ -2109,7 +2109,8 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu
|
||||
bucket.LoopsLeft--;
|
||||
bucket.LoopsDone++;
|
||||
}
|
||||
value = SampleAnimation(node, loop, length, 0.0f, bucket.TimePosition, newTimePos, anim, slot.Speed);
|
||||
// Speed is accounted for in the new time pos, so keep sample speed at 1
|
||||
value = SampleAnimation(node, loop, length, 0.0f, bucket.TimePosition, newTimePos, anim, 1);
|
||||
bucket.TimePosition = newTimePos;
|
||||
if (bucket.LoopsLeft == 0 && slot.BlendOutTime > 0.0f && length - slot.BlendOutTime < bucket.TimePosition)
|
||||
{
|
||||
|
||||
@@ -16,11 +16,13 @@
|
||||
|
||||
AssetReferenceBase::~AssetReferenceBase()
|
||||
{
|
||||
if (_asset)
|
||||
Asset* asset = _asset;
|
||||
if (asset)
|
||||
{
|
||||
_asset->OnLoaded.Unbind<AssetReferenceBase, &AssetReferenceBase::OnLoaded>(this);
|
||||
_asset->OnUnloaded.Unbind<AssetReferenceBase, &AssetReferenceBase::OnUnloaded>(this);
|
||||
_asset->RemoveReference();
|
||||
_asset = nullptr;
|
||||
asset->OnLoaded.Unbind<AssetReferenceBase, &AssetReferenceBase::OnLoaded>(this);
|
||||
asset->OnUnloaded.Unbind<AssetReferenceBase, &AssetReferenceBase::OnUnloaded>(this);
|
||||
asset->RemoveReference();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,8 +72,12 @@ void AssetReferenceBase::OnUnloaded(Asset* asset)
|
||||
|
||||
WeakAssetReferenceBase::~WeakAssetReferenceBase()
|
||||
{
|
||||
if (_asset)
|
||||
_asset->OnUnloaded.Unbind<WeakAssetReferenceBase, &WeakAssetReferenceBase::OnUnloaded>(this);
|
||||
Asset* asset = _asset;
|
||||
if (asset)
|
||||
{
|
||||
_asset = nullptr;
|
||||
asset->OnUnloaded.Unbind<WeakAssetReferenceBase, &WeakAssetReferenceBase::OnUnloaded>(this);
|
||||
}
|
||||
}
|
||||
|
||||
String WeakAssetReferenceBase::ToString() const
|
||||
@@ -101,6 +107,20 @@ void WeakAssetReferenceBase::OnUnloaded(Asset* asset)
|
||||
_asset = nullptr;
|
||||
}
|
||||
|
||||
SoftAssetReferenceBase::~SoftAssetReferenceBase()
|
||||
{
|
||||
Asset* asset = _asset;
|
||||
if (asset)
|
||||
{
|
||||
_asset = nullptr;
|
||||
asset->OnUnloaded.Unbind<SoftAssetReferenceBase, &SoftAssetReferenceBase::OnUnloaded>(this);
|
||||
asset->RemoveReference();
|
||||
}
|
||||
#if !BUILD_RELEASE
|
||||
_id = Guid::Empty;
|
||||
#endif
|
||||
}
|
||||
|
||||
String SoftAssetReferenceBase::ToString() const
|
||||
{
|
||||
return _asset ? _asset->ToString() : (_id.IsValid() ? _id.ToString() : TEXT("<null>"));
|
||||
|
||||
@@ -9,9 +9,6 @@
|
||||
/// </summary>
|
||||
class FLAXENGINE_API AssetReferenceBase
|
||||
{
|
||||
public:
|
||||
typedef Delegate<> EventType;
|
||||
|
||||
protected:
|
||||
Asset* _asset = nullptr;
|
||||
|
||||
@@ -19,17 +16,17 @@ public:
|
||||
/// <summary>
|
||||
/// The asset loaded event (fired when asset gets loaded or is already loaded after change).
|
||||
/// </summary>
|
||||
EventType Loaded;
|
||||
Action Loaded;
|
||||
|
||||
/// <summary>
|
||||
/// The asset unloading event (should cleanup refs to it).
|
||||
/// </summary>
|
||||
EventType Unload;
|
||||
Action Unload;
|
||||
|
||||
/// <summary>
|
||||
/// Action fired when field gets changed (link a new asset or change to the another value).
|
||||
/// </summary>
|
||||
EventType Changed;
|
||||
Action Changed;
|
||||
|
||||
public:
|
||||
NON_COPYABLE(AssetReferenceBase);
|
||||
|
||||
@@ -1428,6 +1428,10 @@ Asset::LoadResult VisualScript::load()
|
||||
#if USE_EDITOR
|
||||
if (_instances.HasItems())
|
||||
{
|
||||
// Mark as already loaded so any WaitForLoaded checks during GetDefaultInstance bellow will handle this Visual Script as ready to use
|
||||
_loadFailed = false;
|
||||
_isLoaded = true;
|
||||
|
||||
// Setup scripting type
|
||||
CacheScriptingType();
|
||||
|
||||
@@ -1512,7 +1516,7 @@ void VisualScript::unload(bool isReloading)
|
||||
// Note: preserve the registered scripting type but invalidate the locally cached handle
|
||||
if (_scriptingTypeHandle)
|
||||
{
|
||||
VisualScriptingModule.Locker.Lock();
|
||||
VisualScriptingBinaryModule::Locker.Lock();
|
||||
auto& type = VisualScriptingModule.Types[_scriptingTypeHandle.TypeIndex];
|
||||
if (type.Script.DefaultInstance)
|
||||
{
|
||||
@@ -1523,7 +1527,7 @@ void VisualScript::unload(bool isReloading)
|
||||
VisualScriptingModule.Scripts[_scriptingTypeHandle.TypeIndex] = nullptr;
|
||||
_scriptingTypeHandleCached = _scriptingTypeHandle;
|
||||
_scriptingTypeHandle = ScriptingTypeHandle();
|
||||
VisualScriptingModule.Locker.Unlock();
|
||||
VisualScriptingBinaryModule::Locker.Unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1534,8 +1538,8 @@ AssetChunksFlag VisualScript::getChunksToPreload() const
|
||||
|
||||
void VisualScript::CacheScriptingType()
|
||||
{
|
||||
ScopeLock lock(VisualScriptingBinaryModule::Locker);
|
||||
auto& binaryModule = VisualScriptingModule;
|
||||
ScopeLock lock(binaryModule.Locker);
|
||||
|
||||
// Find base type
|
||||
const StringAnsi baseTypename(Meta.BaseTypename);
|
||||
|
||||
@@ -30,9 +30,7 @@ public:
|
||||
/// <summary>
|
||||
/// Finalizes an instance of the <see cref="SoftAssetReferenceBase"/> class.
|
||||
/// </summary>
|
||||
~SoftAssetReferenceBase()
|
||||
{
|
||||
}
|
||||
~SoftAssetReferenceBase();
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
|
||||
@@ -22,6 +22,16 @@ private:
|
||||
int32 _capacity;
|
||||
AllocationData _allocation;
|
||||
|
||||
FORCE_INLINE static int32 ToItemCount(int32 size)
|
||||
{
|
||||
return Math::DivideAndRoundUp<int32>(size, sizeof(ItemType));
|
||||
}
|
||||
|
||||
FORCE_INLINE static int32 ToItemCapacity(int32 size)
|
||||
{
|
||||
return Math::Max<int32>(Math::DivideAndRoundUp<int32>(size, sizeof(ItemType)), 1);
|
||||
}
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="BitArray"/> class.
|
||||
@@ -41,7 +51,7 @@ public:
|
||||
, _capacity(capacity)
|
||||
{
|
||||
if (capacity > 0)
|
||||
_allocation.Allocate(Math::Max<ItemType>(capacity / sizeof(ItemType), 1));
|
||||
_allocation.Allocate(ToItemCapacity(capacity));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -53,7 +63,7 @@ public:
|
||||
_count = _capacity = other.Count();
|
||||
if (_capacity > 0)
|
||||
{
|
||||
const uint64 itemsCapacity = Math::Max<ItemType>(_capacity / sizeof(ItemType), 1);
|
||||
const int32 itemsCapacity = ToItemCapacity(_capacity);
|
||||
_allocation.Allocate(itemsCapacity);
|
||||
Platform::MemoryCopy(Get(), other.Get(), itemsCapacity * sizeof(ItemType));
|
||||
}
|
||||
@@ -69,7 +79,7 @@ public:
|
||||
_count = _capacity = other.Count();
|
||||
if (_capacity > 0)
|
||||
{
|
||||
const uint64 itemsCapacity = Math::Max<ItemType>(_capacity / sizeof(ItemType), 1);
|
||||
const int32 itemsCapacity = ToItemCapacity(_capacity);
|
||||
_allocation.Allocate(itemsCapacity);
|
||||
Platform::MemoryCopy(Get(), other.Get(), itemsCapacity * sizeof(ItemType));
|
||||
}
|
||||
@@ -101,7 +111,7 @@ public:
|
||||
{
|
||||
_allocation.Free();
|
||||
_capacity = other._count;
|
||||
const uint64 itemsCapacity = Math::Max<ItemType>(_capacity / sizeof(ItemType), 1);
|
||||
const int32 itemsCapacity = ToItemCapacity(_capacity);
|
||||
_allocation.Allocate(itemsCapacity);
|
||||
Platform::MemoryCopy(Get(), other.Get(), itemsCapacity * sizeof(ItemType));
|
||||
}
|
||||
@@ -246,7 +256,7 @@ public:
|
||||
return;
|
||||
ASSERT(capacity >= 0);
|
||||
const int32 count = preserveContents ? (_count < capacity ? _count : capacity) : 0;
|
||||
_allocation.Relocate(Math::Max<ItemType>(capacity / sizeof(ItemType), 1), _count / sizeof(ItemType), count / sizeof(ItemType));
|
||||
_allocation.Relocate(ToItemCapacity(capacity), ToItemCount(_count), ToItemCount(count));
|
||||
_capacity = capacity;
|
||||
_count = count;
|
||||
}
|
||||
@@ -272,7 +282,7 @@ public:
|
||||
{
|
||||
if (_capacity < minCapacity)
|
||||
{
|
||||
const int32 capacity = _allocation.CalculateCapacityGrow(Math::Max<int32>(_capacity / sizeof(ItemType), 1), minCapacity);
|
||||
const int32 capacity = _allocation.CalculateCapacityGrow(ToItemCapacity(_capacity), minCapacity);
|
||||
SetCapacity(capacity, preserveContents);
|
||||
}
|
||||
}
|
||||
@@ -284,7 +294,7 @@ public:
|
||||
void SetAll(const bool value)
|
||||
{
|
||||
if (_count != 0)
|
||||
Platform::MemorySet(_allocation.Get(), Math::Max<ItemType>(_count / sizeof(ItemType), 1), value ? MAX_int32 : 0);
|
||||
Platform::MemorySet(_allocation.Get(), ToItemCount(_count) * sizeof(ItemType), value ? MAX_uint32 : 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -2,13 +2,26 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
/// <summary>
|
||||
/// Default capacity for the dictionaries (amount of space for the elements)
|
||||
/// </summary>
|
||||
#define DICTIONARY_DEFAULT_CAPACITY 256
|
||||
#include "Engine/Platform/Defines.h"
|
||||
|
||||
/// <summary>
|
||||
/// Function for dictionary that tells how change hash index during iteration (size param is a buckets table size)
|
||||
/// Default capacity for the dictionaries (amount of space for the elements).
|
||||
/// </summary>
|
||||
#ifndef DICTIONARY_DEFAULT_CAPACITY
|
||||
#if PLATFORM_DESKTOP
|
||||
#define DICTIONARY_DEFAULT_CAPACITY 256
|
||||
#else
|
||||
#define DICTIONARY_DEFAULT_CAPACITY 64
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Default slack space divider for the dictionaries.
|
||||
/// </summary>
|
||||
#define DICTIONARY_DEFAULT_SLACK_SCALE 3
|
||||
|
||||
/// <summary>
|
||||
/// Function for dictionary that tells how change hash index during iteration (size param is a buckets table size).
|
||||
/// </summary>
|
||||
#define DICTIONARY_PROB_FUNC(size, numChecks) (numChecks)
|
||||
//#define DICTIONARY_PROB_FUNC(size, numChecks) (1)
|
||||
|
||||
@@ -40,7 +40,7 @@ public:
|
||||
private:
|
||||
State _state;
|
||||
|
||||
void Free()
|
||||
FORCE_INLINE void Free()
|
||||
{
|
||||
if (_state == Occupied)
|
||||
{
|
||||
@@ -50,7 +50,7 @@ public:
|
||||
_state = Empty;
|
||||
}
|
||||
|
||||
void Delete()
|
||||
FORCE_INLINE void Delete()
|
||||
{
|
||||
_state = Deleted;
|
||||
Memory::DestructItem(&Key);
|
||||
@@ -58,7 +58,7 @@ public:
|
||||
}
|
||||
|
||||
template<typename KeyComparableType>
|
||||
void Occupy(const KeyComparableType& key)
|
||||
FORCE_INLINE void Occupy(const KeyComparableType& key)
|
||||
{
|
||||
Memory::ConstructItems(&Key, &key, 1);
|
||||
Memory::ConstructItem(&Value);
|
||||
@@ -66,7 +66,7 @@ public:
|
||||
}
|
||||
|
||||
template<typename KeyComparableType>
|
||||
void Occupy(const KeyComparableType& key, const ValueType& value)
|
||||
FORCE_INLINE void Occupy(const KeyComparableType& key, const ValueType& value)
|
||||
{
|
||||
Memory::ConstructItems(&Key, &key, 1);
|
||||
Memory::ConstructItems(&Value, &value, 1);
|
||||
@@ -74,7 +74,7 @@ public:
|
||||
}
|
||||
|
||||
template<typename KeyComparableType>
|
||||
void Occupy(const KeyComparableType& key, ValueType&& value)
|
||||
FORCE_INLINE void Occupy(const KeyComparableType& key, ValueType&& value)
|
||||
{
|
||||
Memory::ConstructItems(&Key, &key, 1);
|
||||
Memory::MoveItems(&Value, &value, 1);
|
||||
@@ -132,9 +132,6 @@ public:
|
||||
/// </summary>
|
||||
/// <param name="other">The other collection to move.</param>
|
||||
Dictionary(Dictionary&& other) noexcept
|
||||
: _elementsCount(other._elementsCount)
|
||||
, _deletedCount(other._deletedCount)
|
||||
, _size(other._size)
|
||||
{
|
||||
_elementsCount = other._elementsCount;
|
||||
_deletedCount = other._deletedCount;
|
||||
@@ -375,8 +372,12 @@ public:
|
||||
template<typename KeyComparableType>
|
||||
ValueType& At(const KeyComparableType& key)
|
||||
{
|
||||
// Check if need to rehash elements (prevent many deleted elements that use too much of capacity)
|
||||
if (_deletedCount > _size / DICTIONARY_DEFAULT_SLACK_SCALE)
|
||||
Compact();
|
||||
|
||||
// Ensure to have enough memory for the next item (in case of new element insertion)
|
||||
EnsureCapacity(_elementsCount + _deletedCount + 1);
|
||||
EnsureCapacity((_elementsCount + 1) * DICTIONARY_DEFAULT_SLACK_SCALE + _deletedCount);
|
||||
|
||||
// Find location of the item or place to insert it
|
||||
FindPositionResult pos;
|
||||
@@ -388,9 +389,9 @@ public:
|
||||
|
||||
// Insert
|
||||
ASSERT(pos.FreeSlotIndex != -1);
|
||||
_elementsCount++;
|
||||
Bucket& bucket = _allocation.Get()[pos.FreeSlotIndex];
|
||||
bucket.Occupy(key);
|
||||
_elementsCount++;
|
||||
return bucket.Value;
|
||||
}
|
||||
|
||||
@@ -493,7 +494,7 @@ public:
|
||||
for (Iterator i = Begin(); i.IsNotEnd(); ++i)
|
||||
{
|
||||
if (i->Value)
|
||||
Delete(i->Value);
|
||||
::Delete(i->Value);
|
||||
}
|
||||
Clear();
|
||||
}
|
||||
@@ -533,13 +534,22 @@ public:
|
||||
}
|
||||
_size = capacity;
|
||||
Bucket* oldData = oldAllocation.Get();
|
||||
if (oldElementsCount != 0 && preserveContents)
|
||||
if (oldElementsCount != 0 && capacity != 0 && preserveContents)
|
||||
{
|
||||
// TODO; move keys and values on realloc
|
||||
FindPositionResult pos;
|
||||
for (int32 i = 0; i < oldSize; i++)
|
||||
{
|
||||
if (oldData[i].IsOccupied())
|
||||
Add(oldData[i].Key, MoveTemp(oldData[i].Value));
|
||||
Bucket& oldBucket = oldData[i];
|
||||
if (oldBucket.IsOccupied())
|
||||
{
|
||||
FindPosition(oldBucket.Key, pos);
|
||||
ASSERT(pos.FreeSlotIndex != -1);
|
||||
Bucket* bucket = &_allocation.Get()[pos.FreeSlotIndex];
|
||||
Memory::MoveItems(&bucket->Key, &oldBucket.Key, 1);
|
||||
Memory::MoveItems(&bucket->Value, &oldBucket.Value, 1);
|
||||
bucket->_state = Bucket::Occupied;
|
||||
_elementsCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (oldElementsCount != 0)
|
||||
@@ -558,9 +568,9 @@ public:
|
||||
{
|
||||
if (_size >= minCapacity)
|
||||
return;
|
||||
if (minCapacity < DICTIONARY_DEFAULT_CAPACITY)
|
||||
minCapacity = DICTIONARY_DEFAULT_CAPACITY;
|
||||
const int32 capacity = _allocation.CalculateCapacityGrow(_size, minCapacity);
|
||||
int32 capacity = _allocation.CalculateCapacityGrow(_size, minCapacity);
|
||||
if (capacity < DICTIONARY_DEFAULT_CAPACITY)
|
||||
capacity = DICTIONARY_DEFAULT_CAPACITY;
|
||||
SetCapacity(capacity, preserveContents);
|
||||
}
|
||||
|
||||
@@ -584,24 +594,10 @@ public:
|
||||
/// <param name="value">The value.</param>
|
||||
/// <returns>Weak reference to the stored bucket.</returns>
|
||||
template<typename KeyComparableType>
|
||||
Bucket* Add(const KeyComparableType& key, const ValueType& value)
|
||||
FORCE_INLINE Bucket* Add(const KeyComparableType& key, const ValueType& value)
|
||||
{
|
||||
// Ensure to have enough memory for the next item (in case of new element insertion)
|
||||
EnsureCapacity(_elementsCount + _deletedCount + 1);
|
||||
|
||||
// Find location of the item or place to insert it
|
||||
FindPositionResult pos;
|
||||
FindPosition(key, pos);
|
||||
|
||||
// Ensure key is unknown
|
||||
ASSERT(pos.ObjectIndex == -1 && "That key has been already added to the dictionary.");
|
||||
|
||||
// Insert
|
||||
ASSERT(pos.FreeSlotIndex != -1);
|
||||
Bucket* bucket = &_allocation.Get()[pos.FreeSlotIndex];
|
||||
Bucket* bucket = OnAdd(key);
|
||||
bucket->Occupy(key, value);
|
||||
_elementsCount++;
|
||||
|
||||
return bucket;
|
||||
}
|
||||
|
||||
@@ -612,24 +608,10 @@ public:
|
||||
/// <param name="value">The value.</param>
|
||||
/// <returns>Weak reference to the stored bucket.</returns>
|
||||
template<typename KeyComparableType>
|
||||
Bucket* Add(const KeyComparableType& key, ValueType&& value)
|
||||
FORCE_INLINE Bucket* Add(const KeyComparableType& key, ValueType&& value)
|
||||
{
|
||||
// Ensure to have enough memory for the next item (in case of new element insertion)
|
||||
EnsureCapacity(_elementsCount + _deletedCount + 1);
|
||||
|
||||
// Find location of the item or place to insert it
|
||||
FindPositionResult pos;
|
||||
FindPosition(key, pos);
|
||||
|
||||
// Ensure key is unknown
|
||||
ASSERT(pos.ObjectIndex == -1 && "That key has been already added to the dictionary.");
|
||||
|
||||
// Insert
|
||||
ASSERT(pos.FreeSlotIndex != -1);
|
||||
Bucket* bucket = &_allocation.Get()[pos.FreeSlotIndex];
|
||||
Bucket* bucket = OnAdd(key);
|
||||
bucket->Occupy(key, MoveTemp(value));
|
||||
_elementsCount++;
|
||||
|
||||
return bucket;
|
||||
}
|
||||
|
||||
@@ -851,7 +833,7 @@ public:
|
||||
return Iterator(this, _size);
|
||||
}
|
||||
|
||||
protected:
|
||||
private:
|
||||
/// <summary>
|
||||
/// The result container of the dictionary item lookup searching.
|
||||
/// </summary>
|
||||
@@ -911,4 +893,66 @@ protected:
|
||||
result.ObjectIndex = -1;
|
||||
result.FreeSlotIndex = insertPos;
|
||||
}
|
||||
|
||||
template<typename KeyComparableType>
|
||||
Bucket* OnAdd(const KeyComparableType& key)
|
||||
{
|
||||
// Check if need to rehash elements (prevent many deleted elements that use too much of capacity)
|
||||
if (_deletedCount > _size / DICTIONARY_DEFAULT_SLACK_SCALE)
|
||||
Compact();
|
||||
|
||||
// Ensure to have enough memory for the next item (in case of new element insertion)
|
||||
EnsureCapacity((_elementsCount + 1) * DICTIONARY_DEFAULT_SLACK_SCALE + _deletedCount);
|
||||
|
||||
// Find location of the item or place to insert it
|
||||
FindPositionResult pos;
|
||||
FindPosition(key, pos);
|
||||
|
||||
// Ensure key is unknown
|
||||
ASSERT(pos.ObjectIndex == -1 && "That key has been already added to the dictionary.");
|
||||
|
||||
// Insert
|
||||
ASSERT(pos.FreeSlotIndex != -1);
|
||||
_elementsCount++;
|
||||
return &_allocation.Get()[pos.FreeSlotIndex];
|
||||
}
|
||||
|
||||
void Compact()
|
||||
{
|
||||
if (_elementsCount == 0)
|
||||
{
|
||||
// Fast path if it's empty
|
||||
Bucket* data = _allocation.Get();
|
||||
for (int32 i = 0; i < _size; i++)
|
||||
data[i]._state = Bucket::Empty;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Rebuild entire table completely
|
||||
AllocationData oldAllocation;
|
||||
oldAllocation.Swap(_allocation);
|
||||
_allocation.Allocate(_size);
|
||||
Bucket* data = _allocation.Get();
|
||||
for (int32 i = 0; i < _size; i++)
|
||||
data[i]._state = Bucket::Empty;
|
||||
Bucket* oldData = oldAllocation.Get();
|
||||
FindPositionResult pos;
|
||||
for (int32 i = 0; i < _size; i++)
|
||||
{
|
||||
Bucket& oldBucket = oldData[i];
|
||||
if (oldBucket.IsOccupied())
|
||||
{
|
||||
FindPosition(oldBucket.Key, pos);
|
||||
ASSERT(pos.FreeSlotIndex != -1);
|
||||
Bucket* bucket = &_allocation.Get()[pos.FreeSlotIndex];
|
||||
Memory::MoveItems(&bucket->Key, &oldBucket.Key, 1);
|
||||
Memory::MoveItems(&bucket->Value, &oldBucket.Value, 1);
|
||||
bucket->_state = Bucket::Occupied;
|
||||
}
|
||||
}
|
||||
for (int32 i = 0; i < _size; i++)
|
||||
oldData[i].Free();
|
||||
}
|
||||
_deletedCount = 0;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -37,26 +37,33 @@ public:
|
||||
private:
|
||||
State _state;
|
||||
|
||||
void Free()
|
||||
FORCE_INLINE void Free()
|
||||
{
|
||||
if (_state == Occupied)
|
||||
Memory::DestructItem(&Item);
|
||||
_state = Empty;
|
||||
}
|
||||
|
||||
void Delete()
|
||||
FORCE_INLINE void Delete()
|
||||
{
|
||||
_state = Deleted;
|
||||
Memory::DestructItem(&Item);
|
||||
}
|
||||
|
||||
template<typename ItemType>
|
||||
void Occupy(const ItemType& item)
|
||||
FORCE_INLINE void Occupy(const ItemType& item)
|
||||
{
|
||||
Memory::ConstructItems(&Item, &item, 1);
|
||||
_state = Occupied;
|
||||
}
|
||||
|
||||
template<typename ItemType>
|
||||
FORCE_INLINE void Occupy(ItemType& item)
|
||||
{
|
||||
Memory::MoveItems(&Item, &item, 1);
|
||||
_state = Occupied;
|
||||
}
|
||||
|
||||
FORCE_INLINE bool IsEmpty() const
|
||||
{
|
||||
return _state == Empty;
|
||||
@@ -82,6 +89,7 @@ public:
|
||||
|
||||
private:
|
||||
int32 _elementsCount = 0;
|
||||
int32 _deletedCount = 0;
|
||||
int32 _size = 0;
|
||||
AllocationData _allocation;
|
||||
|
||||
@@ -107,12 +115,12 @@ public:
|
||||
/// </summary>
|
||||
/// <param name="other">The other collection to move.</param>
|
||||
HashSet(HashSet&& other) noexcept
|
||||
: _elementsCount(other._elementsCount)
|
||||
, _size(other._size)
|
||||
{
|
||||
_elementsCount = other._elementsCount;
|
||||
_deletedCount = other._deletedCount;
|
||||
_size = other._size;
|
||||
other._elementsCount = 0;
|
||||
other._deletedCount = 0;
|
||||
other._size = 0;
|
||||
_allocation.Swap(other._allocation);
|
||||
}
|
||||
@@ -150,8 +158,10 @@ public:
|
||||
Clear();
|
||||
_allocation.Free();
|
||||
_elementsCount = other._elementsCount;
|
||||
_deletedCount = other._deletedCount;
|
||||
_size = other._size;
|
||||
other._elementsCount = 0;
|
||||
other._deletedCount = 0;
|
||||
other._size = 0;
|
||||
_allocation.Swap(other._allocation);
|
||||
}
|
||||
@@ -163,7 +173,7 @@ public:
|
||||
/// </summary>
|
||||
~HashSet()
|
||||
{
|
||||
SetCapacity(0, false);
|
||||
Clear();
|
||||
}
|
||||
|
||||
public:
|
||||
@@ -210,6 +220,7 @@ public:
|
||||
HashSet* _collection;
|
||||
int32 _index;
|
||||
|
||||
public:
|
||||
Iterator(HashSet* collection, const int32 index)
|
||||
: _collection(collection)
|
||||
, _index(index)
|
||||
@@ -222,7 +233,12 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
Iterator()
|
||||
: _collection(nullptr)
|
||||
, _index(-1)
|
||||
{
|
||||
}
|
||||
|
||||
Iterator(const Iterator& i)
|
||||
: _collection(i._collection)
|
||||
, _index(i._index)
|
||||
@@ -236,6 +252,11 @@ public:
|
||||
}
|
||||
|
||||
public:
|
||||
FORCE_INLINE int32 Index() const
|
||||
{
|
||||
return _index;
|
||||
}
|
||||
|
||||
FORCE_INLINE bool IsEnd() const
|
||||
{
|
||||
return _index == _collection->_size;
|
||||
@@ -331,12 +352,12 @@ public:
|
||||
/// </summary>
|
||||
void Clear()
|
||||
{
|
||||
if (_elementsCount != 0)
|
||||
if (_elementsCount + _deletedCount != 0)
|
||||
{
|
||||
Bucket* data = _allocation.Get();
|
||||
for (int32 i = 0; i < _size; i++)
|
||||
data[i].Free();
|
||||
_elementsCount = 0;
|
||||
_elementsCount = _deletedCount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -371,7 +392,7 @@ public:
|
||||
oldAllocation.Swap(_allocation);
|
||||
const int32 oldSize = _size;
|
||||
const int32 oldElementsCount = _elementsCount;
|
||||
_elementsCount = 0;
|
||||
_deletedCount = _elementsCount = 0;
|
||||
if (capacity != 0 && (capacity & (capacity - 1)) != 0)
|
||||
{
|
||||
// Align capacity value to the next power of two (http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2)
|
||||
@@ -392,13 +413,21 @@ public:
|
||||
}
|
||||
_size = capacity;
|
||||
Bucket* oldData = oldAllocation.Get();
|
||||
if (oldElementsCount != 0 && preserveContents)
|
||||
if (oldElementsCount != 0 && capacity != 0 && preserveContents)
|
||||
{
|
||||
// TODO; move keys and values on realloc
|
||||
FindPositionResult pos;
|
||||
for (int32 i = 0; i < oldSize; i++)
|
||||
{
|
||||
if (oldData[i].IsOccupied())
|
||||
Add(oldData[i].Item);
|
||||
Bucket& oldBucket = oldData[i];
|
||||
if (oldBucket.IsOccupied())
|
||||
{
|
||||
FindPosition(oldBucket.Item, pos);
|
||||
ASSERT(pos.FreeSlotIndex != -1);
|
||||
Bucket* bucket = &_allocation.Get()[pos.FreeSlotIndex];
|
||||
Memory::MoveItems(&bucket->Item, &oldBucket.Item, 1);
|
||||
bucket->_state = Bucket::Occupied;
|
||||
_elementsCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (oldElementsCount != 0)
|
||||
@@ -415,14 +444,26 @@ public:
|
||||
/// <param name="preserveContents">True if preserve collection data when changing its size, otherwise collection after resize will be empty.</param>
|
||||
void EnsureCapacity(int32 minCapacity, bool preserveContents = true)
|
||||
{
|
||||
if (Capacity() >= minCapacity)
|
||||
if (_size >= minCapacity)
|
||||
return;
|
||||
if (minCapacity < DICTIONARY_DEFAULT_CAPACITY)
|
||||
minCapacity = DICTIONARY_DEFAULT_CAPACITY;
|
||||
const int32 capacity = _allocation.CalculateCapacityGrow(_size, minCapacity);
|
||||
int32 capacity = _allocation.CalculateCapacityGrow(_size, minCapacity);
|
||||
if (capacity < DICTIONARY_DEFAULT_CAPACITY)
|
||||
capacity = DICTIONARY_DEFAULT_CAPACITY;
|
||||
SetCapacity(capacity, preserveContents);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Swaps the contents of collection with the other object without copy operation. Performs fast internal data exchange.
|
||||
/// </summary>
|
||||
/// <param name="other">The other collection.</param>
|
||||
void Swap(HashSet& other)
|
||||
{
|
||||
::Swap(_elementsCount, other._elementsCount);
|
||||
::Swap(_deletedCount, other._deletedCount);
|
||||
::Swap(_size, other._size);
|
||||
_allocation.Swap(other._allocation);
|
||||
}
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
/// Add element to the collection.
|
||||
@@ -432,24 +473,23 @@ public:
|
||||
template<typename ItemType>
|
||||
bool Add(const ItemType& item)
|
||||
{
|
||||
// Ensure to have enough memory for the next item (in case of new element insertion)
|
||||
EnsureCapacity(_elementsCount + 1);
|
||||
Bucket* bucket = OnAdd(item);
|
||||
if (bucket)
|
||||
bucket->Occupy(item);
|
||||
return bucket != nullptr;
|
||||
}
|
||||
|
||||
// Find location of the item or place to insert it
|
||||
FindPositionResult pos;
|
||||
FindPosition(item, pos);
|
||||
|
||||
// Check if object has been already added
|
||||
if (pos.ObjectIndex != -1)
|
||||
return false;
|
||||
|
||||
// Insert
|
||||
ASSERT(pos.FreeSlotIndex != -1);
|
||||
Bucket* bucket = &_allocation.Get()[pos.FreeSlotIndex];
|
||||
bucket->Occupy(item);
|
||||
_elementsCount++;
|
||||
|
||||
return true;
|
||||
/// <summary>
|
||||
/// Add element to the collection.
|
||||
/// </summary>
|
||||
/// <param name="item">The element to add to the set.</param>
|
||||
/// <returns>True if element has been added to the collection, otherwise false if the element is already present.</returns>
|
||||
bool Add(T&& item)
|
||||
{
|
||||
Bucket* bucket = OnAdd(item);
|
||||
if (bucket)
|
||||
bucket->Occupy(MoveTemp(item));
|
||||
return bucket != nullptr;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -479,6 +519,7 @@ public:
|
||||
{
|
||||
_allocation.Get()[pos.ObjectIndex].Delete();
|
||||
_elementsCount--;
|
||||
_deletedCount++;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -497,6 +538,7 @@ public:
|
||||
ASSERT(_allocation.Get()[i._index].IsOccupied());
|
||||
_allocation.Get()[i._index].Delete();
|
||||
_elementsCount--;
|
||||
_deletedCount++;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -585,7 +627,7 @@ public:
|
||||
return Iterator(this, _size);
|
||||
}
|
||||
|
||||
protected:
|
||||
private:
|
||||
/// <summary>
|
||||
/// The result container of the set item lookup searching.
|
||||
/// </summary>
|
||||
@@ -646,4 +688,66 @@ protected:
|
||||
result.ObjectIndex = -1;
|
||||
result.FreeSlotIndex = insertPos;
|
||||
}
|
||||
|
||||
template<typename ItemType>
|
||||
Bucket* OnAdd(const ItemType& key)
|
||||
{
|
||||
// Check if need to rehash elements (prevent many deleted elements that use too much of capacity)
|
||||
if (_deletedCount > _size / DICTIONARY_DEFAULT_SLACK_SCALE)
|
||||
Compact();
|
||||
|
||||
// Ensure to have enough memory for the next item (in case of new element insertion)
|
||||
EnsureCapacity((_elementsCount + 1) * DICTIONARY_DEFAULT_SLACK_SCALE + _deletedCount);
|
||||
|
||||
// Find location of the item or place to insert it
|
||||
FindPositionResult pos;
|
||||
FindPosition(key, pos);
|
||||
|
||||
// Check if object has been already added
|
||||
if (pos.ObjectIndex != -1)
|
||||
return nullptr;
|
||||
|
||||
// Insert
|
||||
ASSERT(pos.FreeSlotIndex != -1);
|
||||
_elementsCount++;
|
||||
return &_allocation.Get()[pos.FreeSlotIndex];
|
||||
}
|
||||
|
||||
void Compact()
|
||||
{
|
||||
if (_elementsCount == 0)
|
||||
{
|
||||
// Fast path if it's empty
|
||||
Bucket* data = _allocation.Get();
|
||||
for (int32 i = 0; i < _size; i++)
|
||||
data[i]._state = Bucket::Empty;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Rebuild entire table completely
|
||||
AllocationData oldAllocation;
|
||||
oldAllocation.Swap(_allocation);
|
||||
_allocation.Allocate(_size);
|
||||
Bucket* data = _allocation.Get();
|
||||
for (int32 i = 0; i < _size; i++)
|
||||
data[i]._state = Bucket::Empty;
|
||||
Bucket* oldData = oldAllocation.Get();
|
||||
FindPositionResult pos;
|
||||
for (int32 i = 0; i < _size; i++)
|
||||
{
|
||||
Bucket& oldBucket = oldData[i];
|
||||
if (oldBucket.IsOccupied())
|
||||
{
|
||||
FindPosition(oldBucket.Item, pos);
|
||||
ASSERT(pos.FreeSlotIndex != -1);
|
||||
Bucket* bucket = &_allocation.Get()[pos.FreeSlotIndex];
|
||||
Memory::MoveItems(&bucket->Item, &oldBucket.Item, 1);
|
||||
bucket->_state = Bucket::Occupied;
|
||||
}
|
||||
}
|
||||
for (int32 i = 0; i < _size; i++)
|
||||
oldData[i].Free();
|
||||
}
|
||||
_deletedCount = 0;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -226,7 +226,7 @@ public:
|
||||
/// <returns>Function result</returns>
|
||||
FORCE_INLINE ReturnType operator()(Params... params) const
|
||||
{
|
||||
ASSERT(_function);
|
||||
ASSERT_LOW_LAYER(_function);
|
||||
return _function(_callee, Forward<Params>(params)...);
|
||||
}
|
||||
|
||||
@@ -289,8 +289,13 @@ protected:
|
||||
intptr volatile _ptr = 0;
|
||||
intptr volatile _size = 0;
|
||||
#else
|
||||
HashSet<FunctionType>* _functions = nullptr;
|
||||
CriticalSection* _locker = nullptr;
|
||||
struct Data
|
||||
{
|
||||
HashSet<FunctionType> Functions;
|
||||
CriticalSection Locker;
|
||||
};
|
||||
// Holds pointer to Data with Functions and Locker. Thread-safe access via atomic operations.
|
||||
intptr volatile _data = 0;
|
||||
#endif
|
||||
typedef void (*StubSignature)(void*, Params...);
|
||||
|
||||
@@ -314,15 +319,12 @@ public:
|
||||
_ptr = (intptr)newBindings;
|
||||
_size = newSize;
|
||||
#else
|
||||
if (other._functions == nullptr)
|
||||
Data* otherData = (Data*)Platform::AtomicRead(&_data);
|
||||
if (otherData == nullptr)
|
||||
return;
|
||||
_functions = New<HashSet<FunctionType>>(*other._functions);
|
||||
for (auto i = _functions->Begin(); i.IsNotEnd(); ++i)
|
||||
{
|
||||
if (i->Item._function && i->Item._lambda)
|
||||
i->Item.LambdaCtor();
|
||||
}
|
||||
_locker = other._locker;
|
||||
ScopeLock lock(otherData->Locker);
|
||||
for (auto i = otherData->Functions.Begin(); i.IsNotEnd(); ++i)
|
||||
Bind(i->Item);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -334,10 +336,8 @@ public:
|
||||
other._ptr = 0;
|
||||
other._size = 0;
|
||||
#else
|
||||
_functions = other._functions;
|
||||
_locker = other._locker;
|
||||
other._functions = nullptr;
|
||||
other._locker = nullptr;
|
||||
_data = other._data;
|
||||
other._data = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -356,20 +356,11 @@ public:
|
||||
Allocator::Free((void*)_ptr);
|
||||
}
|
||||
#else
|
||||
if (_locker != nullptr)
|
||||
Data* data = (Data*)_data;
|
||||
if (data)
|
||||
{
|
||||
Allocator::Free(_locker);
|
||||
_locker = nullptr;
|
||||
}
|
||||
if (_functions != nullptr)
|
||||
{
|
||||
for (auto i = _functions->Begin(); i.IsNotEnd(); ++i)
|
||||
{
|
||||
if (i->Item._lambda)
|
||||
i->Item.LambdaCtor();
|
||||
}
|
||||
Allocator::Free(_functions);
|
||||
_functions = nullptr;
|
||||
_data = 0;
|
||||
Delete(data);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -385,8 +376,13 @@ public:
|
||||
for (intptr i = 0; i < size; i++)
|
||||
Bind(bindings[i]);
|
||||
#else
|
||||
for (auto i = other._functions->Begin(); i.IsNotEnd(); ++i)
|
||||
Bind(i->Item);
|
||||
Data* otherData = (Data*)Platform::AtomicRead(&_data);
|
||||
if (otherData != nullptr)
|
||||
{
|
||||
ScopeLock lock(otherData->Locker);
|
||||
for (auto i = otherData->Functions.Begin(); i.IsNotEnd(); ++i)
|
||||
Bind(i->Item);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
return *this;
|
||||
@@ -402,10 +398,8 @@ public:
|
||||
other._ptr = 0;
|
||||
other._size = 0;
|
||||
#else
|
||||
_functions = other._functions;
|
||||
_locker = other._locker;
|
||||
other._functions = nullptr;
|
||||
other._locker = nullptr;
|
||||
_data = other._data;
|
||||
other._data = 0;
|
||||
#endif
|
||||
}
|
||||
return *this;
|
||||
@@ -507,12 +501,20 @@ public:
|
||||
Allocator::Free(bindings);
|
||||
}
|
||||
#else
|
||||
if (_locker == nullptr)
|
||||
_locker = New<CriticalSection>();
|
||||
ScopeLock lock(*_locker);
|
||||
if (_functions == nullptr)
|
||||
_functions = New<HashSet<FunctionType>>();
|
||||
_functions->Add(f);
|
||||
Data* data = (Data*)Platform::AtomicRead(&_data);
|
||||
while (!data)
|
||||
{
|
||||
Data* newData = New<Data>();
|
||||
Data* oldData = (Data*)Platform::InterlockedCompareExchange(&_data, (intptr)newData, (intptr)data);
|
||||
if (oldData != data)
|
||||
{
|
||||
// Other thread already set the new data so free it and try again
|
||||
Delete(newData);
|
||||
}
|
||||
data = (Data*)Platform::AtomicRead(&_data);
|
||||
}
|
||||
ScopeLock lock(data->Locker);
|
||||
data->Functions.Add(f);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -568,13 +570,22 @@ public:
|
||||
}
|
||||
}
|
||||
#else
|
||||
if (_locker == nullptr)
|
||||
_locker = New<CriticalSection>();
|
||||
ScopeLock lock(*_locker);
|
||||
if (_functions && _functions->Contains(f))
|
||||
return;
|
||||
Data* data = (Data*)Platform::AtomicRead(&_data);
|
||||
if (data)
|
||||
{
|
||||
data->Locker.Lock();
|
||||
if (data->Functions.Contains(f))
|
||||
{
|
||||
data->Locker.Unlock();
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
Bind(f);
|
||||
#if !DELEGATE_USE_ATOMIC
|
||||
if (data)
|
||||
data->Locker.Unlock();
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -640,10 +651,11 @@ public:
|
||||
Unbind(f);
|
||||
}
|
||||
#else
|
||||
if (_functions == nullptr)
|
||||
Data* data = (Data*)Platform::AtomicRead(&_data);
|
||||
if (!data)
|
||||
return;
|
||||
ScopeLock lock(*_locker);
|
||||
_functions->Remove(f);
|
||||
ScopeLock lock(data->Locker);
|
||||
data->Functions.Remove(f);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -666,15 +678,11 @@ public:
|
||||
Platform::AtomicStore((intptr volatile*)&bindings[i]._callee, 0);
|
||||
}
|
||||
#else
|
||||
if (_functions == nullptr)
|
||||
Data* data = (Data*)Platform::AtomicRead(&_data);
|
||||
if (!data)
|
||||
return;
|
||||
ScopeLock lock(*_locker);
|
||||
for (auto i = _functions->Begin(); i.IsNotEnd(); ++i)
|
||||
{
|
||||
if (i->Item._lambda)
|
||||
i->Item.LambdaDtor();
|
||||
}
|
||||
_functions->Clear();
|
||||
ScopeLock lock(data->Locker);
|
||||
data->Functions.Clear();
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -684,22 +692,24 @@ public:
|
||||
/// <returns>The bound functions count.</returns>
|
||||
int32 Count() const
|
||||
{
|
||||
int32 result = 0;
|
||||
#if DELEGATE_USE_ATOMIC
|
||||
int32 count = 0;
|
||||
const intptr size = Platform::AtomicRead((intptr volatile*)&_size);
|
||||
FunctionType* bindings = (FunctionType*)Platform::AtomicRead((intptr volatile*)&_ptr);
|
||||
for (intptr i = 0; i < size; i++)
|
||||
{
|
||||
if (Platform::AtomicRead((intptr volatile*)&bindings[i]._function) != 0)
|
||||
count++;
|
||||
result++;
|
||||
}
|
||||
return count;
|
||||
#else
|
||||
if (_functions == nullptr)
|
||||
return 0;
|
||||
ScopeLock lock(*_locker);
|
||||
return _functions->Count();
|
||||
Data* data = (Data*)Platform::AtomicRead((intptr volatile*)&_data);
|
||||
if (data)
|
||||
{
|
||||
ScopeLock lock(data->Locker);
|
||||
result = data->Functions.Count();
|
||||
}
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -710,10 +720,14 @@ public:
|
||||
#if DELEGATE_USE_ATOMIC
|
||||
return (int32)Platform::AtomicRead((intptr volatile*)&_size);
|
||||
#else
|
||||
if (_functions == nullptr)
|
||||
return 0;
|
||||
ScopeLock lock(*_locker);
|
||||
return _functions->Capacity();
|
||||
int32 result = 0;
|
||||
Data* data = (Data*)Platform::AtomicRead((intptr volatile*)&_data);
|
||||
if (data)
|
||||
{
|
||||
ScopeLock lock(data->Locker);
|
||||
result = data->Functions.Capacity();
|
||||
}
|
||||
return result;
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -733,10 +747,14 @@ public:
|
||||
}
|
||||
return false;
|
||||
#else
|
||||
if (_functions == nullptr)
|
||||
return false;
|
||||
ScopeLock lock(*_locker);
|
||||
return _functions->Count() > 0;
|
||||
bool result = false;
|
||||
Data* data = (Data*)Platform::AtomicRead((intptr volatile*)&_data);
|
||||
if (data)
|
||||
{
|
||||
ScopeLock lock(data->Locker);
|
||||
result = data->Functions.Count() != 0;
|
||||
}
|
||||
return result;
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -765,18 +783,13 @@ public:
|
||||
}
|
||||
}
|
||||
#else
|
||||
if (_functions == nullptr)
|
||||
return 0;
|
||||
ScopeLock lock(*_locker);
|
||||
for (auto i = _functions->Begin(); i.IsNotEnd(); ++i)
|
||||
Data* data = (Data*)Platform::AtomicRead((intptr volatile*)&_data);
|
||||
if (data)
|
||||
{
|
||||
if (i->Item._function != nullptr)
|
||||
ScopeLock lock(data->Locker);
|
||||
for (auto i = data->Functions.Begin(); i.IsNotEnd(); ++i)
|
||||
{
|
||||
buffer[count]._function = (StubSignature)i->Item._function;
|
||||
buffer[count]._callee = (void*)i->Item._callee;
|
||||
buffer[count]._lambda = (typename FunctionType::Lambda*)i->Item._lambda;
|
||||
if (buffer[count]._lambda)
|
||||
buffer[count].LambdaCtor();
|
||||
new(buffer + count) FunctionType((const FunctionType&)i->Item);
|
||||
count++;
|
||||
}
|
||||
}
|
||||
@@ -802,15 +815,15 @@ public:
|
||||
++bindings;
|
||||
}
|
||||
#else
|
||||
if (_functions == nullptr)
|
||||
Data* data = (Data*)Platform::AtomicRead((intptr volatile*)&_data);
|
||||
if (!data)
|
||||
return;
|
||||
ScopeLock lock(*_locker);
|
||||
for (auto i = _functions->Begin(); i.IsNotEnd(); ++i)
|
||||
ScopeLock lock(data->Locker);
|
||||
for (auto i = data->Functions.Begin(); i.IsNotEnd(); ++i)
|
||||
{
|
||||
auto function = (StubSignature)(i->Item._function);
|
||||
auto callee = (void*)(i->Item._callee);
|
||||
if (function != nullptr)
|
||||
function(callee, Forward<Params>(params)...);
|
||||
const FunctionType& item = i->Item;
|
||||
ASSERT_LOW_LAYER(item._function);
|
||||
item._function(item._callee, Forward<Params>(params)...);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -382,9 +382,8 @@ void Quaternion::GetRotationFromTo(const Float3& from, const Float3& to, Quatern
|
||||
v0.Normalize();
|
||||
v1.Normalize();
|
||||
|
||||
const float d = Float3::Dot(v0, v1);
|
||||
|
||||
// If dot == 1, vectors are the same
|
||||
const float d = Float3::Dot(v0, v1);
|
||||
if (d >= 1.0f)
|
||||
{
|
||||
result = Identity;
|
||||
|
||||
@@ -1077,6 +1077,115 @@ namespace FlaxEngine
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the shortest arc quaternion to rotate this vector to the destination vector.
|
||||
/// </summary>
|
||||
/// <param name="from">The source vector.</param>
|
||||
/// <param name="to">The destination vector.</param>
|
||||
/// <param name="result">The result.</param>
|
||||
/// <param name="fallbackAxis">The fallback axis.</param>
|
||||
public static void GetRotationFromTo(ref Float3 from, ref Float3 to, out Quaternion result, ref Float3 fallbackAxis)
|
||||
{
|
||||
// Based on Stan Melax's article in Game Programming Gems
|
||||
|
||||
Float3 v0 = from;
|
||||
Float3 v1 = to;
|
||||
v0.Normalize();
|
||||
v1.Normalize();
|
||||
|
||||
// If dot == 1, vectors are the same
|
||||
float d = Float3.Dot(ref v0, ref v1);
|
||||
if (d >= 1.0f)
|
||||
{
|
||||
result = Identity;
|
||||
return;
|
||||
}
|
||||
|
||||
if (d < 1e-6f - 1.0f)
|
||||
{
|
||||
if (fallbackAxis != Float3.Zero)
|
||||
{
|
||||
// Rotate 180 degrees about the fallback axis
|
||||
RotationAxis(ref fallbackAxis, Mathf.Pi, out result);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Generate an axis
|
||||
Float3 axis = Float3.Cross(Float3.UnitX, from);
|
||||
if (axis.LengthSquared < Mathf.Epsilon) // Pick another if colinear
|
||||
axis = Float3.Cross(Float3.UnitY, from);
|
||||
axis.Normalize();
|
||||
RotationAxis(ref axis, Mathf.Pi, out result);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
float s = Mathf.Sqrt((1 + d) * 2);
|
||||
float invS = 1 / s;
|
||||
Float3.Cross(ref v0, ref v1, out var c);
|
||||
result.X = c.X * invS;
|
||||
result.Y = c.Y * invS;
|
||||
result.Z = c.Z * invS;
|
||||
result.W = s * 0.5f;
|
||||
result.Normalize();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the shortest arc quaternion to rotate this vector to the destination vector.
|
||||
/// </summary>
|
||||
/// <param name="from">The source vector.</param>
|
||||
/// <param name="to">The destination vector.</param>
|
||||
/// <param name="fallbackAxis">The fallback axis.</param>
|
||||
/// <returns>The rotation.</returns>
|
||||
public static Quaternion GetRotationFromTo(Float3 from, Float3 to, Float3 fallbackAxis)
|
||||
{
|
||||
GetRotationFromTo(ref from, ref to, out var result, ref fallbackAxis);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the quaternion that will rotate vector from into vector to, around their plan perpendicular axis.The input vectors don't need to be normalized.
|
||||
/// </summary>
|
||||
/// <param name="from">The source vector.</param>
|
||||
/// <param name="to">The destination vector.</param>
|
||||
/// <param name="result">The result.</param>
|
||||
public static void FindBetween(ref Float3 from, ref Float3 to, out Quaternion result)
|
||||
{
|
||||
// http://lolengine.net/blog/2014/02/24/quaternion-from-two-vectors-final
|
||||
float normFromNormTo = Mathf.Sqrt(from.LengthSquared * to.LengthSquared);
|
||||
if (normFromNormTo < Mathf.Epsilon)
|
||||
{
|
||||
result = Identity;
|
||||
return;
|
||||
}
|
||||
float w = normFromNormTo + Float3.Dot(from, to);
|
||||
if (w < 1.0-6f * normFromNormTo)
|
||||
{
|
||||
result = Mathf.Abs(from.X) > Mathf.Abs(from.Z)
|
||||
? new Quaternion(-from.Y, from.X, 0.0f, 0.0f)
|
||||
: new Quaternion(0.0f, -from.Z, from.Y, 0.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
Float3 cross = Float3.Cross(from, to);
|
||||
result = new Quaternion(cross.X, cross.Y, cross.Z, w);
|
||||
}
|
||||
result.Normalize();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the quaternion that will rotate vector from into vector to, around their plan perpendicular axis.The input vectors don't need to be normalized.
|
||||
/// </summary>
|
||||
/// <param name="from">The source vector.</param>
|
||||
/// <param name="to">The destination vector.</param>
|
||||
/// <returns>The rotation.</returns>
|
||||
public static Quaternion FindBetween(Float3 from, Float3 to)
|
||||
{
|
||||
FindBetween(ref from, ref to, out var result);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a left-handed spherical billboard that rotates around a specified object position.
|
||||
/// </summary>
|
||||
|
||||
@@ -43,14 +43,14 @@ public:
|
||||
return Capacity;
|
||||
}
|
||||
|
||||
FORCE_INLINE void Allocate(uint64 capacity)
|
||||
FORCE_INLINE void Allocate(int32 capacity)
|
||||
{
|
||||
#if ENABLE_ASSERTION_LOW_LAYERS
|
||||
ASSERT(capacity <= Capacity);
|
||||
#endif
|
||||
}
|
||||
|
||||
FORCE_INLINE void Relocate(uint64 capacity, int32 oldCount, int32 newCount)
|
||||
FORCE_INLINE void Relocate(int32 capacity, int32 oldCount, int32 newCount)
|
||||
{
|
||||
#if ENABLE_ASSERTION_LOW_LAYERS
|
||||
ASSERT(capacity <= Capacity);
|
||||
@@ -120,12 +120,15 @@ public:
|
||||
capacity |= capacity >> 4;
|
||||
capacity |= capacity >> 8;
|
||||
capacity |= capacity >> 16;
|
||||
capacity = (capacity + 1) * 2;
|
||||
uint64 capacity64 = (uint64)(capacity + 1) * 2;
|
||||
if (capacity64 > MAX_int32)
|
||||
capacity64 = MAX_int32;
|
||||
capacity = (int32)capacity64;
|
||||
}
|
||||
return capacity;
|
||||
}
|
||||
|
||||
FORCE_INLINE void Allocate(uint64 capacity)
|
||||
FORCE_INLINE void Allocate(int32 capacity)
|
||||
{
|
||||
#if ENABLE_ASSERTION_LOW_LAYERS
|
||||
ASSERT(!_data);
|
||||
@@ -137,7 +140,7 @@ public:
|
||||
#endif
|
||||
}
|
||||
|
||||
FORCE_INLINE void Relocate(uint64 capacity, int32 oldCount, int32 newCount)
|
||||
FORCE_INLINE void Relocate(int32 capacity, int32 oldCount, int32 newCount)
|
||||
{
|
||||
T* newData = capacity != 0 ? (T*)Allocator::Allocate(capacity * sizeof(T)) : nullptr;
|
||||
#if !BUILD_RELEASE
|
||||
@@ -210,7 +213,7 @@ public:
|
||||
return minCapacity <= Capacity ? Capacity : _other.CalculateCapacityGrow(capacity, minCapacity);
|
||||
}
|
||||
|
||||
FORCE_INLINE void Allocate(uint64 capacity)
|
||||
FORCE_INLINE void Allocate(int32 capacity)
|
||||
{
|
||||
if (capacity > Capacity)
|
||||
{
|
||||
@@ -219,7 +222,7 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
FORCE_INLINE void Relocate(uint64 capacity, int32 oldCount, int32 newCount)
|
||||
FORCE_INLINE void Relocate(int32 capacity, int32 oldCount, int32 newCount)
|
||||
{
|
||||
// Check if the new allocation will fit into inlined storage
|
||||
if (capacity <= Capacity)
|
||||
|
||||
@@ -327,6 +327,18 @@ public:
|
||||
bool operator!=(const String& other) const;
|
||||
|
||||
public:
|
||||
using StringViewBase::StartsWith;
|
||||
FORCE_INLINE bool StartsWith(const StringView& prefix, StringSearchCase searchCase = StringSearchCase::IgnoreCase) const
|
||||
{
|
||||
return StringViewBase::StartsWith(prefix, searchCase);
|
||||
}
|
||||
|
||||
using StringViewBase::EndsWith;
|
||||
FORCE_INLINE bool EndsWith(const StringView& suffix, StringSearchCase searchCase = StringSearchCase::IgnoreCase) const
|
||||
{
|
||||
return StringViewBase::EndsWith(suffix, searchCase);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the left most given number of characters.
|
||||
/// </summary>
|
||||
@@ -511,6 +523,18 @@ public:
|
||||
bool operator!=(const StringAnsi& other) const;
|
||||
|
||||
public:
|
||||
using StringViewBase::StartsWith;
|
||||
FORCE_INLINE bool StartsWith(const StringAnsiView& prefix, StringSearchCase searchCase = StringSearchCase::IgnoreCase) const
|
||||
{
|
||||
return StringViewBase::StartsWith(prefix, searchCase);
|
||||
}
|
||||
|
||||
using StringViewBase::EndsWith;
|
||||
FORCE_INLINE bool EndsWith(const StringAnsiView& suffix, StringSearchCase searchCase = StringSearchCase::IgnoreCase) const
|
||||
{
|
||||
return StringViewBase::EndsWith(suffix, searchCase);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves substring created from characters starting from startIndex to the String end.
|
||||
/// </summary>
|
||||
|
||||
@@ -696,12 +696,15 @@ void* DebugDraw::AllocateContext()
|
||||
|
||||
void DebugDraw::FreeContext(void* context)
|
||||
{
|
||||
ASSERT(context);
|
||||
Memory::DestructItem((DebugDrawContext*)context);
|
||||
Allocator::Free(context);
|
||||
}
|
||||
|
||||
void DebugDraw::UpdateContext(void* context, float deltaTime)
|
||||
{
|
||||
if (!context)
|
||||
context = &GlobalContext;
|
||||
((DebugDrawContext*)context)->DebugDrawDefault.Update(deltaTime);
|
||||
((DebugDrawContext*)context)->DebugDrawDepthTest.Update(deltaTime);
|
||||
}
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
#include "Engine/Threading/MainThreadTask.h"
|
||||
#include "Engine/Threading/ThreadRegistry.h"
|
||||
#include "Engine/Graphics/GPUDevice.h"
|
||||
#include "Engine/Scripting/ManagedCLR/MCore.h"
|
||||
#include "Engine/Scripting/ScriptingType.h"
|
||||
#include "Engine/Content/Content.h"
|
||||
#include "Engine/Content/JsonAsset.h"
|
||||
@@ -327,14 +326,6 @@ void Engine::OnUpdate()
|
||||
|
||||
// Update services
|
||||
EngineService::OnUpdate();
|
||||
|
||||
#ifdef USE_NETCORE
|
||||
// Force GC to run in background periodically to avoid large blocking collections causing hitches
|
||||
if (Time::Update.TicksCount % 60 == 0)
|
||||
{
|
||||
MCore::GC::Collect(MCore::GC::MaxGeneration(), MGCCollectionMode::Forced, false, false);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void Engine::OnLateUpdate()
|
||||
|
||||
@@ -26,12 +26,12 @@ public:
|
||||
, _srcResource(src)
|
||||
, _dstResource(dst)
|
||||
{
|
||||
_srcResource.OnUnload.Bind<GPUCopyResourceTask, &GPUCopyResourceTask::OnResourceUnload>(this);
|
||||
_dstResource.OnUnload.Bind<GPUCopyResourceTask, &GPUCopyResourceTask::OnResourceUnload>(this);
|
||||
_srcResource.Released.Bind<GPUCopyResourceTask, &GPUCopyResourceTask::OnResourceReleased>(this);
|
||||
_dstResource.Released.Bind<GPUCopyResourceTask, &GPUCopyResourceTask::OnResourceReleased>(this);
|
||||
}
|
||||
|
||||
private:
|
||||
void OnResourceUnload(GPUResourceReference* ref)
|
||||
void OnResourceReleased()
|
||||
{
|
||||
Cancel();
|
||||
}
|
||||
@@ -47,14 +47,11 @@ protected:
|
||||
// [GPUTask]
|
||||
Result run(GPUTasksContext* context) override
|
||||
{
|
||||
if (_srcResource.IsMissing() || _dstResource.IsMissing())
|
||||
if (!_srcResource || !_dstResource)
|
||||
return Result::MissingResources;
|
||||
|
||||
context->GPU->CopyResource(_dstResource, _srcResource);
|
||||
|
||||
return Result::Ok;
|
||||
}
|
||||
|
||||
void OnEnd() override
|
||||
{
|
||||
_srcResource.Unlink();
|
||||
|
||||
@@ -31,12 +31,12 @@ public:
|
||||
, _srcSubresource(srcSubresource)
|
||||
, _dstSubresource(dstSubresource)
|
||||
{
|
||||
_srcResource.OnUnload.Bind<GPUCopySubresourceTask, &GPUCopySubresourceTask::OnResourceUnload>(this);
|
||||
_dstResource.OnUnload.Bind<GPUCopySubresourceTask, &GPUCopySubresourceTask::OnResourceUnload>(this);
|
||||
_srcResource.Released.Bind<GPUCopySubresourceTask, &GPUCopySubresourceTask::OnResourceReleased>(this);
|
||||
_dstResource.Released.Bind<GPUCopySubresourceTask, &GPUCopySubresourceTask::OnResourceReleased>(this);
|
||||
}
|
||||
|
||||
private:
|
||||
void OnResourceUnload(GPUResourceReference* ref)
|
||||
void OnResourceReleased()
|
||||
{
|
||||
Cancel();
|
||||
}
|
||||
@@ -52,14 +52,11 @@ protected:
|
||||
// [GPUTask]
|
||||
Result run(GPUTasksContext* context) override
|
||||
{
|
||||
if (_srcResource.IsMissing() || _dstResource.IsMissing())
|
||||
if (!_srcResource || !_dstResource)
|
||||
return Result::MissingResources;
|
||||
|
||||
context->GPU->CopySubresource(_dstResource, _dstSubresource, _srcResource, _srcSubresource);
|
||||
|
||||
return Result::Ok;
|
||||
}
|
||||
|
||||
void OnEnd() override
|
||||
{
|
||||
_srcResource.Unlink();
|
||||
|
||||
@@ -31,7 +31,7 @@ public:
|
||||
, _buffer(buffer)
|
||||
, _offset(offset)
|
||||
{
|
||||
_buffer.OnUnload.Bind<GPUUploadBufferTask, &GPUUploadBufferTask::OnResourceUnload>(this);
|
||||
_buffer.Released.Bind<GPUUploadBufferTask, &GPUUploadBufferTask::OnResourceReleased>(this);
|
||||
|
||||
if (copyData)
|
||||
_data.Copy(data);
|
||||
@@ -40,7 +40,7 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
void OnResourceUnload(BufferReference* ref)
|
||||
void OnResourceReleased()
|
||||
{
|
||||
Cancel();
|
||||
}
|
||||
@@ -56,14 +56,11 @@ protected:
|
||||
// [GPUTask]
|
||||
Result run(GPUTasksContext* context) override
|
||||
{
|
||||
if (_buffer.IsMissing())
|
||||
if (!_buffer)
|
||||
return Result::MissingResources;
|
||||
|
||||
context->GPU->UpdateBuffer(_buffer, _data.Get(), _data.Length(), _offset);
|
||||
|
||||
return Result::Ok;
|
||||
}
|
||||
|
||||
void OnEnd() override
|
||||
{
|
||||
_buffer.Unlink();
|
||||
|
||||
@@ -35,7 +35,7 @@ public:
|
||||
, _rowPitch(rowPitch)
|
||||
, _slicePitch(slicePitch)
|
||||
{
|
||||
_texture.OnUnload.Bind<GPUUploadTextureMipTask, &GPUUploadTextureMipTask::OnResourceUnload>(this);
|
||||
_texture.Released.Bind<GPUUploadTextureMipTask, &GPUUploadTextureMipTask::OnResourceReleased>(this);
|
||||
|
||||
if (copyData)
|
||||
_data.Copy(data);
|
||||
@@ -44,7 +44,7 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
void OnResourceUnload(GPUTextureReference* ref)
|
||||
void OnResourceReleased()
|
||||
{
|
||||
Cancel();
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "GPUDevice.h"
|
||||
#include "RenderTargetPool.h"
|
||||
#include "GPUPipelineState.h"
|
||||
#include "GPUResourceProperty.h"
|
||||
#include "GPUSwapChain.h"
|
||||
#include "RenderTask.h"
|
||||
#include "RenderTools.h"
|
||||
@@ -25,6 +26,39 @@
|
||||
#include "Engine/Renderer/RenderList.h"
|
||||
#include "Engine/Scripting/Enums.h"
|
||||
|
||||
GPUResourcePropertyBase::~GPUResourcePropertyBase()
|
||||
{
|
||||
const auto e = _resource;
|
||||
if (e)
|
||||
{
|
||||
_resource = nullptr;
|
||||
e->Releasing.Unbind<GPUResourcePropertyBase, &GPUResourcePropertyBase::OnReleased>(this);
|
||||
}
|
||||
}
|
||||
|
||||
void GPUResourcePropertyBase::OnSet(GPUResource* resource)
|
||||
{
|
||||
auto e = _resource;
|
||||
if (e != resource)
|
||||
{
|
||||
if (e)
|
||||
e->Releasing.Unbind<GPUResourcePropertyBase, &GPUResourcePropertyBase::OnReleased>(this);
|
||||
_resource = e = resource;
|
||||
if (e)
|
||||
e->Releasing.Bind<GPUResourcePropertyBase, &GPUResourcePropertyBase::OnReleased>(this);
|
||||
}
|
||||
}
|
||||
|
||||
void GPUResourcePropertyBase::OnReleased()
|
||||
{
|
||||
auto e = _resource;
|
||||
if (e)
|
||||
{
|
||||
_resource = nullptr;
|
||||
e->Releasing.Unbind<GPUResourcePropertyBase, &GPUResourcePropertyBase::OnReleased>(this);
|
||||
}
|
||||
}
|
||||
|
||||
GPUPipelineState* GPUPipelineState::Spawn(const SpawnParams& params)
|
||||
{
|
||||
return GPUDevice::Instance->CreatePipelineState();
|
||||
|
||||
@@ -8,28 +8,39 @@
|
||||
/// <summary>
|
||||
/// GPU Resource container utility object.
|
||||
/// </summary>
|
||||
template<typename T = GPUResource>
|
||||
class GPUResourceProperty
|
||||
class FLAXENGINE_API GPUResourcePropertyBase
|
||||
{
|
||||
private:
|
||||
T* _resource;
|
||||
protected:
|
||||
GPUResource* _resource = nullptr;
|
||||
|
||||
private:
|
||||
// Disable copy actions
|
||||
GPUResourceProperty(const GPUResourceProperty& other) = delete;
|
||||
public:
|
||||
NON_COPYABLE(GPUResourcePropertyBase);
|
||||
|
||||
GPUResourcePropertyBase() = default;
|
||||
~GPUResourcePropertyBase();
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
/// Action fired when resource gets unloaded (reference gets cleared bu async tasks should stop execution).
|
||||
/// Action fired when resource gets released (reference gets cleared bu async tasks should stop execution).
|
||||
/// </summary>
|
||||
Delegate<GPUResourceProperty*> OnUnload;
|
||||
Action Released;
|
||||
|
||||
protected:
|
||||
void OnSet(GPUResource* resource);
|
||||
void OnReleased();
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// GPU Resource container utility object.
|
||||
/// </summary>
|
||||
template<typename T = GPUResource>
|
||||
class GPUResourceProperty : public GPUResourcePropertyBase
|
||||
{
|
||||
public:
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="GPUResourceProperty"/> class.
|
||||
/// </summary>
|
||||
GPUResourceProperty()
|
||||
: _resource(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -38,9 +49,37 @@ public:
|
||||
/// </summary>
|
||||
/// <param name="resource">The resource.</param>
|
||||
GPUResourceProperty(T* resource)
|
||||
: _resource(nullptr)
|
||||
{
|
||||
Set(resource);
|
||||
OnSet(resource);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="GPUResourceProperty"/> class.
|
||||
/// </summary>
|
||||
/// <param name="other">The other value.</param>
|
||||
GPUResourceProperty(const GPUResourceProperty& other)
|
||||
{
|
||||
OnSet(other.Get());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="GPUResourceProperty"/> class.
|
||||
/// </summary>
|
||||
/// <param name="other">The other value.</param>
|
||||
GPUResourceProperty(GPUResourceProperty&& other)
|
||||
{
|
||||
OnSet(other.Get());
|
||||
other.OnSet(nullptr);
|
||||
}
|
||||
|
||||
GPUResourceProperty& operator=(GPUResourceProperty&& other)
|
||||
{
|
||||
if (&other != this)
|
||||
{
|
||||
OnSet(other._resource);
|
||||
other.OnSet(nullptr);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -48,13 +87,6 @@ public:
|
||||
/// </summary>
|
||||
~GPUResourceProperty()
|
||||
{
|
||||
// Check if object has been binded
|
||||
if (_resource)
|
||||
{
|
||||
// Unlink
|
||||
_resource->Releasing.template Unbind<GPUResourceProperty, &GPUResourceProperty::onResourceUnload>(this);
|
||||
_resource = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
@@ -63,43 +95,34 @@ public:
|
||||
return Get() == other;
|
||||
}
|
||||
|
||||
FORCE_INLINE bool operator==(GPUResourceProperty& other) const
|
||||
FORCE_INLINE bool operator==(const GPUResourceProperty& other) const
|
||||
{
|
||||
return Get() == other.Get();
|
||||
}
|
||||
|
||||
GPUResourceProperty& operator=(const GPUResourceProperty& other)
|
||||
{
|
||||
if (this != &other)
|
||||
Set(other.Get());
|
||||
return *this;
|
||||
}
|
||||
|
||||
FORCE_INLINE GPUResourceProperty& operator=(T& other)
|
||||
{
|
||||
Set(&other);
|
||||
OnSet(&other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
FORCE_INLINE GPUResourceProperty& operator=(T* other)
|
||||
{
|
||||
Set(other);
|
||||
OnSet(other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implicit conversion to GPU Resource
|
||||
/// </summary>
|
||||
/// <returns>Resource</returns>
|
||||
FORCE_INLINE operator T*() const
|
||||
{
|
||||
return _resource;
|
||||
return (T*)_resource;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implicit conversion to resource
|
||||
/// </summary>
|
||||
/// <returns>True if resource has been binded, otherwise false</returns>
|
||||
FORCE_INLINE operator bool() const
|
||||
{
|
||||
return _resource != nullptr;
|
||||
@@ -108,37 +131,17 @@ public:
|
||||
/// <summary>
|
||||
/// Implicit conversion to resource
|
||||
/// </summary>
|
||||
/// <returns>Resource</returns>
|
||||
FORCE_INLINE T* operator->() const
|
||||
{
|
||||
return _resource;
|
||||
return (T*)_resource;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets linked resource
|
||||
/// </summary>
|
||||
/// <returns>Resource</returns>
|
||||
FORCE_INLINE T* Get() const
|
||||
{
|
||||
return _resource;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if resource has been binded
|
||||
/// </summary>
|
||||
/// <returns>True if resource has been binded, otherwise false</returns>
|
||||
FORCE_INLINE bool IsBinded() const
|
||||
{
|
||||
return _resource != nullptr;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if resource is missing
|
||||
/// </summary>
|
||||
/// <returns>True if resource is missing, otherwise false</returns>
|
||||
FORCE_INLINE bool IsMissing() const
|
||||
{
|
||||
return _resource == nullptr;
|
||||
return (T*)_resource;
|
||||
}
|
||||
|
||||
public:
|
||||
@@ -148,19 +151,7 @@ public:
|
||||
/// <param name="value">Value to assign</param>
|
||||
void Set(T* value)
|
||||
{
|
||||
if (_resource != value)
|
||||
{
|
||||
// Remove reference from the old one
|
||||
if (_resource)
|
||||
_resource->Releasing.template Unbind<GPUResourceProperty, &GPUResourceProperty::onResourceUnload>(this);
|
||||
|
||||
// Change referenced object
|
||||
_resource = value;
|
||||
|
||||
// Add reference to the new one
|
||||
if (_resource)
|
||||
_resource->Releasing.template Bind<GPUResourceProperty, &GPUResourceProperty::onResourceUnload>(this);
|
||||
}
|
||||
OnSet(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -168,22 +159,7 @@ public:
|
||||
/// </summary>
|
||||
void Unlink()
|
||||
{
|
||||
if (_resource)
|
||||
{
|
||||
// Remove reference from the old one
|
||||
_resource->Releasing.template Unbind<GPUResourceProperty, &GPUResourceProperty::onResourceUnload>(this);
|
||||
_resource = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void onResourceUnload()
|
||||
{
|
||||
if (_resource)
|
||||
{
|
||||
_resource = nullptr;
|
||||
OnUnload(this);
|
||||
}
|
||||
OnSet(nullptr);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -90,6 +90,21 @@ public:
|
||||
/// </summary>
|
||||
Array<BlendShape> BlendShapes;
|
||||
|
||||
/// <summary>
|
||||
/// Global translation for this mesh to be at it's local origin.
|
||||
/// </summary>
|
||||
Vector3 OriginTranslation = Vector3::Zero;
|
||||
|
||||
/// <summary>
|
||||
/// Orientation for this mesh at it's local origin.
|
||||
/// </summary>
|
||||
Quaternion OriginOrientation = Quaternion::Identity;
|
||||
|
||||
/// <summary>
|
||||
/// Meshes scaling.
|
||||
/// </summary>
|
||||
Vector3 Scaling = Vector3::One;
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
/// Determines whether this instance has any mesh data.
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "Engine/Core/Math/Vector3.h"
|
||||
#include "Engine/Core/Math/Vector4.h"
|
||||
#include "Engine/Content/AssetReference.h"
|
||||
#include "Engine/Content/SoftAssetReference.h"
|
||||
#include "Engine/Core/ISerializable.h"
|
||||
#include "Engine/Content/Assets/Texture.h"
|
||||
#include "Engine/Content/Assets/MaterialBase.h"
|
||||
@@ -850,7 +851,7 @@ API_STRUCT() struct FLAXENGINE_API ColorGradingSettings : ISerializable
|
||||
/// The Lookup Table (LUT) used to perform color correction.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="DefaultValue(null), EditorOrder(22), PostProcessSetting((int)ColorGradingSettingsOverride.LutTexture)")
|
||||
AssetReference<Texture> LutTexture;
|
||||
SoftAssetReference<Texture> LutTexture;
|
||||
|
||||
/// <summary>
|
||||
/// The LUT blending weight (normalized to range 0-1). Default is 1.0.
|
||||
@@ -1277,7 +1278,7 @@ API_STRUCT() struct FLAXENGINE_API LensFlaresSettings : ISerializable
|
||||
/// Fullscreen lens dirt texture.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="DefaultValue(null), EditorOrder(8), PostProcessSetting((int)LensFlaresSettingsOverride.LensDirt)")
|
||||
AssetReference<Texture> LensDirt;
|
||||
SoftAssetReference<Texture> LensDirt;
|
||||
|
||||
/// <summary>
|
||||
/// Fullscreen lens dirt intensity parameter. Allows to tune dirt visibility.
|
||||
@@ -1289,13 +1290,13 @@ API_STRUCT() struct FLAXENGINE_API LensFlaresSettings : ISerializable
|
||||
/// Custom lens color texture (1D) used for lens color spectrum.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="DefaultValue(null), EditorOrder(10), PostProcessSetting((int)LensFlaresSettingsOverride.LensColor)")
|
||||
AssetReference<Texture> LensColor;
|
||||
SoftAssetReference<Texture> LensColor;
|
||||
|
||||
/// <summary>
|
||||
/// Custom lens star texture sampled by lens flares.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="DefaultValue(null), EditorOrder(11), PostProcessSetting((int)LensFlaresSettingsOverride.LensStar)")
|
||||
AssetReference<Texture> LensStar;
|
||||
SoftAssetReference<Texture> LensStar;
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
@@ -1487,7 +1488,7 @@ API_STRUCT() struct FLAXENGINE_API DepthOfFieldSettings : ISerializable
|
||||
/// If BokehShape is set to Custom, then this texture will be used for the bokeh shapes. For best performance, use small, compressed, grayscale textures (for instance 32px).
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="DefaultValue(null), EditorOrder(11), PostProcessSetting((int)DepthOfFieldSettingsOverride.BokehShapeCustom)")
|
||||
AssetReference<Texture> BokehShapeCustom;
|
||||
SoftAssetReference<Texture> BokehShapeCustom;
|
||||
|
||||
/// <summary>
|
||||
/// The minimum pixel brightness to create bokeh. Pixels with lower brightness will be skipped.
|
||||
|
||||
@@ -22,10 +22,10 @@ TextureHeader::TextureHeader()
|
||||
TextureGroup = -1;
|
||||
}
|
||||
|
||||
TextureHeader::TextureHeader(TextureHeader_Deprecated& old)
|
||||
TextureHeader::TextureHeader(const TextureHeader_Deprecated& old)
|
||||
{
|
||||
Platform::MemoryClear(this, sizeof(*this));
|
||||
Width = old.Width;;
|
||||
Width = old.Width;
|
||||
Height = old.Height;
|
||||
MipLevels = old.MipLevels;
|
||||
Format = old.Format;
|
||||
@@ -49,7 +49,7 @@ StreamingTexture::StreamingTexture(ITextureOwner* parent, const String& name)
|
||||
, _texture(nullptr)
|
||||
, _isBlockCompressed(false)
|
||||
{
|
||||
ASSERT(_owner != nullptr);
|
||||
ASSERT(parent != nullptr);
|
||||
|
||||
// Always have created texture object
|
||||
ASSERT(GPUDevice::Instance);
|
||||
@@ -329,11 +329,11 @@ public:
|
||||
, _dataLock(_streamingTexture->GetOwner()->LockData())
|
||||
{
|
||||
_streamingTexture->_streamingTasks.Add(this);
|
||||
_texture.OnUnload.Bind<StreamTextureMipTask, &StreamTextureMipTask::onResourceUnload2>(this);
|
||||
_texture.Released.Bind<StreamTextureMipTask, &StreamTextureMipTask::OnResourceReleased2>(this);
|
||||
}
|
||||
|
||||
private:
|
||||
void onResourceUnload2(GPUTextureReference* ref)
|
||||
void OnResourceReleased2()
|
||||
{
|
||||
// Unlink texture
|
||||
if (_streamingTexture)
|
||||
|
||||
@@ -106,5 +106,5 @@ struct FLAXENGINE_API TextureHeader
|
||||
byte CustomData[10];
|
||||
|
||||
TextureHeader();
|
||||
TextureHeader(TextureHeader_Deprecated& old);
|
||||
TextureHeader(const TextureHeader_Deprecated& old);
|
||||
};
|
||||
|
||||
@@ -1406,7 +1406,7 @@ Script* Actor::FindScript(const MClass* type) const
|
||||
CHECK_RETURN(type, nullptr);
|
||||
for (auto script : Scripts)
|
||||
{
|
||||
if (script->GetClass()->IsSubClassOf(type))
|
||||
if (script->GetClass()->IsSubClassOf(type) || script->GetClass()->HasInterface(type))
|
||||
return script;
|
||||
}
|
||||
for (auto child : Children)
|
||||
|
||||
@@ -66,7 +66,7 @@ public:
|
||||
/// <summary>
|
||||
/// Gets the value indicating if camera should use perspective rendering mode, otherwise it will use orthographic projection.
|
||||
/// </summary>
|
||||
API_PROPERTY(Attributes="EditorOrder(20), DefaultValue(true), EditorDisplay(\"Camera\"), Tooltip(\"Enables perspective projection mode, otherwise uses orthographic.\")")
|
||||
API_PROPERTY(Attributes="EditorOrder(10), DefaultValue(true), EditorDisplay(\"Camera\")")
|
||||
bool GetUsePerspective() const;
|
||||
|
||||
/// <summary>
|
||||
@@ -77,7 +77,7 @@ public:
|
||||
/// <summary>
|
||||
/// Gets the camera's field of view (in degrees).
|
||||
/// </summary>
|
||||
API_PROPERTY(Attributes="EditorOrder(10), DefaultValue(60.0f), Limit(0, 179), EditorDisplay(\"Camera\", \"Field Of View\"), Tooltip(\"Field of view angle in degrees.\")")
|
||||
API_PROPERTY(Attributes="EditorOrder(20), DefaultValue(60.0f), Limit(0, 179), EditorDisplay(\"Camera\", \"Field Of View\"), VisibleIf(nameof(UsePerspective))")
|
||||
float GetFieldOfView() const;
|
||||
|
||||
/// <summary>
|
||||
@@ -88,7 +88,7 @@ public:
|
||||
/// <summary>
|
||||
/// Gets the custom aspect ratio. 0 if not use custom value.
|
||||
/// </summary>
|
||||
API_PROPERTY(Attributes="EditorOrder(50), DefaultValue(0.0f), Limit(0, 10, 0.01f), EditorDisplay(\"Camera\"), Tooltip(\"Custom aspect ratio to use. Set to 0 to disable.\")")
|
||||
API_PROPERTY(Attributes="EditorOrder(50), DefaultValue(0.0f), Limit(0, 10, 0.01f), EditorDisplay(\"Camera\"), VisibleIf(nameof(UsePerspective))")
|
||||
float GetCustomAspectRatio() const;
|
||||
|
||||
/// <summary>
|
||||
@@ -99,7 +99,7 @@ public:
|
||||
/// <summary>
|
||||
/// Gets camera's near plane distance.
|
||||
/// </summary>
|
||||
API_PROPERTY(Attributes="EditorOrder(30), DefaultValue(10.0f), Limit(0, 1000, 0.05f), EditorDisplay(\"Camera\"), Tooltip(\"Near clipping plane distance\")")
|
||||
API_PROPERTY(Attributes="EditorOrder(30), DefaultValue(10.0f), Limit(0, 1000, 0.05f), EditorDisplay(\"Camera\")")
|
||||
float GetNearPlane() const;
|
||||
|
||||
/// <summary>
|
||||
@@ -110,7 +110,7 @@ public:
|
||||
/// <summary>
|
||||
/// Gets camera's far plane distance.
|
||||
/// </summary>
|
||||
API_PROPERTY(Attributes="EditorOrder(40), DefaultValue(40000.0f), Limit(0, float.MaxValue, 5), EditorDisplay(\"Camera\"), Tooltip(\"Far clipping plane distance\")")
|
||||
API_PROPERTY(Attributes="EditorOrder(40), DefaultValue(40000.0f), Limit(0, float.MaxValue, 5), EditorDisplay(\"Camera\")")
|
||||
float GetFarPlane() const;
|
||||
|
||||
/// <summary>
|
||||
@@ -121,7 +121,7 @@ public:
|
||||
/// <summary>
|
||||
/// Gets the orthographic projection scale.
|
||||
/// </summary>
|
||||
API_PROPERTY(Attributes="EditorOrder(60), DefaultValue(1.0f), Limit(0.0001f, 1000, 0.01f), EditorDisplay(\"Camera\"), Tooltip(\"Orthographic projection scale\")")
|
||||
API_PROPERTY(Attributes="EditorOrder(60), DefaultValue(1.0f), Limit(0.0001f, 1000, 0.01f), EditorDisplay(\"Camera\"), VisibleIf(nameof(UsePerspective), true)")
|
||||
float GetOrthographicScale() const;
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -24,7 +24,7 @@ private:
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
/// Light source bulb radius
|
||||
/// Light source bulb radius.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="EditorOrder(2), DefaultValue(0.0f), EditorDisplay(\"Light\"), Limit(0, 1000, 0.01f)")
|
||||
float SourceRadius = 0.0f;
|
||||
@@ -42,54 +42,51 @@ public:
|
||||
float FallOffExponent = 8.0f;
|
||||
|
||||
/// <summary>
|
||||
/// IES texture (light profiles from real world measured data)
|
||||
/// IES texture (light profiles from real world measured data).
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="EditorOrder(211), DefaultValue(null), EditorDisplay(\"IES Profile\", \"IES Texture\")")
|
||||
AssetReference<IESProfile> IESTexture;
|
||||
|
||||
/// <summary>
|
||||
/// Enable/disable using light brightness from IES profile
|
||||
/// Enable/disable using light brightness from IES profile.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="EditorOrder(212), DefaultValue(false), EditorDisplay(\"IES Profile\", \"Use IES Brightness\")")
|
||||
bool UseIESBrightness = false;
|
||||
|
||||
/// <summary>
|
||||
/// Global scale for IES brightness contribution
|
||||
/// Global scale for IES brightness contribution.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="EditorOrder(213), DefaultValue(1.0f), Limit(0, 10000, 0.01f), EditorDisplay(\"IES Profile\", \"Brightness Scale\")")
|
||||
float IESBrightnessScale = 1.0f;
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
/// Computes light brightness value
|
||||
/// Computes light brightness value.
|
||||
/// </summary>
|
||||
/// <returns>Brightness</returns>
|
||||
float ComputeBrightness() const;
|
||||
|
||||
/// <summary>
|
||||
/// Gets scaled light radius
|
||||
/// Gets scaled light radius.
|
||||
/// </summary>
|
||||
float GetScaledRadius() const;
|
||||
|
||||
/// <summary>
|
||||
/// Gets light radius
|
||||
/// Gets light radius.
|
||||
/// </summary>
|
||||
API_PROPERTY(Attributes="EditorOrder(1), DefaultValue(1000.0f), EditorDisplay(\"Light\"), Tooltip(\"Light radius\"), Limit(0, 10000, 0.1f)")
|
||||
API_PROPERTY(Attributes="EditorOrder(1), DefaultValue(1000.0f), EditorDisplay(\"Light\"), Limit(0, 10000, 0.1f)")
|
||||
FORCE_INLINE float GetRadius() const
|
||||
{
|
||||
return _radius;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets light radius
|
||||
/// Sets light radius.
|
||||
/// </summary>
|
||||
/// <param name="value">New radius</param>
|
||||
API_PROPERTY() void SetRadius(float value);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the spot light's outer cone angle (in degrees)
|
||||
/// Gets the spot light's outer cone angle (in degrees).
|
||||
/// </summary>
|
||||
/// <returns>Outer angle (in degrees)</returns>
|
||||
API_PROPERTY(Attributes="EditorOrder(22), DefaultValue(43.0f), EditorDisplay(\"Light\"), Limit(1, 89, 0.1f)")
|
||||
FORCE_INLINE float GetOuterConeAngle() const
|
||||
{
|
||||
@@ -97,15 +94,13 @@ public:
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the spot light's outer cone angle (in degrees)
|
||||
/// Sets the spot light's outer cone angle (in degrees).
|
||||
/// </summary>
|
||||
/// <param name="value">Value to assign</param>
|
||||
API_PROPERTY() void SetOuterConeAngle(float value);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the spot light's inner cone angle (in degrees)
|
||||
/// Sets the spot light's inner cone angle (in degrees).
|
||||
/// </summary>
|
||||
/// <returns>Inner angle (in degrees)</returns>
|
||||
API_PROPERTY(Attributes="EditorOrder(21), DefaultValue(10.0f), EditorDisplay(\"Light\"), Limit(1, 89, 0.1f)")
|
||||
FORCE_INLINE float GetInnerConeAngle() const
|
||||
{
|
||||
@@ -113,9 +108,8 @@ public:
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the spot light's inner cone angle (in degrees)
|
||||
/// Sets the spot light's inner cone angle (in degrees).
|
||||
/// </summary>
|
||||
/// <param name="value">Value to assign</param>
|
||||
API_PROPERTY() void SetInnerConeAngle(float value);
|
||||
|
||||
private:
|
||||
|
||||
@@ -19,7 +19,7 @@ API_CLASS(Static) class FLAXENGINE_API LargeWorlds
|
||||
/// <summary>
|
||||
/// Defines the size of a single chunk. Large world (64-bit) gets divided into smaller chunks so all the math operations (32-bit) can be performed relative to the chunk origin without precision loss.
|
||||
/// </summary>
|
||||
API_FIELD() static constexpr Real ChunkSize = 262144;
|
||||
API_FIELD() static constexpr Real ChunkSize = 8192;
|
||||
|
||||
/// <summary>
|
||||
/// Updates the large world origin to match the input position. The origin is snapped to the best matching chunk location.
|
||||
|
||||
@@ -30,6 +30,20 @@ typedef struct
|
||||
} MonoCultureInfo;
|
||||
#endif
|
||||
|
||||
namespace
|
||||
{
|
||||
const CultureInfoEntry* FindEntry(const StringAnsiView& name)
|
||||
{
|
||||
for (int32 i = 0; i < NUM_CULTURE_ENTRIES; i++)
|
||||
{
|
||||
const CultureInfoEntry& e = culture_entries[i];
|
||||
if (name == idx2string(e.name))
|
||||
return &e;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
CultureInfo::CultureInfo(int32 lcid)
|
||||
{
|
||||
_lcid = lcid;
|
||||
@@ -80,23 +94,24 @@ CultureInfo::CultureInfo(const StringAnsiView& name)
|
||||
_englishName = TEXT("Invariant Culture");
|
||||
return;
|
||||
}
|
||||
for (int32 i = 0; i < NUM_CULTURE_ENTRIES; i++)
|
||||
const CultureInfoEntry* e = FindEntry(name);
|
||||
if (!e && name.Find('-') != -1)
|
||||
{
|
||||
auto& e = culture_entries[i];
|
||||
if (name == idx2string(e.name))
|
||||
{
|
||||
_data = (void*)&e;
|
||||
_lcid = (int32)e.lcid;
|
||||
_lcidParent = (int32)e.parent_lcid;
|
||||
_name.SetUTF8(name.Get(), name.Length());
|
||||
const char* nativename = idx2string(e.nativename);
|
||||
_nativeName.SetUTF8(nativename, StringUtils::Length(nativename));
|
||||
const char* englishname = idx2string(e.englishname);
|
||||
_englishName.SetUTF8(englishname, StringUtils::Length(englishname));
|
||||
break;
|
||||
}
|
||||
e = FindEntry(name.Substring(0, name.Find('-')));
|
||||
}
|
||||
if (!_data)
|
||||
if (e)
|
||||
{
|
||||
_data = (void*)e;
|
||||
_lcid = (int32)e->lcid;
|
||||
_lcidParent = (int32)e->parent_lcid;
|
||||
const char* ename = idx2string(e->name);
|
||||
_name.SetUTF8(ename, StringUtils::Length(ename));
|
||||
const char* nativename = idx2string(e->nativename);
|
||||
_nativeName.SetUTF8(nativename, StringUtils::Length(nativename));
|
||||
const char* englishname = idx2string(e->englishname);
|
||||
_englishName.SetUTF8(englishname, StringUtils::Length(englishname));
|
||||
}
|
||||
else
|
||||
{
|
||||
_lcid = 127;
|
||||
_lcidParent = 0;
|
||||
|
||||
@@ -4,6 +4,9 @@
|
||||
#include "IOnlinePlatform.h"
|
||||
#include "Engine/Core/Log.h"
|
||||
#include "Engine/Engine/EngineService.h"
|
||||
#if USE_EDITOR
|
||||
#include "Engine/Scripting/Scripting.h"
|
||||
#endif
|
||||
#include "Engine/Scripting/ScriptingObject.h"
|
||||
|
||||
class OnlineService : public EngineService
|
||||
@@ -25,6 +28,16 @@ IOnlinePlatform* Online::Platform = nullptr;
|
||||
Action Online::PlatformChanged;
|
||||
OnlineService OnlineServiceInstance;
|
||||
|
||||
#if USE_EDITOR
|
||||
|
||||
void OnOnlineScriptsReloading()
|
||||
{
|
||||
// Dispose any active platform
|
||||
Online::Initialize(nullptr);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
bool Online::Initialize(IOnlinePlatform* platform)
|
||||
{
|
||||
if (Platform == platform)
|
||||
@@ -34,6 +47,9 @@ bool Online::Initialize(IOnlinePlatform* platform)
|
||||
|
||||
if (Platform)
|
||||
{
|
||||
#if USE_EDITOR
|
||||
Scripting::ScriptsReloading.Unbind(OnOnlineScriptsReloading);
|
||||
#endif
|
||||
Platform->Deinitialize();
|
||||
}
|
||||
Platform = platform;
|
||||
@@ -45,6 +61,9 @@ bool Online::Initialize(IOnlinePlatform* platform)
|
||||
LOG(Error, "Failed to initialize online platform.");
|
||||
return true;
|
||||
}
|
||||
#if USE_EDITOR
|
||||
Scripting::ScriptsReloading.Bind(OnOnlineScriptsReloading);
|
||||
#endif
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
@@ -41,19 +41,8 @@ protected:
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
/// Enables kinematic mode for the rigidbody.
|
||||
/// Enables kinematic mode for the rigidbody. Kinematic rigidbodies are special dynamic actors that are not influenced by forces(such as gravity), and have no momentum. They are considered to have infinite mass and can push regular dynamic actors out of the way. Kinematics will not collide with static or other kinematic objects but are great for moving platforms or characters, where direct motion control is desired.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Kinematic rigidbodies are special dynamic actors that are not influenced by forces(such as gravity), and have no momentum.
|
||||
/// They are considered to have infinite mass and can push regular dynamic actors out of the way.
|
||||
/// Kinematics will not collide with static or other kinematic objects.
|
||||
/// <para>
|
||||
/// Kinematic rigidbodies are great for moving platforms or characters, where direct motion control is desired.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Kinematic rigidbodies are incompatible with CCD.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
API_PROPERTY(Attributes="EditorOrder(10), DefaultValue(false), EditorDisplay(\"Rigid Body\")")
|
||||
FORCE_INLINE bool GetIsKinematic() const
|
||||
{
|
||||
@@ -61,26 +50,13 @@ public:
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enables kinematic mode for the rigidbody.
|
||||
/// Enables kinematic mode for the rigidbody. Kinematic rigidbodies are special dynamic actors that are not influenced by forces(such as gravity), and have no momentum. They are considered to have infinite mass and can push regular dynamic actors out of the way. Kinematics will not collide with static or other kinematic objects but are great for moving platforms or characters, where direct motion control is desired.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Kinematic rigidbodies are special dynamic actors that are not influenced by forces(such as gravity), and have no momentum.
|
||||
/// They are considered to have infinite mass and can push regular dynamic actors out of the way.
|
||||
/// Kinematics will not collide with static or other kinematic objects.
|
||||
/// <para>
|
||||
/// Kinematic rigidbodies are great for moving platforms or characters, where direct motion control is desired.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Kinematic rigidbodies are incompatible with CCD.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
/// <param name="value">The value.</param>
|
||||
API_PROPERTY() void SetIsKinematic(const bool value);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the 'drag' force added to reduce linear movement.
|
||||
/// Gets the 'drag' force added to reduce linear movement. Linear damping can be used to slow down an object. The higher the drag the more the object slows down.
|
||||
/// </summary>
|
||||
/// <remarks>Linear damping can be used to slow down an object. The higher the drag the more the object slows down.</remarks>
|
||||
API_PROPERTY(Attributes="EditorOrder(60), DefaultValue(0.01f), Limit(0), EditorDisplay(\"Rigid Body\")")
|
||||
FORCE_INLINE float GetLinearDamping() const
|
||||
{
|
||||
@@ -88,16 +64,13 @@ public:
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the 'drag' force added to reduce linear movement.
|
||||
/// Sets the 'drag' force added to reduce linear movement. Linear damping can be used to slow down an object. The higher the drag the more the object slows down.
|
||||
/// </summary>
|
||||
/// <remarks>Linear damping can be used to slow down an object. The higher the drag the more the object slows down.</remarks>
|
||||
/// <param name="value">The value.</param>
|
||||
API_PROPERTY() void SetLinearDamping(float value);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the 'drag' force added to reduce angular movement.
|
||||
/// Gets the 'drag' force added to reduce angular movement. Angular damping can be used to slow down the rotation of an object. The higher the drag the more the rotation slows down.
|
||||
/// </summary>
|
||||
/// <remarks>Angular damping can be used to slow down the rotation of an object. The higher the drag the more the rotation slows down.</remarks>
|
||||
API_PROPERTY(Attributes="EditorOrder(70), DefaultValue(0.05f), Limit(0), EditorDisplay(\"Rigid Body\")")
|
||||
FORCE_INLINE float GetAngularDamping() const
|
||||
{
|
||||
@@ -105,9 +78,8 @@ public:
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the 'drag' force added to reduce angular movement.
|
||||
/// Sets the 'drag' force added to reduce angular movement. Angular damping can be used to slow down the rotation of an object. The higher the drag the more the rotation slows down.
|
||||
/// </summary>
|
||||
/// <remarks>Angular damping can be used to slow down the rotation of an object. The higher the drag the more the rotation slows down.</remarks>
|
||||
/// <param name="value">The value.</param>
|
||||
API_PROPERTY() void SetAngularDamping(float value);
|
||||
|
||||
@@ -123,7 +95,6 @@ public:
|
||||
/// <summary>
|
||||
/// If true simulation and collisions detection will be enabled for the rigidbody.
|
||||
/// </summary>
|
||||
/// <param name="value">The value.</param>
|
||||
API_PROPERTY() void SetEnableSimulation(bool value);
|
||||
|
||||
/// <summary>
|
||||
@@ -138,7 +109,6 @@ public:
|
||||
/// <summary>
|
||||
/// If true Continuous Collision Detection (CCD) will be used for this component.
|
||||
/// </summary>
|
||||
/// <param name="value">The value.</param>
|
||||
API_PROPERTY() void SetUseCCD(const bool value);
|
||||
|
||||
/// <summary>
|
||||
@@ -153,7 +123,6 @@ public:
|
||||
/// <summary>
|
||||
/// If object should have the force of gravity applied.
|
||||
/// </summary>
|
||||
/// <param name="value">The value.</param>
|
||||
API_PROPERTY() void SetEnableGravity(bool value);
|
||||
|
||||
/// <summary>
|
||||
@@ -168,7 +137,6 @@ public:
|
||||
/// <summary>
|
||||
/// If object should start awake, or if it should initially be sleeping.
|
||||
/// </summary>
|
||||
/// <param name="value">The value.</param>
|
||||
API_PROPERTY() void SetStartAwake(bool value);
|
||||
|
||||
/// <summary>
|
||||
@@ -183,16 +151,11 @@ public:
|
||||
/// <summary>
|
||||
/// If true, it will update mass when actor scale changes.
|
||||
/// </summary>
|
||||
/// <param name="value">The value.</param>
|
||||
API_PROPERTY() void SetUpdateMassWhenScaleChanges(bool value);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the maximum angular velocity that a simulated object can achieve.
|
||||
/// Gets the maximum angular velocity that a simulated object can achieve. The angular velocity of rigidbodies is clamped to MaxAngularVelocity to avoid numerical instability with fast rotating bodies. Because this may prevent intentional fast rotations on objects such as wheels, you can override this value per rigidbody.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The angular velocity of rigidbodies is clamped to MaxAngularVelocity to avoid numerical instability with fast rotating bodies.
|
||||
/// Because this may prevent intentional fast rotations on objects such as wheels, you can override this value per rigidbody.
|
||||
/// </remarks>
|
||||
API_PROPERTY(Attributes="EditorOrder(90), DefaultValue(7.0f), Limit(0), EditorDisplay(\"Rigid Body\")")
|
||||
FORCE_INLINE float GetMaxAngularVelocity() const
|
||||
{
|
||||
@@ -200,13 +163,8 @@ public:
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the maximum angular velocity that a simulated object can achieve.
|
||||
/// Sets the maximum angular velocity that a simulated object can achieve. The angular velocity of rigidbodies is clamped to MaxAngularVelocity to avoid numerical instability with fast rotating bodies. Because this may prevent intentional fast rotations on objects such as wheels, you can override this value per rigidbody.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The angular velocity of rigidbodies is clamped to MaxAngularVelocity to avoid numerical instability with fast rotating bodies.
|
||||
/// Because this may prevent intentional fast rotations on objects such as wheels, you can override this value per rigidbody.
|
||||
/// </remarks>
|
||||
/// <param name="value">The value.</param>
|
||||
API_PROPERTY() void SetMaxAngularVelocity(float value);
|
||||
|
||||
/// <summary>
|
||||
@@ -218,7 +176,6 @@ public:
|
||||
/// <summary>
|
||||
/// Override the auto computed mass.
|
||||
/// </summary>
|
||||
/// <param name="value">The value.</param>
|
||||
API_PROPERTY() void SetOverrideMass(bool value);
|
||||
|
||||
/// <summary>
|
||||
@@ -230,8 +187,6 @@ public:
|
||||
/// <summary>
|
||||
/// Sets the mass value measured in kilograms (use override value only if OverrideMass is checked).
|
||||
/// </summary>
|
||||
/// <remarks>If set auto enables mass override.</remarks>
|
||||
/// <param name="value">The value.</param>
|
||||
API_PROPERTY() void SetMass(float value);
|
||||
|
||||
/// <summary>
|
||||
@@ -243,7 +198,6 @@ public:
|
||||
/// <summary>
|
||||
/// Sets the per-instance scaling of the mass.
|
||||
/// </summary>
|
||||
/// <param name="value">The value.</param>
|
||||
API_PROPERTY() void SetMassScale(float value);
|
||||
|
||||
/// <summary>
|
||||
@@ -258,7 +212,6 @@ public:
|
||||
/// <summary>
|
||||
/// Sets the user specified offset for the center of mass of this object, from the calculated location.
|
||||
/// </summary>
|
||||
/// <param name="value">The value.</param>
|
||||
API_PROPERTY() void SetCenterOfMassOffset(const Float3& value);
|
||||
|
||||
/// <summary>
|
||||
@@ -273,28 +226,27 @@ public:
|
||||
/// <summary>
|
||||
/// Sets the object movement constraint flags that define degrees of freedom are allowed for the simulation of object.
|
||||
/// </summary>
|
||||
/// <param name="value">The value.</param>
|
||||
API_PROPERTY() void SetConstraints(const RigidbodyConstraints value);
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
/// Gets the linear velocity of the rigidbody.
|
||||
/// </summary>
|
||||
/// <remarks>It's used mostly to get the current velocity. Manual modifications may result in unrealistic behaviour. </remarks>
|
||||
/// <remarks>It's used mostly to get the current velocity. Manual modifications may result in unrealistic behaviour.</remarks>
|
||||
API_PROPERTY(Attributes="HideInEditor")
|
||||
Vector3 GetLinearVelocity() const;
|
||||
|
||||
/// <summary>
|
||||
/// Sets the linear velocity of the rigidbody.
|
||||
/// </summary>
|
||||
/// <remarks>It's used mostly to get the current velocity. Manual modifications may result in unrealistic behaviour. </remarks>
|
||||
/// <remarks>It's used mostly to get the current velocity. Manual modifications may result in unrealistic behaviour.</remarks>
|
||||
/// <param name="value">The value.</param>
|
||||
API_PROPERTY() void SetLinearVelocity(const Vector3& value) const;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the angular velocity of the rigidbody measured in radians per second.
|
||||
/// </summary>
|
||||
/// <remarks>It's used mostly to get the current angular velocity. Manual modifications may result in unrealistic behaviour. </remarks>
|
||||
/// <remarks>It's used mostly to get the current angular velocity. Manual modifications may result in unrealistic behaviour.</remarks>
|
||||
API_PROPERTY(Attributes="HideInEditor")
|
||||
Vector3 GetAngularVelocity() const;
|
||||
|
||||
|
||||
@@ -81,6 +81,13 @@ void BoxCollider::OnDebugDrawSelected()
|
||||
const Color color = Color::GreenYellow;
|
||||
DEBUG_DRAW_WIRE_BOX(_bounds, color * 0.3f, 0, false);
|
||||
|
||||
if (_contactOffset > 0)
|
||||
{
|
||||
OrientedBoundingBox contactBounds = _bounds;
|
||||
contactBounds.Extents += Vector3(_contactOffset) / contactBounds.Transformation.Scale;
|
||||
DEBUG_DRAW_WIRE_BOX(contactBounds, Color::Blue.AlphaMultiplied(0.2f), 0, false);
|
||||
}
|
||||
|
||||
Vector3 corners[8];
|
||||
_bounds.GetCorners(corners);
|
||||
const float margin = 1.0f;
|
||||
|
||||
@@ -42,27 +42,33 @@ void CapsuleCollider::DrawPhysicsDebug(RenderView& view)
|
||||
const BoundingSphere sphere(_sphere.Center - view.Origin, _sphere.Radius);
|
||||
if (!view.CullingFrustum.Intersects(sphere))
|
||||
return;
|
||||
Quaternion rot;
|
||||
Quaternion::Multiply(_transform.Orientation, Quaternion::Euler(0, 90, 0), rot);
|
||||
Quaternion rotation;
|
||||
Quaternion::Multiply(_transform.Orientation, Quaternion::Euler(0, 90, 0), rotation);
|
||||
const float scaling = _cachedScale.GetAbsolute().MaxValue();
|
||||
const float minSize = 0.001f;
|
||||
const float radius = Math::Max(Math::Abs(_radius) * scaling, minSize);
|
||||
const float height = Math::Max(Math::Abs(_height) * scaling, minSize);
|
||||
if (view.Mode == ViewMode::PhysicsColliders && !GetIsTrigger())
|
||||
DEBUG_DRAW_TUBE(_transform.LocalToWorld(_center), rot, radius, height, _staticActor ? Color::CornflowerBlue : Color::Orchid, 0, true);
|
||||
DEBUG_DRAW_TUBE(_transform.LocalToWorld(_center), rotation, radius, height, _staticActor ? Color::CornflowerBlue : Color::Orchid, 0, true);
|
||||
else
|
||||
DEBUG_DRAW_WIRE_TUBE(_transform.LocalToWorld(_center), rot, radius, height, Color::GreenYellow * 0.8f, 0, true);
|
||||
DEBUG_DRAW_WIRE_TUBE(_transform.LocalToWorld(_center), rotation, radius, height, Color::GreenYellow * 0.8f, 0, true);
|
||||
}
|
||||
|
||||
void CapsuleCollider::OnDebugDrawSelected()
|
||||
{
|
||||
Quaternion rot;
|
||||
Quaternion::Multiply(_transform.Orientation, Quaternion::Euler(0, 90, 0), rot);
|
||||
Quaternion rotation;
|
||||
Quaternion::Multiply(_transform.Orientation, Quaternion::Euler(0, 90, 0), rotation);
|
||||
const float scaling = _cachedScale.GetAbsolute().MaxValue();
|
||||
const float minSize = 0.001f;
|
||||
const float radius = Math::Max(Math::Abs(_radius) * scaling, minSize);
|
||||
const float height = Math::Max(Math::Abs(_height) * scaling, minSize);
|
||||
DEBUG_DRAW_WIRE_TUBE(_transform.LocalToWorld(_center), rot, radius, height, Color::GreenYellow, 0, false);
|
||||
const Vector3 position = _transform.LocalToWorld(_center);
|
||||
DEBUG_DRAW_WIRE_TUBE(position, rotation, radius, height, Color::GreenYellow, 0, false);
|
||||
|
||||
if (_contactOffset > 0)
|
||||
{
|
||||
DEBUG_DRAW_WIRE_TUBE(position, rotation, radius + _contactOffset, height, Color::Blue.AlphaMultiplied(0.2f), 0, false);
|
||||
}
|
||||
|
||||
// Base
|
||||
Collider::OnDebugDrawSelected();
|
||||
|
||||
@@ -23,6 +23,7 @@ CharacterController::CharacterController(const SpawnParams& params)
|
||||
, _nonWalkableMode(NonWalkableModes::PreventClimbing)
|
||||
, _lastFlags(CollisionFlags::None)
|
||||
{
|
||||
_contactOffset = 10.0f;
|
||||
}
|
||||
|
||||
float CharacterController::GetRadius() const
|
||||
|
||||
@@ -19,7 +19,7 @@ Collider::Collider(const SpawnParams& params)
|
||||
, _shape(nullptr)
|
||||
, _staticActor(nullptr)
|
||||
, _cachedScale(1.0f)
|
||||
, _contactOffset(10.0f)
|
||||
, _contactOffset(2.0f)
|
||||
{
|
||||
Material.Changed.Bind<Collider, &Collider::OnMaterialChanged>(this);
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user