Merge remote-tracking branch 'origin/master' into 1.12
# Conflicts: # Source/Editor/Options/InterfaceOptions.cs # Source/Engine/Audio/OpenAL/AudioBackendOAL.cpp # Source/Engine/Graphics/Graphics.cpp # Source/Engine/GraphicsDevice/Vulkan/CmdBufferVulkan.cpp # Source/Engine/GraphicsDevice/Vulkan/CmdBufferVulkan.h # Source/Engine/GraphicsDevice/Vulkan/Config.h # Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.cpp # Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.h # Source/Engine/GraphicsDevice/Vulkan/QueueVulkan.cpp
This commit is contained in:
@@ -739,6 +739,8 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
/// <inheritdoc />
|
||||
public override void Initialize(LayoutElementsContainer layout)
|
||||
{
|
||||
var style = FlaxEngine.GUI.Style.Current;
|
||||
|
||||
// Area for drag&drop scripts
|
||||
var dragArea = layout.CustomContainer<DragAreaControl>();
|
||||
dragArea.CustomControl.ScriptsEditor = this;
|
||||
@@ -800,17 +802,10 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
bool hasAllRequirements = true;
|
||||
if (scriptType.HasAttribute(typeof(RequireScriptAttribute), false))
|
||||
{
|
||||
RequireScriptAttribute scriptAttribute = null;
|
||||
foreach (var e in scriptType.GetAttributes(false))
|
||||
var attribute = (RequireScriptAttribute)scriptType.GetAttributes(false).FirstOrDefault(x => x is RequireScriptAttribute);
|
||||
if (attribute != null)
|
||||
{
|
||||
if (e is not RequireScriptAttribute requireScriptAttribute)
|
||||
continue;
|
||||
scriptAttribute = requireScriptAttribute;
|
||||
}
|
||||
|
||||
if (scriptAttribute != null)
|
||||
{
|
||||
foreach (var type in scriptAttribute.RequiredTypes)
|
||||
foreach (var type in attribute.RequiredTypes)
|
||||
{
|
||||
if (!type.IsSubclassOf(typeof(Script)))
|
||||
continue;
|
||||
@@ -825,15 +820,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
}
|
||||
if (scriptType.HasAttribute(typeof(RequireActorAttribute), false))
|
||||
{
|
||||
RequireActorAttribute attribute = null;
|
||||
foreach (var e in scriptType.GetAttributes(false))
|
||||
{
|
||||
if (e is not RequireActorAttribute requireActorAttribute)
|
||||
continue;
|
||||
attribute = requireActorAttribute;
|
||||
break;
|
||||
}
|
||||
|
||||
var attribute = (RequireActorAttribute)scriptType.GetAttributes(false).FirstOrDefault(x => x is RequireActorAttribute);
|
||||
if (attribute != null)
|
||||
{
|
||||
var actor = script.Actor;
|
||||
@@ -850,7 +837,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
var title = Utilities.Utils.GetPropertyNameUI(scriptType.Name);
|
||||
var group = layout.Group(title, editor);
|
||||
if (!hasAllRequirements)
|
||||
group.Panel.HeaderTextColor = FlaxEngine.GUI.Style.Current.Statusbar.Failed;
|
||||
group.Panel.HeaderTextColor = style.Statusbar.Failed;
|
||||
if ((Presenter.Features & FeatureFlags.CacheExpandedGroups) != 0)
|
||||
{
|
||||
if (Editor.Instance.ProjectCache.IsGroupToggled(title))
|
||||
@@ -863,9 +850,10 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
group.Panel.Open();
|
||||
|
||||
// Customize
|
||||
float totalHeaderButtonsOffset = 0f;
|
||||
group.Panel.TooltipText = Editor.Instance.CodeDocs.GetTooltip(scriptType);
|
||||
if (script.HasPrefabLink)
|
||||
group.Panel.HeaderTextColor = FlaxEngine.GUI.Style.Current.ProgressNormal;
|
||||
group.Panel.HeaderTextColor = style.ProgressNormal;
|
||||
|
||||
// Add toggle button to the group
|
||||
var headerHeight = group.Panel.HeaderHeight;
|
||||
@@ -889,7 +877,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
TooltipText = "Script reference.",
|
||||
AutoFocus = true,
|
||||
IsScrollable = false,
|
||||
Color = FlaxEngine.GUI.Style.Current.ForegroundGrey,
|
||||
Color = style.ForegroundGrey,
|
||||
Parent = group.Panel,
|
||||
Bounds = new Rectangle(scriptToggle.Right, 0.5f, headerHeight, headerHeight),
|
||||
Margin = new Margin(1),
|
||||
@@ -908,10 +896,35 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
var settingsButton = group.AddSettingsButton();
|
||||
settingsButton.Tag = script;
|
||||
settingsButton.Clicked += OnSettingsButtonClicked;
|
||||
totalHeaderButtonsOffset += settingsButton.Width + FlaxEditor.Utilities.Constants.UIMargin;
|
||||
|
||||
// Add script obsolete icon to the group
|
||||
if (scriptType.HasAttribute(typeof(ObsoleteAttribute), false))
|
||||
{
|
||||
var attribute = (ObsoleteAttribute)scriptType.GetAttributes(false).First(x => x is ObsoleteAttribute);
|
||||
var tooltip = "Script marked as obsolete." +
|
||||
(string.IsNullOrEmpty(attribute.Message) ? "" : $"\n{attribute.Message}") +
|
||||
(string.IsNullOrEmpty(attribute.DiagnosticId) ? "" : $"\n{attribute.DiagnosticId}");
|
||||
var obsoleteButton = group.AddHeaderButton(tooltip, totalHeaderButtonsOffset, Editor.Instance.Icons.Info32);
|
||||
obsoleteButton.Color = Color.Orange;
|
||||
obsoleteButton.MouseOverColor = Color.DarkOrange;
|
||||
totalHeaderButtonsOffset += obsoleteButton.Width;
|
||||
}
|
||||
|
||||
// Show visual indicator if script only exists in prefab instance and is not part of the prefab
|
||||
bool isPrefabActor = scripts.Any(s => s.Actor.HasPrefabLink);
|
||||
if (isPrefabActor && script.PrefabID == Guid.Empty)
|
||||
{
|
||||
var prefabInstanceButton = group.AddHeaderButton("Script only exists in this prefab instance.", totalHeaderButtonsOffset, Editor.Instance.Icons.Add32);
|
||||
prefabInstanceButton.Color = style.ProgressNormal;
|
||||
prefabInstanceButton.MouseOverColor = style.ProgressNormal * 0.9f;
|
||||
totalHeaderButtonsOffset += prefabInstanceButton.Width;
|
||||
}
|
||||
|
||||
// Adjust margin to not overlap with other ui elements in the header
|
||||
group.Panel.HeaderTextMargin = group.Panel.HeaderTextMargin with { Left = scriptDrag.Right - 12, Right = settingsButton.Width + Utilities.Constants.UIMargin };
|
||||
group.Object(values, editor);
|
||||
|
||||
// Remove drop down arrows and containment lines if no objects in the group
|
||||
if (group.Children.Count == 0)
|
||||
{
|
||||
|
||||
@@ -37,25 +37,35 @@ namespace FlaxEditor.CustomEditors.Elements
|
||||
public override ContainerControl ContainerControl => Panel;
|
||||
|
||||
/// <summary>
|
||||
/// Adds utility settings button to the group header.
|
||||
/// Add utility settings button to the group header.
|
||||
/// </summary>
|
||||
/// <returns>The created control.</returns>
|
||||
public Image AddSettingsButton()
|
||||
{
|
||||
return AddHeaderButton("Settings", 0, Style.Current.Settings);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a button to the group header.
|
||||
/// </summary>
|
||||
/// <returns>The created control.</returns>
|
||||
public Image AddHeaderButton(string tooltipText, float xOffset, SpriteHandle sprite)
|
||||
{
|
||||
var style = Style.Current;
|
||||
const float padding = 2.0f;
|
||||
var settingsButtonSize = Panel.HeaderHeight;
|
||||
Panel.HeaderTextMargin = Panel.HeaderTextMargin with { Right = settingsButtonSize + Utilities.Constants.UIMargin };
|
||||
; return new Image
|
||||
{
|
||||
TooltipText = "Settings",
|
||||
TooltipText = tooltipText,
|
||||
AutoFocus = true,
|
||||
AnchorPreset = AnchorPresets.TopRight,
|
||||
Parent = Panel,
|
||||
Bounds = new Rectangle(Panel.Width - settingsButtonSize, 0, settingsButtonSize, settingsButtonSize),
|
||||
Bounds = new Rectangle(Panel.Width - settingsButtonSize - xOffset, padding * 0.5f, settingsButtonSize - padding, settingsButtonSize - padding),
|
||||
IsScrollable = false,
|
||||
Color = style.ForegroundGrey,
|
||||
Margin = new Margin(1),
|
||||
Brush = new SpriteBrush(style.Settings),
|
||||
Brush = new SpriteBrush(sprite),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -161,7 +161,7 @@ namespace FlaxEditor.Options
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Options focus Game Window behaviour when play mode is entered.
|
||||
/// Options for focus Game Window behaviour when play mode is entered.
|
||||
/// </summary>
|
||||
public enum PlayModeFocus
|
||||
{
|
||||
@@ -209,6 +209,22 @@ namespace FlaxEditor.Options
|
||||
ClientSide,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generic options for a disabled or hidden state. Used for example in create content button.
|
||||
/// </summary>
|
||||
public enum DisabledHidden
|
||||
{
|
||||
/// <summary>
|
||||
/// Disabled state.
|
||||
/// </summary>
|
||||
Disabled,
|
||||
|
||||
/// <summary>
|
||||
/// Hidden state.
|
||||
/// </summary>
|
||||
Hidden,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Editor User Interface scale. Applied to all UI elements, windows and text. Can be used to scale the interface up on a bigger display. Editor restart required.
|
||||
/// </summary>
|
||||
@@ -562,6 +578,13 @@ namespace FlaxEditor.Options
|
||||
[EditorDisplay("Visject", "Warn when deleting used parameter"), EditorOrder(552)]
|
||||
public bool WarnOnDeletingUsedVisjectParameter { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating what should happen to unavaliable options in the content create menu.
|
||||
/// </summary>
|
||||
[DefaultValue(DisabledHidden.Hidden)]
|
||||
[EditorDisplay("Content"), EditorOrder(600)]
|
||||
public DisabledHidden UnavaliableContentCreateOptions { get; set; } = DisabledHidden.Hidden;
|
||||
|
||||
private static FontAsset DefaultFont => FlaxEngine.Content.LoadAsyncInternal<FontAsset>(EditorAssets.PrimaryFont);
|
||||
private static FontAsset ConsoleFont => FlaxEngine.Content.LoadAsyncInternal<FontAsset>(EditorAssets.InconsolataRegularFont);
|
||||
|
||||
|
||||
@@ -586,8 +586,9 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
TypeID = 13,
|
||||
Title = "Pre-skinned Local Position",
|
||||
Description = "Per vertex local position (before skinning)",
|
||||
AlternativeTitles = new[] { "Vertex Position", "Pre skinning Local Vertex Position" },
|
||||
Flags = NodeFlags.MaterialGraph,
|
||||
Size = new Float2(230, 40),
|
||||
Size = new Float2(270, 40),
|
||||
Elements = new[]
|
||||
{
|
||||
NodeElementArchetype.Factory.Output(0, string.Empty, typeof(Float3), 0),
|
||||
@@ -598,8 +599,9 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
TypeID = 14,
|
||||
Title = "Pre-skinned Local Normal",
|
||||
Description = "Per vertex local normal (before skinning)",
|
||||
AlternativeTitles = new[] { "Vertex Normal", "Pre skinning Local Normal" },
|
||||
Flags = NodeFlags.MaterialGraph,
|
||||
Size = new Float2(230, 40),
|
||||
Size = new Float2(270, 40),
|
||||
Elements = new[]
|
||||
{
|
||||
NodeElementArchetype.Factory.Output(0, string.Empty, typeof(Float3), 0),
|
||||
|
||||
@@ -436,10 +436,11 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
new NodeArchetype
|
||||
{
|
||||
TypeID = 102,
|
||||
Title = "Particle Lifetime",
|
||||
Description = "Particle lifetime (in seconds).",
|
||||
Title = "Particle Total Lifetime",
|
||||
Description = "Total particle lifetime (in seconds) at the time when the particle was created. Always the same, no matter the particles age.",
|
||||
AlternativeTitles = new[] { "Age" },
|
||||
Flags = NodeFlags.MaterialGraph | NodeFlags.ParticleEmitterGraph,
|
||||
Size = new Float2(200, 30),
|
||||
Size = new Float2(250, 30),
|
||||
Elements = new[]
|
||||
{
|
||||
NodeElementArchetype.Factory.Output(0, string.Empty, typeof(float), 0),
|
||||
@@ -449,9 +450,10 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
{
|
||||
TypeID = 103,
|
||||
Title = "Particle Age",
|
||||
Description = "Particle age (in seconds).",
|
||||
Description = "Particle age (in seconds). How long the particle has been alive since it was created.",
|
||||
AlternativeTitles = new[] { "Lifetime" },
|
||||
Flags = NodeFlags.MaterialGraph | NodeFlags.ParticleEmitterGraph,
|
||||
Size = new Float2(200, 30),
|
||||
Size = new Float2(170, 30),
|
||||
Elements = new[]
|
||||
{
|
||||
NodeElementArchetype.Factory.Output(0, string.Empty, typeof(float), 0),
|
||||
@@ -533,9 +535,10 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
{
|
||||
TypeID = 110,
|
||||
Title = "Particle Normalized Age",
|
||||
Description = "Particle normalized age to range 0-1 (age divided by lifetime).",
|
||||
Description = "The normalized age of the particle, represented as 0 (max lifetime) to 1 (max age). (Same as age divided by lifetime.)",
|
||||
AlternativeTitles = new[] { "Lifetime" },
|
||||
Flags = NodeFlags.MaterialGraph | NodeFlags.ParticleEmitterGraph,
|
||||
Size = new Float2(230, 30),
|
||||
Size = new Float2(250, 30),
|
||||
Elements = new[]
|
||||
{
|
||||
NodeElementArchetype.Factory.Output(0, string.Empty, typeof(float), 0),
|
||||
|
||||
@@ -708,12 +708,30 @@ namespace FlaxEditor.Surface
|
||||
{
|
||||
var index = (int)label.Tag;
|
||||
menu.AddSeparator();
|
||||
menu.AddButton("Copy name", () => Clipboard.Text = ((IVisjectSurfaceWindow)Values[0]).VisjectSurface.Parameters[index].Name);
|
||||
// TODO: move 'Copy all names' to context menu of the Properties category (as it's not item-specific)
|
||||
menu.AddButton("Copy all names", CopyAllParameterNamesAsConstantCSharpCode);
|
||||
menu.AddSeparator();
|
||||
menu.AddButton("Rename", () => StartParameterRenaming(index, label));
|
||||
menu.AddButton("Edit attributes...", () => EditAttributesParameter(index, label));
|
||||
menu.AddButton("Delete", () => DeleteParameter(index));
|
||||
OnParamContextMenu(index, menu);
|
||||
}
|
||||
|
||||
private void CopyAllParameterNamesAsConstantCSharpCode()
|
||||
{
|
||||
string allParamNames = "";
|
||||
foreach (var param in ((IVisjectSurfaceWindow)Values[0]).VisjectSurface.Parameters)
|
||||
{
|
||||
string cleanParamName = param.Name.Replace(" ", "");
|
||||
// Filter out headers and other non-parameter entries that can be present in the parameters list
|
||||
if (string.IsNullOrEmpty(cleanParamName))
|
||||
continue;
|
||||
allParamNames += $"private const string {cleanParamName}ParameterName = \"{param.Name}\";\n";
|
||||
}
|
||||
Clipboard.Text = allParamNames;
|
||||
}
|
||||
|
||||
private void StartParameterRenaming(int index, Control label)
|
||||
{
|
||||
var window = (IVisjectSurfaceWindow)Values[0];
|
||||
|
||||
@@ -39,8 +39,7 @@ namespace FlaxEditor.Windows
|
||||
folder = CurrentViewFolder;
|
||||
}
|
||||
Assert.IsNotNull(folder);
|
||||
bool isRootFolder = CurrentViewFolder == _root.Folder;
|
||||
|
||||
|
||||
// Create context menu
|
||||
ContextMenuButton b;
|
||||
ContextMenu cm = new ContextMenu
|
||||
@@ -78,7 +77,7 @@ namespace FlaxEditor.Windows
|
||||
cm.AddButton(Utilities.Constants.ShowInExplorer, () => FileSystem.ShowFileExplorer(System.IO.Path.GetDirectoryName(item.Path)));
|
||||
|
||||
if (!String.IsNullOrEmpty(Editor.Instance.Windows.ContentWin._itemsSearchBox.Text))
|
||||
{
|
||||
{
|
||||
cm.AddButton("Show in Content Panel", () =>
|
||||
{
|
||||
Editor.Instance.Windows.ContentWin.ClearItemsSearch();
|
||||
@@ -178,19 +177,61 @@ namespace FlaxEditor.Windows
|
||||
|
||||
cm.AddSeparator();
|
||||
|
||||
CreateNewModuleMenu(cm, folder);
|
||||
CreateNewFolderMenu(cm, folder, false, item);
|
||||
CreateNewContentItemMenu(cm, folder);
|
||||
|
||||
if (folder.CanHaveAssets)
|
||||
{
|
||||
cm.AddButton("Import file", () =>
|
||||
{
|
||||
_view.ClearSelection();
|
||||
Editor.ContentImporting.ShowImportFileDialog(CurrentViewFolder);
|
||||
});
|
||||
}
|
||||
|
||||
// Remove any leftover separator
|
||||
if (cm.ItemsContainer.Children.LastOrDefault() is ContextMenuSeparator)
|
||||
cm.ItemsContainer.Children.Last().Dispose();
|
||||
|
||||
// Show it
|
||||
cm.Show(this, location);
|
||||
}
|
||||
|
||||
private void CreateNewModuleMenu(ContextMenu menu, ContentFolder folder, bool disableUncreatable = false)
|
||||
{
|
||||
// Check if is source folder to add new module
|
||||
if (folder?.ParentFolder?.Node is ProjectTreeNode parentFolderNode && folder.Node == parentFolderNode.Source)
|
||||
{
|
||||
var button = cm.AddButton("New module");
|
||||
var button = menu.AddButton("New module");
|
||||
button.CloseMenuOnClick = false;
|
||||
button.Clicked += () => NewModule(button, parentFolderNode.Source.Path);
|
||||
}
|
||||
|
||||
if (!isRootFolder && !(item is ContentFolder projectFolder && projectFolder.Node is ProjectTreeNode))
|
||||
else if (disableUncreatable)
|
||||
{
|
||||
cm.AddButton("New folder", NewFolder);
|
||||
var button = menu.AddButton("New module");
|
||||
button.Enabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
private bool CanCreateFolder(ContentItem item = null)
|
||||
{
|
||||
bool canCreateFolder = CurrentViewFolder != _root.Folder && !(item is ContentFolder projectFolder && projectFolder.Node is ProjectTreeNode);
|
||||
return canCreateFolder;
|
||||
}
|
||||
|
||||
private void CreateNewFolderMenu(ContextMenu menu, ContentFolder folder, bool disableUncreatable = false, ContentItem item = null)
|
||||
{
|
||||
bool canCreateFolder = CanCreateFolder(item);
|
||||
if (canCreateFolder || disableUncreatable)
|
||||
{
|
||||
var b = menu.AddButton("New folder", NewFolder);
|
||||
b.Enabled = canCreateFolder;
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateNewContentItemMenu(ContextMenu menu, ContentFolder folder, bool showNew = true, bool disableUncreatable = false)
|
||||
{
|
||||
// Loop through each proxy and user defined json type and add them to the context menu
|
||||
var actorType = new ScriptType(typeof(Actor));
|
||||
var scriptType = new ScriptType(typeof(Script));
|
||||
@@ -230,7 +271,8 @@ namespace FlaxEditor.Windows
|
||||
if (p == null)
|
||||
continue;
|
||||
|
||||
if (p.CanCreate(folder))
|
||||
bool canCreate = p.CanCreate(folder);
|
||||
if (canCreate || disableUncreatable)
|
||||
{
|
||||
var parts = attribute.Path.Split('/');
|
||||
ContextMenuChildMenu childCM = null;
|
||||
@@ -238,16 +280,20 @@ namespace FlaxEditor.Windows
|
||||
for (int i = 0; i < parts?.Length; i++)
|
||||
{
|
||||
var part = parts[i].Trim();
|
||||
if (part == "New" && !showNew)
|
||||
continue;
|
||||
if (i == parts.Length - 1)
|
||||
{
|
||||
if (mainCM)
|
||||
{
|
||||
cm.AddButton(part, () => NewItem(p));
|
||||
var b = menu.AddButton(part, () => NewItem(p));
|
||||
b.Enabled = canCreate;
|
||||
mainCM = false;
|
||||
}
|
||||
else if (childCM != null)
|
||||
{
|
||||
childCM.ContextMenu.AddButton(part, () => NewItem(p));
|
||||
var b = childCM.ContextMenu.AddButton(part, () => NewItem(p));
|
||||
b.Enabled = canCreate;
|
||||
childCM.ContextMenu.AutoSort = true;
|
||||
}
|
||||
}
|
||||
@@ -255,35 +301,21 @@ namespace FlaxEditor.Windows
|
||||
{
|
||||
if (mainCM)
|
||||
{
|
||||
childCM = cm.GetOrAddChildMenu(part);
|
||||
childCM = menu.GetOrAddChildMenu(part);
|
||||
childCM.ContextMenu.AutoSort = true;
|
||||
childCM.Enabled = canCreate;
|
||||
mainCM = false;
|
||||
}
|
||||
else if (childCM != null)
|
||||
{
|
||||
childCM = childCM.ContextMenu.GetOrAddChildMenu(part);
|
||||
childCM.ContextMenu.AutoSort = true;
|
||||
childCM.Enabled = canCreate;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (folder.CanHaveAssets)
|
||||
{
|
||||
cm.AddButton("Import file", () =>
|
||||
{
|
||||
_view.ClearSelection();
|
||||
Editor.ContentImporting.ShowImportFileDialog(CurrentViewFolder);
|
||||
});
|
||||
}
|
||||
|
||||
// Remove any leftover separator
|
||||
if (cm.ItemsContainer.Children.LastOrDefault() is ContextMenuSeparator)
|
||||
cm.ItemsContainer.Children.Last().Dispose();
|
||||
|
||||
// Show it
|
||||
cm.Show(this, location);
|
||||
}
|
||||
|
||||
private void OnExpandAllClicked(ContextMenuButton button)
|
||||
|
||||
@@ -37,6 +37,7 @@ namespace FlaxEditor.Windows
|
||||
|
||||
private readonly ToolStrip _toolStrip;
|
||||
private readonly ToolStripButton _importButton;
|
||||
private readonly ToolStripButton _createNewButton;
|
||||
private readonly ToolStripButton _navigateBackwardButton;
|
||||
private readonly ToolStripButton _navigateForwardButton;
|
||||
private readonly ToolStripButton _navigateUpButton;
|
||||
@@ -154,11 +155,12 @@ namespace FlaxEditor.Windows
|
||||
{
|
||||
Parent = this,
|
||||
};
|
||||
_importButton = (ToolStripButton)_toolStrip.AddButton(Editor.Icons.Import64, () => Editor.ContentImporting.ShowImportFileDialog(CurrentViewFolder)).LinkTooltip("Import content");
|
||||
_importButton = (ToolStripButton)_toolStrip.AddButton(Editor.Icons.Import64, () => Editor.ContentImporting.ShowImportFileDialog(CurrentViewFolder)).LinkTooltip("Import content.");
|
||||
_createNewButton = (ToolStripButton)_toolStrip.AddButton(Editor.Icons.Add64, OnCreateNewItemButtonClicked).LinkTooltip("Create a new asset. Shift + left click to create a new folder.");
|
||||
_toolStrip.AddSeparator();
|
||||
_navigateBackwardButton = (ToolStripButton)_toolStrip.AddButton(Editor.Icons.Left64, NavigateBackward).LinkTooltip("Navigate backward");
|
||||
_navigateForwardButton = (ToolStripButton)_toolStrip.AddButton(Editor.Icons.Right64, NavigateForward).LinkTooltip("Navigate forward");
|
||||
_navigateUpButton = (ToolStripButton)_toolStrip.AddButton(Editor.Icons.Up64, NavigateUp).LinkTooltip("Navigate up");
|
||||
_navigateBackwardButton = (ToolStripButton)_toolStrip.AddButton(Editor.Icons.Left64, NavigateBackward).LinkTooltip("Navigate backward.");
|
||||
_navigateForwardButton = (ToolStripButton)_toolStrip.AddButton(Editor.Icons.Right64, NavigateForward).LinkTooltip("Navigate forward.");
|
||||
_navigateUpButton = (ToolStripButton)_toolStrip.AddButton(Editor.Icons.Up64, NavigateUp).LinkTooltip("Navigate up.");
|
||||
_toolStrip.AddSeparator();
|
||||
|
||||
// Navigation bar
|
||||
@@ -271,6 +273,42 @@ namespace FlaxEditor.Windows
|
||||
InputActions.Add(options => options.Search, () => _itemsSearchBox.Focus());
|
||||
}
|
||||
|
||||
private void OnCreateNewItemButtonClicked()
|
||||
{
|
||||
if (Input.GetKey(KeyboardKeys.Shift) && CanCreateFolder())
|
||||
{
|
||||
NewFolder();
|
||||
return;
|
||||
}
|
||||
|
||||
var menu = new ContextMenu();
|
||||
|
||||
InterfaceOptions interfaceOptions = Editor.Instance.Options.Options.Interface;
|
||||
bool disableUnavaliable = interfaceOptions.UnavaliableContentCreateOptions == InterfaceOptions.DisabledHidden.Disabled;
|
||||
|
||||
CreateNewFolderMenu(menu, CurrentViewFolder, disableUnavaliable);
|
||||
CreateNewModuleMenu(menu, CurrentViewFolder, disableUnavaliable);
|
||||
menu.AddSeparator();
|
||||
CreateNewContentItemMenu(menu, CurrentViewFolder, false, disableUnavaliable);
|
||||
// Hack: Show the menu once to get the direction, then show it above or below the button depending on the direction.
|
||||
menu.Show(this, _createNewButton.UpperLeft);
|
||||
var direction = menu.Direction;
|
||||
menu.Hide();
|
||||
bool below = false;
|
||||
switch (direction)
|
||||
{
|
||||
case ContextMenuDirection.RightDown:
|
||||
case ContextMenuDirection.LeftDown:
|
||||
below = true;
|
||||
break;
|
||||
case ContextMenuDirection.RightUp:
|
||||
case ContextMenuDirection.LeftUp:
|
||||
below = false;
|
||||
break;
|
||||
}
|
||||
menu.Show(this, below ? _createNewButton.BottomLeft : _createNewButton.UpperLeft, direction);
|
||||
}
|
||||
|
||||
private ContextMenu OnViewDropdownPopupCreate(ComboBox comboBox)
|
||||
{
|
||||
var menu = new ContextMenu();
|
||||
|
||||
@@ -207,16 +207,16 @@ public:
|
||||
API_PROPERTY() void SetAttenuation(float value);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the doppler effect factor. Scale for source velocity. Default is 1.
|
||||
/// Gets the doppler effect factor. Scale for source velocity. Default is 1. Used by spatial sources only. Cannot scale the effect up (only dim it).
|
||||
/// </summary>
|
||||
API_PROPERTY(Attributes="EditorOrder(75), DefaultValue(1.0f), Limit(0, float.MaxValue, 0.1f), EditorDisplay(\"Audio Source\")")
|
||||
API_PROPERTY(Attributes="EditorOrder(75), DefaultValue(1.0f), Limit(0, 1.0f, 0.1f), EditorDisplay(\"Audio Source\")")
|
||||
FORCE_INLINE float GetDopplerFactor() const
|
||||
{
|
||||
return _dopplerFactor;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the doppler effect factor. Scale for source velocity. Default is 1.
|
||||
/// Sets the doppler effect factor. Scale for source velocity. Default is 1. Used by spatial sources only. Cannot scale the effect up (only dim it).
|
||||
/// </summary>
|
||||
API_PROPERTY() void SetDopplerFactor(float value);
|
||||
|
||||
|
||||
@@ -848,6 +848,13 @@ bool AudioBackendOAL::Base_Init()
|
||||
}
|
||||
|
||||
// Init
|
||||
alDistanceModel(AL_INVERSE_DISTANCE_CLAMPED); // Default attenuation model
|
||||
if (Audio::GetActiveDeviceIndex() == Math::Clamp(activeDeviceIndex, -1, Audio::Devices.Count() - 1))
|
||||
{
|
||||
// Manually create context if SetActiveDeviceIndex won't call it
|
||||
Base_OnActiveDeviceChanged();
|
||||
}
|
||||
Audio::SetActiveDeviceIndex(activeDeviceIndex);
|
||||
#ifdef AL_SOFT_source_spatialize
|
||||
if (ALC::IsExtensionSupported("AL_SOFT_source_spatialize"))
|
||||
ALC::Features = EnumAddFlags(ALC::Features, FeatureFlags::SpatialMultiChannel);
|
||||
@@ -856,13 +863,6 @@ bool AudioBackendOAL::Base_Init()
|
||||
ALC::Features = EnumAddFlags(ALC::Features, FeatureFlags::HRTF);
|
||||
#endif
|
||||
Base_SetDopplerFactor(AudioSettings::Get()->DopplerFactor);
|
||||
alDistanceModel(AL_INVERSE_DISTANCE_CLAMPED); // Default attenuation model
|
||||
if (Audio::GetActiveDeviceIndex() == Math::Clamp(activeDeviceIndex, -1, Audio::Devices.Count() - 1))
|
||||
{
|
||||
// Manually create context if SetActiveDeviceIndex won't call it
|
||||
Base_OnActiveDeviceChanged();
|
||||
}
|
||||
Audio::SetActiveDeviceIndex(activeDeviceIndex);
|
||||
ALC::Inited = true;
|
||||
|
||||
// Log service info
|
||||
|
||||
@@ -246,7 +246,7 @@ VariantType::VariantType(VariantType&& other) noexcept
|
||||
VariantType& VariantType::operator=(const Types& type)
|
||||
{
|
||||
Type = type;
|
||||
if (StaticName)
|
||||
if (!StaticName)
|
||||
Allocator::Free(TypeName);
|
||||
TypeName = nullptr;
|
||||
StaticName = 0;
|
||||
@@ -265,7 +265,7 @@ VariantType& VariantType::operator=(const VariantType& other)
|
||||
{
|
||||
ASSERT(this != &other);
|
||||
Type = other.Type;
|
||||
if (StaticName)
|
||||
if (!StaticName)
|
||||
Allocator::Free(TypeName);
|
||||
StaticName = other.StaticName;
|
||||
if (StaticName)
|
||||
@@ -315,7 +315,7 @@ void VariantType::SetTypeName(const StringView& typeName)
|
||||
{
|
||||
if (StringUtils::Length(TypeName) != typeName.Length())
|
||||
{
|
||||
if (StaticName)
|
||||
if (!StaticName)
|
||||
Allocator::Free(TypeName);
|
||||
StaticName = 0;
|
||||
TypeName = static_cast<char*>(Allocator::Allocate(typeName.Length() + 1));
|
||||
@@ -328,7 +328,7 @@ void VariantType::SetTypeName(const StringAnsiView& typeName, bool staticName)
|
||||
{
|
||||
if (StringUtils::Length(TypeName) != typeName.Length() || StaticName != staticName)
|
||||
{
|
||||
if (StaticName)
|
||||
if (!StaticName)
|
||||
Allocator::Free(TypeName);
|
||||
StaticName = staticName;
|
||||
if (staticName)
|
||||
|
||||
@@ -6,14 +6,13 @@
|
||||
#include "Engine/Core/Log.h"
|
||||
#include "Engine/Core/Random.h"
|
||||
#include "Engine/Engine/Engine.h"
|
||||
#include "Engine/Graphics/Graphics.h"
|
||||
#include "Engine/Graphics/RenderTask.h"
|
||||
#include "Engine/Graphics/RenderTools.h"
|
||||
#include "Engine/Graphics/GPUDevice.h"
|
||||
#include "Engine/Content/Deprecated.h"
|
||||
#if !FOLIAGE_USE_SINGLE_QUAD_TREE
|
||||
#include "Engine/Threading/JobSystem.h"
|
||||
#if FOLIAGE_USE_DRAW_CALLS_BATCHING
|
||||
#include "Engine/Graphics/RenderTools.h"
|
||||
#endif
|
||||
#endif
|
||||
#include "Engine/Level/SceneQuery.h"
|
||||
#include "Engine/Profiler/ProfilerCPU.h"
|
||||
@@ -125,7 +124,7 @@ void Foliage::AddToCluster(ChunkedArray<FoliageCluster, FOLIAGE_CLUSTER_CHUNKS_S
|
||||
|
||||
#if !FOLIAGE_USE_SINGLE_QUAD_TREE && FOLIAGE_USE_DRAW_CALLS_BATCHING
|
||||
|
||||
void Foliage::DrawInstance(RenderContext& renderContext, FoliageInstance& instance, const FoliageType& type, Model* model, int32 lod, float lodDitherFactor, DrawCallsList* drawCallsLists, BatchedDrawCalls& result) const
|
||||
void Foliage::DrawInstance(DrawContext& context, FoliageInstance& instance, Model* model, int32 lod, float lodDitherFactor, DrawCallsList* drawCallsLists, BatchedDrawCalls& result) const
|
||||
{
|
||||
const auto& meshes = model->LODs.Get()[lod].Meshes;
|
||||
for (int32 meshIndex = 0; meshIndex < meshes.Count(); meshIndex++)
|
||||
@@ -141,7 +140,7 @@ void Foliage::DrawInstance(RenderContext& renderContext, FoliageInstance& instan
|
||||
auto* e = result.TryGet(key);
|
||||
if (!e)
|
||||
{
|
||||
e = &result.Add(key, BatchedDrawCall(renderContext.List))->Value;
|
||||
e = &result.Add(key, BatchedDrawCall(context.RenderContext.List))->Value;
|
||||
ASSERT_LOW_LAYER(key.Mat);
|
||||
e->DrawCall.Material = key.Mat;
|
||||
e->DrawCall.Surface.Lightmap = EnumHasAnyFlags(_staticFlags, StaticFlags::Lightmap) && _scene ? _scene->LightmapsData.GetReadyLightmap(key.Lightmap) : nullptr;
|
||||
@@ -152,21 +151,18 @@ void Foliage::DrawInstance(RenderContext& renderContext, FoliageInstance& instan
|
||||
auto& instanceData = e->Instances.AddOne();
|
||||
Matrix world;
|
||||
const Transform transform = _transform.LocalToWorld(instance.Transform);
|
||||
const Float3 translation = transform.Translation - renderContext.View.Origin;
|
||||
const Float3 translation = transform.Translation - context.ViewOrigin;
|
||||
Matrix::Transformation(transform.Scale, transform.Orientation, translation, world);
|
||||
constexpr float worldDeterminantSign = 1.0f;
|
||||
instanceData.Store(world, world, instance.Lightmap.UVsArea, drawCall.Surface.GeometrySize, instance.Random, worldDeterminantSign, lodDitherFactor);
|
||||
}
|
||||
}
|
||||
|
||||
void Foliage::DrawCluster(RenderContext& renderContext, FoliageCluster* cluster, const FoliageType& type, DrawCallsList* drawCallsLists, BatchedDrawCalls& result) const
|
||||
void Foliage::DrawCluster(DrawContext& context, FoliageCluster* cluster, DrawCallsList* drawCallsLists, BatchedDrawCalls& result) const
|
||||
{
|
||||
// Skip clusters that around too far from view
|
||||
const auto lodView = (renderContext.LodProxyView ? renderContext.LodProxyView : &renderContext.View);
|
||||
if (Float3::Distance(lodView->Position, cluster->TotalBoundsSphere.Center - lodView->Origin) - (float)cluster->TotalBoundsSphere.Radius > cluster->MaxCullDistance)
|
||||
if (Float3::Distance(context.LodView.Position, cluster->TotalBoundsSphere.Center - context.LodView.Origin) - (float)cluster->TotalBoundsSphere.Radius > cluster->MaxCullDistance)
|
||||
return;
|
||||
const Vector3 viewOrigin = renderContext.View.Origin;
|
||||
|
||||
//DebugDraw::DrawBox(cluster->Bounds, Color::Red);
|
||||
|
||||
// Draw visible children
|
||||
@@ -178,10 +174,10 @@ void Foliage::DrawCluster(RenderContext& renderContext, FoliageCluster* cluster,
|
||||
BoundingBox box;
|
||||
#define DRAW_CLUSTER(idx) \
|
||||
box = cluster->Children[idx]->TotalBounds; \
|
||||
box.Minimum -= viewOrigin; \
|
||||
box.Maximum -= viewOrigin; \
|
||||
if (renderContext.View.CullingFrustum.Intersects(box)) \
|
||||
DrawCluster(renderContext, cluster->Children[idx], type, drawCallsLists, result)
|
||||
box.Minimum -= context.ViewOrigin; \
|
||||
box.Maximum -= context.ViewOrigin; \
|
||||
if (context.RenderContext.View.CullingFrustum.Intersects(box)) \
|
||||
DrawCluster(context, cluster->Children[idx], drawCallsLists, result)
|
||||
DRAW_CLUSTER(0);
|
||||
DRAW_CLUSTER(1);
|
||||
DRAW_CLUSTER(2);
|
||||
@@ -192,21 +188,22 @@ void Foliage::DrawCluster(RenderContext& renderContext, FoliageCluster* cluster,
|
||||
{
|
||||
// Draw visible instances
|
||||
const auto frame = Engine::FrameCount;
|
||||
const auto model = type.Model.Get();
|
||||
const auto transitionLOD = renderContext.View.Pass != DrawPass::Depth; // Let the main view pass update LOD transitions
|
||||
const auto model = context.FoliageType.Model.Get();
|
||||
const auto transitionLOD = context.RenderContext.View.Pass != DrawPass::Depth; // Let the main view pass update LOD transitions
|
||||
// TODO: move DrawState to be stored per-view (so shadows can fade objects on their own)
|
||||
for (int32 i = 0; i < cluster->Instances.Count(); i++)
|
||||
{
|
||||
auto& instance = *cluster->Instances.Get()[i];
|
||||
BoundingSphere sphere = instance.Bounds;
|
||||
sphere.Center -= viewOrigin;
|
||||
if (Float3::Distance(lodView->Position, sphere.Center) - (float)sphere.Radius < instance.CullDistance &&
|
||||
renderContext.View.CullingFrustum.Intersects(sphere))
|
||||
sphere.Center -= context.ViewOrigin;
|
||||
if (Float3::Distance(context.LodView.Position, sphere.Center) - (float)sphere.Radius < instance.CullDistance &&
|
||||
context.RenderContext.View.CullingFrustum.Intersects(sphere) &&
|
||||
RenderTools::ComputeBoundsScreenRadiusSquared(sphere.Center, sphere.Radius, context.RenderContext.View) * context.ViewScreenSizeSq >= context.MinObjectPixelSizeSq)
|
||||
{
|
||||
const auto modelFrame = instance.DrawState.PrevFrame + 1;
|
||||
|
||||
// Select a proper LOD index (model may be culled)
|
||||
int32 lodIndex = RenderTools::ComputeModelLOD(model, sphere.Center, (float)sphere.Radius, renderContext);
|
||||
int32 lodIndex = RenderTools::ComputeModelLOD(model, sphere.Center, (float)sphere.Radius, context.RenderContext);
|
||||
if (lodIndex == -1)
|
||||
{
|
||||
// Handling model fade-out transition
|
||||
@@ -231,20 +228,20 @@ void Foliage::DrawCluster(RenderContext& renderContext, FoliageCluster* cluster,
|
||||
{
|
||||
const auto prevLOD = model->ClampLODIndex(instance.DrawState.PrevLOD);
|
||||
const float normalizedProgress = static_cast<float>(instance.DrawState.LODTransition) * (1.0f / 255.0f);
|
||||
DrawInstance(renderContext, instance, type, model, prevLOD, normalizedProgress, drawCallsLists, result);
|
||||
DrawInstance(context, instance, model, prevLOD, normalizedProgress, drawCallsLists, result);
|
||||
}
|
||||
}
|
||||
else if (instance.DrawState.LODTransition < 255)
|
||||
{
|
||||
const auto prevLOD = model->ClampLODIndex(instance.DrawState.PrevLOD);
|
||||
const float normalizedProgress = static_cast<float>(instance.DrawState.LODTransition) * (1.0f / 255.0f);
|
||||
DrawInstance(renderContext, instance, type, model, prevLOD, normalizedProgress, drawCallsLists, result);
|
||||
DrawInstance(context, instance, model, prevLOD, normalizedProgress, drawCallsLists, result);
|
||||
}
|
||||
}
|
||||
instance.DrawState.PrevFrame = frame;
|
||||
continue;
|
||||
}
|
||||
lodIndex += renderContext.View.ModelLODBias;
|
||||
lodIndex += context.RenderContext.View.ModelLODBias;
|
||||
lodIndex = model->ClampLODIndex(lodIndex);
|
||||
|
||||
if (transitionLOD)
|
||||
@@ -278,19 +275,19 @@ void Foliage::DrawCluster(RenderContext& renderContext, FoliageCluster* cluster,
|
||||
// Draw
|
||||
if (instance.DrawState.PrevLOD == lodIndex)
|
||||
{
|
||||
DrawInstance(renderContext, instance, type, model, lodIndex, 0.0f, drawCallsLists, result);
|
||||
DrawInstance(context, instance, model, lodIndex, 0.0f, drawCallsLists, result);
|
||||
}
|
||||
else if (instance.DrawState.PrevLOD == -1)
|
||||
{
|
||||
const float normalizedProgress = static_cast<float>(instance.DrawState.LODTransition) * (1.0f / 255.0f);
|
||||
DrawInstance(renderContext, instance, type, model, lodIndex, 1.0f - normalizedProgress, drawCallsLists, result);
|
||||
DrawInstance(context, instance, model, lodIndex, 1.0f - normalizedProgress, drawCallsLists, result);
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto prevLOD = model->ClampLODIndex(instance.DrawState.PrevLOD);
|
||||
const float normalizedProgress = static_cast<float>(instance.DrawState.LODTransition) * (1.0f / 255.0f);
|
||||
DrawInstance(renderContext, instance, type, model, prevLOD, normalizedProgress, drawCallsLists, result);
|
||||
DrawInstance(renderContext, instance, type, model, lodIndex, normalizedProgress - 1.0f, drawCallsLists, result);
|
||||
DrawInstance(context, instance, model, prevLOD, normalizedProgress, drawCallsLists, result);
|
||||
DrawInstance(context, instance, model, lodIndex, normalizedProgress - 1.0f, drawCallsLists, result);
|
||||
}
|
||||
|
||||
//DebugDraw::DrawSphere(instance.Bounds, Color::YellowGreen);
|
||||
@@ -304,14 +301,11 @@ void Foliage::DrawCluster(RenderContext& renderContext, FoliageCluster* cluster,
|
||||
|
||||
#else
|
||||
|
||||
void Foliage::DrawCluster(RenderContext& renderContext, FoliageCluster* cluster, Mesh::DrawInfo& draw)
|
||||
void Foliage::DrawCluster(DrawContext& context, FoliageCluster* cluster, Mesh::DrawInfo& draw)
|
||||
{
|
||||
// Skip clusters that around too far from view
|
||||
const auto lodView = (renderContext.LodProxyView ? renderContext.LodProxyView : &renderContext.View);
|
||||
if (Float3::Distance(lodView->Position, cluster->TotalBoundsSphere.Center - lodView->Origin) - (float)cluster->TotalBoundsSphere.Radius > cluster->MaxCullDistance)
|
||||
if (Float3::Distance(context.LodView.Position, cluster->TotalBoundsSphere.Center - context.LodView.Origin) - (float)cluster->TotalBoundsSphere.Radius > cluster->MaxCullDistance)
|
||||
return;
|
||||
const Vector3 viewOrigin = renderContext.View.Origin;
|
||||
|
||||
//DebugDraw::DrawBox(cluster->Bounds, Color::Red);
|
||||
|
||||
// Draw visible children
|
||||
@@ -323,10 +317,10 @@ void Foliage::DrawCluster(RenderContext& renderContext, FoliageCluster* cluster,
|
||||
BoundingBox box;
|
||||
#define DRAW_CLUSTER(idx) \
|
||||
box = cluster->Children[idx]->TotalBounds; \
|
||||
box.Minimum -= viewOrigin; \
|
||||
box.Maximum -= viewOrigin; \
|
||||
if (renderContext.View.CullingFrustum.Intersects(box)) \
|
||||
DrawCluster(renderContext, cluster->Children[idx], draw)
|
||||
box.Minimum -= context.ViewOrigin; \
|
||||
box.Maximum -= context.ViewOrigin; \
|
||||
if (context.RenderContext.View.CullingFrustum.Intersects(box)) \
|
||||
DrawCluster(context, cluster->Children[idx], draw)
|
||||
DRAW_CLUSTER(0);
|
||||
DRAW_CLUSTER(1);
|
||||
DRAW_CLUSTER(2);
|
||||
@@ -342,16 +336,17 @@ void Foliage::DrawCluster(RenderContext& renderContext, FoliageCluster* cluster,
|
||||
auto& instance = *cluster->Instances[i];
|
||||
auto& type = FoliageTypes[instance.Type];
|
||||
BoundingSphere sphere = instance.Bounds;
|
||||
sphere.Center -= viewOrigin;
|
||||
sphere.Center -= context.ViewOrigin;
|
||||
|
||||
// Check if can draw this instance
|
||||
if (type._canDraw &&
|
||||
Float3::Distance(lodView->Position, sphere.Center) - (float)sphere.Radius < instance.CullDistance &&
|
||||
renderContext.View.CullingFrustum.Intersects(sphere))
|
||||
Float3::Distance(context.LodView.Position, sphere.Center) - (float)sphere.Radius < instance.CullDistance &&
|
||||
context.RenderContext.View.CullingFrustum.Intersects(sphere) &&
|
||||
RenderTools::ComputeBoundsScreenRadiusSquared(sphere.Center, sphere.Radius, context.RenderContext.View) * context.ViewScreenSizeSq >= context.MinObjectPixelSizeSq)
|
||||
{
|
||||
Matrix world;
|
||||
const Transform transform = _transform.LocalToWorld(instance.Transform);
|
||||
const Float3 translation = transform.Translation - renderContext.View.Origin;
|
||||
const Float3 translation = transform.Translation - context.ViewOrigin;
|
||||
Matrix::Transformation(transform.Scale, transform.Orientation, translation, world);
|
||||
|
||||
// Disable motion blur
|
||||
@@ -367,7 +362,7 @@ void Foliage::DrawCluster(RenderContext& renderContext, FoliageCluster* cluster,
|
||||
draw.PerInstanceRandom = instance.Random;
|
||||
draw.DrawModes = type.DrawModes;
|
||||
draw.SetStencilValue(_layer);
|
||||
type.Model->Draw(renderContext, draw);
|
||||
type.Model->Draw(context.RenderContext, draw);
|
||||
|
||||
//DebugDraw::DrawSphere(instance.Bounds, Color::YellowGreen);
|
||||
|
||||
@@ -446,22 +441,50 @@ void Foliage::DrawFoliageJob(int32 i)
|
||||
PROFILE_CPU();
|
||||
PROFILE_MEM(Graphics);
|
||||
const FoliageType& type = FoliageTypes[i];
|
||||
if (type.IsReady() && type.Model->CanBeRendered())
|
||||
if (type._canDraw)
|
||||
{
|
||||
DrawCallsList drawCallsLists[MODEL_MAX_LODS];
|
||||
for (RenderContext& renderContext : _renderContextBatch->Contexts)
|
||||
{
|
||||
#if !FOLIAGE_USE_SINGLE_QUAD_TREE && FOLIAGE_USE_DRAW_CALLS_BATCHING
|
||||
DrawType(renderContext, type, drawCallsLists);
|
||||
#else
|
||||
Mesh::DrawInfo draw;
|
||||
draw.Flags = GetStaticFlags();
|
||||
draw.DrawModes = (DrawPass)(DrawPass::Default & renderContext.View.Pass);
|
||||
draw.LODBias = 0;
|
||||
draw.ForcedLOD = -1;
|
||||
draw.VertexColors = nullptr;
|
||||
draw.Deformation = nullptr;
|
||||
DrawType(renderContext, type, draw);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if !FOLIAGE_USE_SINGLE_QUAD_TREE && FOLIAGE_USE_DRAW_CALLS_BATCHING
|
||||
void Foliage::DrawType(RenderContext& renderContext, const FoliageType& type, DrawCallsList* drawCallsLists)
|
||||
#else
|
||||
void Foliage::DrawType(RenderContext& renderContext, const FoliageType& type, Mesh::DrawInfo& draw)
|
||||
#endif
|
||||
{
|
||||
if (!type.Root || !FOLIAGE_CAN_DRAW(renderContext, type))
|
||||
return;
|
||||
const DrawPass typeDrawModes = FOLIAGE_GET_DRAW_MODES(renderContext, type);
|
||||
PROFILE_CPU_ASSET(type.Model);
|
||||
DrawContext context
|
||||
{
|
||||
renderContext,
|
||||
renderContext.LodProxyView ? *renderContext.LodProxyView : renderContext.View,
|
||||
type,
|
||||
renderContext.View.Origin,
|
||||
Math::Square(Graphics::Shadows::MinObjectPixelSize),
|
||||
renderContext.View.ScreenSize.X * renderContext.View.ScreenSize.Y,
|
||||
};
|
||||
if (context.RenderContext.View.Pass != DrawPass::Depth)
|
||||
context.MinObjectPixelSizeSq = 0.0f; // Don't use it in main view
|
||||
#if FOLIAGE_USE_DRAW_CALLS_BATCHING
|
||||
// Initialize draw calls for foliage type all LODs meshes
|
||||
for (int32 lod = 0; lod < type.Model->LODs.Count(); lod++)
|
||||
@@ -506,7 +529,7 @@ void Foliage::DrawType(RenderContext& renderContext, const FoliageType& type, Dr
|
||||
|
||||
// Draw instances of the foliage type
|
||||
BatchedDrawCalls result(&renderContext.List->Memory);
|
||||
DrawCluster(renderContext, type.Root, type, drawCallsLists, result);
|
||||
DrawCluster(context, type.Root, drawCallsLists, result);
|
||||
|
||||
// Submit draw calls with valid instances added
|
||||
for (auto& e : result)
|
||||
@@ -568,10 +591,22 @@ void Foliage::DrawType(RenderContext& renderContext, const FoliageType& type, Dr
|
||||
}
|
||||
}
|
||||
#else
|
||||
DrawCluster(renderContext, type.Root, draw);
|
||||
DrawCluster(context, type.Root, draw);
|
||||
#endif
|
||||
}
|
||||
|
||||
void Foliage::InitType(const RenderView& view, FoliageType& type)
|
||||
{
|
||||
const DrawPass drawModes = type.DrawModes & view.Pass & view.GetShadowsDrawPassMask(type.ShadowsMode);
|
||||
type._canDraw = type.IsReady() && drawModes != DrawPass::None && type.Model && type.Model->CanBeRendered();
|
||||
for (int32 j = 0; j < type.Entries.Count(); j++)
|
||||
{
|
||||
auto& e = type.Entries[j];
|
||||
e.ReceiveDecals = type.ReceiveDecals != 0;
|
||||
e.ShadowsMode = type.ShadowsMode;
|
||||
}
|
||||
}
|
||||
|
||||
int32 Foliage::GetInstancesCount() const
|
||||
{
|
||||
return Instances.Count();
|
||||
@@ -1131,12 +1166,7 @@ void Foliage::Draw(RenderContext& renderContext)
|
||||
// Cache data per foliage instance type
|
||||
for (auto& type : FoliageTypes)
|
||||
{
|
||||
for (int32 j = 0; j < type.Entries.Count(); j++)
|
||||
{
|
||||
auto& e = type.Entries[j];
|
||||
e.ReceiveDecals = type.ReceiveDecals != 0;
|
||||
e.ShadowsMode = type.ShadowsMode;
|
||||
}
|
||||
InitType(renderContext.View, type);
|
||||
}
|
||||
|
||||
if (renderContext.View.Pass == DrawPass::GlobalSDF)
|
||||
@@ -1202,12 +1232,7 @@ void Foliage::Draw(RenderContext& renderContext)
|
||||
// Draw single foliage instance projection into Global Surface Atlas
|
||||
auto& instance = *(FoliageInstance*)GlobalSurfaceAtlasPass::Instance()->GetCurrentActorObject();
|
||||
auto& type = FoliageTypes[instance.Type];
|
||||
for (int32 i = 0; i < type.Entries.Count(); i++)
|
||||
{
|
||||
auto& e = type.Entries[i];
|
||||
e.ReceiveDecals = type.ReceiveDecals != 0;
|
||||
e.ShadowsMode = type.ShadowsMode;
|
||||
}
|
||||
InitType(renderContext.View, type);
|
||||
Matrix world;
|
||||
const Transform transform = _transform.LocalToWorld(instance.Transform);
|
||||
renderContext.View.GetWorldMatrix(transform, world);
|
||||
@@ -1239,8 +1264,9 @@ void Foliage::Draw(RenderContext& renderContext)
|
||||
draw.LODBias = 0;
|
||||
draw.ForcedLOD = -1;
|
||||
draw.VertexColors = nullptr;
|
||||
draw.Deformation = nullptr;
|
||||
#else
|
||||
DrawCallsList drawCallsLists[MODEL_MAX_LODS];
|
||||
DrawCallsList draw[MODEL_MAX_LODS];
|
||||
#endif
|
||||
#if FOLIAGE_USE_SINGLE_QUAD_TREE
|
||||
if (Root)
|
||||
@@ -1248,7 +1274,7 @@ void Foliage::Draw(RenderContext& renderContext)
|
||||
#else
|
||||
for (auto& type : FoliageTypes)
|
||||
{
|
||||
DrawType(renderContext, type, drawCallsLists);
|
||||
DrawType(renderContext, type, draw);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -1265,14 +1291,7 @@ void Foliage::Draw(RenderContextBatch& renderContextBatch)
|
||||
{
|
||||
// Cache data per foliage instance type
|
||||
for (FoliageType& type : FoliageTypes)
|
||||
{
|
||||
for (int32 j = 0; j < type.Entries.Count(); j++)
|
||||
{
|
||||
auto& e = type.Entries[j];
|
||||
e.ReceiveDecals = type.ReceiveDecals != 0;
|
||||
e.ShadowsMode = type.ShadowsMode;
|
||||
}
|
||||
}
|
||||
InitType(view, type);
|
||||
|
||||
// Run async job for each foliage type
|
||||
_renderContextBatch = &renderContextBatch;
|
||||
|
||||
@@ -158,6 +158,15 @@ public:
|
||||
|
||||
private:
|
||||
void AddToCluster(ChunkedArray<FoliageCluster, FOLIAGE_CLUSTER_CHUNKS_SIZE>& clusters, FoliageCluster* cluster, FoliageInstance& instance);
|
||||
struct DrawContext
|
||||
{
|
||||
RenderContext& RenderContext;
|
||||
const RenderView& LodView;
|
||||
const FoliageType& FoliageType;
|
||||
Vector3 ViewOrigin;
|
||||
float MinObjectPixelSizeSq;
|
||||
float ViewScreenSizeSq;
|
||||
};
|
||||
#if !FOLIAGE_USE_SINGLE_QUAD_TREE && FOLIAGE_USE_DRAW_CALLS_BATCHING
|
||||
struct DrawKey
|
||||
{
|
||||
@@ -181,10 +190,12 @@ private:
|
||||
|
||||
typedef Array<struct DrawCall, InlinedAllocation<8>> DrawCallsList;
|
||||
typedef Dictionary<DrawKey, struct BatchedDrawCall, ConcurrentArenaAllocation> BatchedDrawCalls;
|
||||
void DrawInstance(RenderContext& renderContext, FoliageInstance& instance, const FoliageType& type, Model* model, int32 lod, float lodDitherFactor, DrawCallsList* drawCallsLists, BatchedDrawCalls& result) const;
|
||||
void DrawCluster(RenderContext& renderContext, FoliageCluster* cluster, const FoliageType& type, DrawCallsList* drawCallsLists, BatchedDrawCalls& result) const;
|
||||
void DrawInstance(DrawContext& context, FoliageInstance& instance, Model* model, int32 lod, float lodDitherFactor, DrawCallsList* drawCallsLists, BatchedDrawCalls& result) const;
|
||||
void DrawCluster(DrawContext& context, FoliageCluster* cluster, DrawCallsList* drawCallsLists, BatchedDrawCalls& result) const;
|
||||
void DrawType(RenderContext& renderContext, const FoliageType& type, DrawCallsList* drawCallsLists);
|
||||
#else
|
||||
void DrawCluster(RenderContext& renderContext, FoliageCluster* cluster, Mesh::DrawInfo& draw);
|
||||
void DrawCluster(DrawContext& context, FoliageCluster* cluster, Mesh::DrawInfo& draw);
|
||||
void DrawType(RenderContext& renderContext, const FoliageType& type, Mesh::DrawInfo& draw);
|
||||
#endif
|
||||
#if !FOLIAGE_USE_SINGLE_QUAD_TREE
|
||||
void DrawClusterGlobalSDF(class GlobalSignDistanceFieldPass* globalSDF, const BoundingBox& globalSDFBounds, FoliageCluster* cluster, const FoliageType& type);
|
||||
@@ -192,7 +203,8 @@ private:
|
||||
void DrawFoliageJob(int32 i);
|
||||
RenderContextBatch* _renderContextBatch;
|
||||
#endif
|
||||
void DrawType(RenderContext& renderContext, const FoliageType& type, DrawCallsList* drawCallsLists);
|
||||
|
||||
void InitType(const RenderView& view, FoliageType& type);
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
|
||||
@@ -47,6 +47,7 @@ API_CLASS(Sealed, NoSpawn) class FLAXENGINE_API FoliageType : public ScriptingOb
|
||||
friend Foliage;
|
||||
private:
|
||||
uint8 _isReady : 1;
|
||||
uint8 _canDraw : 1;
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
|
||||
@@ -32,6 +32,7 @@ bool Graphics::SpreadWorkload = true;
|
||||
#if !BUILD_RELEASE || USE_EDITOR
|
||||
float Graphics::TestValue = 0.0f;
|
||||
#endif
|
||||
float Graphics::Shadows::MinObjectPixelSize = 2.0f;
|
||||
bool Graphics::PostProcessing::ColorGradingVolumeLUT = true;
|
||||
|
||||
#if GRAPHICS_API_NULL
|
||||
|
||||
@@ -101,8 +101,17 @@ public:
|
||||
#endif
|
||||
|
||||
public:
|
||||
// Shadows rendering configuration.
|
||||
API_CLASS(Static, Attributes="DebugCommand") class FLAXENGINE_API Shadows
|
||||
{
|
||||
DECLARE_SCRIPTING_TYPE_MINIMAL(Shadows);
|
||||
|
||||
// The minimum size in pixels of objects to cast shadows. Improves performance by skipping too small objects (eg. sub-pixel) from rendering into shadow maps.
|
||||
API_FIELD() static float MinObjectPixelSize;
|
||||
};
|
||||
|
||||
// Post Processing effects rendering configuration.
|
||||
API_CLASS(Static, Attributes = "DebugCommand") class FLAXENGINE_API PostProcessing
|
||||
API_CLASS(Static, Attributes="DebugCommand") class FLAXENGINE_API PostProcessing
|
||||
{
|
||||
DECLARE_SCRIPTING_TYPE_MINIMAL(PostProcessing);
|
||||
|
||||
|
||||
@@ -20,15 +20,6 @@ void CmdBufferVulkan::AddWaitSemaphore(VkPipelineStageFlags waitFlags, Semaphore
|
||||
_waitSemaphores.Add(waitSemaphore);
|
||||
}
|
||||
|
||||
void CmdBufferVulkan::Wait(float timeInSecondsToWait)
|
||||
{
|
||||
if (!IsSubmitted())
|
||||
return;
|
||||
const bool failed = _device->FenceManager.WaitForFence(_fence, (uint64)(timeInSecondsToWait * 1e9));
|
||||
ASSERT(!failed);
|
||||
RefreshFenceStatus();
|
||||
}
|
||||
|
||||
void CmdBufferVulkan::Begin()
|
||||
{
|
||||
PROFILE_CPU();
|
||||
@@ -145,6 +136,16 @@ void CmdBufferVulkan::EndEvent()
|
||||
|
||||
#endif
|
||||
|
||||
void CmdBufferVulkan::Wait(float timeoutSeconds)
|
||||
{
|
||||
if (!IsSubmitted())
|
||||
return;
|
||||
PROFILE_CPU();
|
||||
const bool failed = _device->FenceManager.WaitForFence(GetFence(), timeoutSeconds);
|
||||
ASSERT(!failed);
|
||||
RefreshFenceStatus();
|
||||
}
|
||||
|
||||
void CmdBufferVulkan::RefreshFenceStatus()
|
||||
{
|
||||
if (_state == State::Submitted)
|
||||
@@ -306,14 +307,6 @@ void CmdBufferManagerVulkan::SubmitActiveCmdBuffer(SemaphoreVulkan* signalSemaph
|
||||
_activeCmdBuffer = nullptr;
|
||||
}
|
||||
|
||||
void CmdBufferManagerVulkan::WaitForCmdBuffer(CmdBufferVulkan* cmdBuffer, float timeInSecondsToWait)
|
||||
{
|
||||
ASSERT(cmdBuffer->IsSubmitted());
|
||||
const bool failed = _device->FenceManager.WaitForFence(cmdBuffer->GetFence(), (uint64)(timeInSecondsToWait * 1e9));
|
||||
ASSERT(!failed);
|
||||
cmdBuffer->RefreshFenceStatus();
|
||||
}
|
||||
|
||||
void CmdBufferManagerVulkan::GetNewActiveCommandBuffer()
|
||||
{
|
||||
PROFILE_CPU();
|
||||
|
||||
@@ -119,7 +119,6 @@ public:
|
||||
|
||||
public:
|
||||
void AddWaitSemaphore(VkPipelineStageFlags waitFlags, SemaphoreVulkan* waitSemaphore);
|
||||
void Wait(float timeInSecondsToWait = 1.0f);
|
||||
|
||||
void Begin();
|
||||
void End();
|
||||
@@ -137,6 +136,7 @@ public:
|
||||
void EndEvent();
|
||||
#endif
|
||||
|
||||
void Wait(float timeoutSeconds = VULKAN_WAIT_TIMEOUT);
|
||||
void RefreshFenceStatus();
|
||||
};
|
||||
|
||||
@@ -210,8 +210,7 @@ public:
|
||||
|
||||
public:
|
||||
void SubmitActiveCmdBuffer(SemaphoreVulkan* signalSemaphore = nullptr);
|
||||
void WaitForCmdBuffer(CmdBufferVulkan* cmdBuffer, float timeInSecondsToWait = 1.0f);
|
||||
void RefreshFenceStatus(const CmdBufferVulkan* skipCmdBuffer = nullptr)
|
||||
void RefreshFenceStatus(CmdBufferVulkan* skipCmdBuffer = nullptr)
|
||||
{
|
||||
_pool.RefreshFenceStatus(skipCmdBuffer);
|
||||
}
|
||||
|
||||
@@ -49,6 +49,11 @@
|
||||
#define VULKAN_USE_TIMER_QUERIES 1
|
||||
#endif
|
||||
|
||||
// Fence wait operation timeout in seconds
|
||||
#ifndef VULKAN_WAIT_TIMEOUT
|
||||
#define VULKAN_WAIT_TIMEOUT 5.0f
|
||||
#endif
|
||||
|
||||
// Toggles GPUTimerQueryVulkan to use BeginQuery/EndQuery via GPuContext rather than old custom implementation
|
||||
#define GPU_VULKAN_QUERY_NEW 1
|
||||
|
||||
|
||||
@@ -2253,16 +2253,15 @@ FenceVulkan* FenceManagerVulkan::AllocateFence(bool createSignaled)
|
||||
return fence;
|
||||
}
|
||||
|
||||
bool FenceManagerVulkan::WaitForFence(FenceVulkan* fence, uint64 timeInNanoseconds) const
|
||||
bool FenceManagerVulkan::WaitForFence(FenceVulkan* fence, float timeoutSeconds) const
|
||||
{
|
||||
if (fence->IsSignaled)
|
||||
return false;
|
||||
PROFILE_CPU();
|
||||
ZoneColor(TracyWaitZoneColor);
|
||||
ASSERT(_usedFences.Contains(fence));
|
||||
if (timeInNanoseconds)
|
||||
timeInNanoseconds = 1000ll * 1000ll * 1000LL; // 1s
|
||||
const VkResult result = vkWaitForFences(_device->Device, 1, &fence->Handle, true, timeInNanoseconds);
|
||||
uint64 timeNanoseconds = (uint64)((double)timeoutSeconds * 1000000000.0);
|
||||
const VkResult result = vkWaitForFences(_device->Device, 1, &fence->Handle, true, timeNanoseconds);
|
||||
LOG_VULKAN_RESULT(result);
|
||||
if (result == VK_SUCCESS)
|
||||
{
|
||||
@@ -2290,11 +2289,11 @@ void FenceManagerVulkan::ReleaseFence(FenceVulkan*& fence)
|
||||
fence = nullptr;
|
||||
}
|
||||
|
||||
void FenceManagerVulkan::WaitAndReleaseFence(FenceVulkan*& fence, uint64 timeInNanoseconds)
|
||||
void FenceManagerVulkan::WaitAndReleaseFence(FenceVulkan*& fence, float timeoutSeconds)
|
||||
{
|
||||
ScopeLock lock(_device->_fenceLock);
|
||||
if (!fence->IsSignaled)
|
||||
WaitForFence(fence, timeInNanoseconds);
|
||||
WaitForFence(fence, timeoutSeconds);
|
||||
ResetFence(fence);
|
||||
_usedFences.Remove(fence);
|
||||
_freeFences.Add(fence);
|
||||
|
||||
@@ -106,7 +106,7 @@ public:
|
||||
}
|
||||
|
||||
// Returns true if waiting timed out or failed, false otherwise.
|
||||
bool WaitForFence(FenceVulkan* fence, uint64 timeInNanoseconds = 0) const;
|
||||
bool WaitForFence(FenceVulkan* fence, float timeoutSeconds = VULKAN_WAIT_TIMEOUT) const;
|
||||
|
||||
void ResetFence(FenceVulkan* fence) const;
|
||||
|
||||
@@ -114,7 +114,7 @@ public:
|
||||
void ReleaseFence(FenceVulkan*& fence);
|
||||
|
||||
// Sets the fence handle to null
|
||||
void WaitAndReleaseFence(FenceVulkan*& fence, uint64 timeInNanoseconds = 0);
|
||||
void WaitAndReleaseFence(FenceVulkan*& fence, float timeoutSeconds = VULKAN_WAIT_TIMEOUT);
|
||||
|
||||
private:
|
||||
// Returns true if fence was signaled, otherwise false.
|
||||
|
||||
@@ -62,10 +62,7 @@ void QueueVulkan::Submit(CmdBufferVulkan* cmdBuffer, uint32 signalSemaphoresCoun
|
||||
const bool WaitForIdleOnSubmit = false;
|
||||
if (WaitForIdleOnSubmit)
|
||||
{
|
||||
bool success = _device->FenceManager.WaitForFence(fence);
|
||||
ASSERT(success);
|
||||
ASSERT(_device->FenceManager.IsFenceSignaled(fence));
|
||||
cmdBuffer->GetOwner()->RefreshFenceStatus();
|
||||
cmdBuffer->Wait();
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@@ -2485,7 +2485,7 @@ void PhysicsBackend::SetRigidActorPose(void* actor, const Vector3& position, con
|
||||
if (kinematic)
|
||||
{
|
||||
auto actorPhysX = (PxRigidDynamic*)actor;
|
||||
if (actorPhysX->getActorFlags() & PxActorFlag::eDISABLE_SIMULATION)
|
||||
if (actorPhysX->getActorFlags() & PxActorFlag::eDISABLE_SIMULATION || !(actorPhysX->getRigidBodyFlags() & PxRigidBodyFlag::eKINEMATIC))
|
||||
{
|
||||
// Ensures the disabled kinematic actor ends up in the correct pose after enabling simulation
|
||||
actorPhysX->setGlobalPose(trans, wakeUp);
|
||||
|
||||
@@ -731,6 +731,7 @@ void RenderList::AddDrawCall(const RenderContextBatch& renderContextBatch, DrawP
|
||||
DrawCallsLists[(int32)DrawCallsListType::MotionVectors].Indices.Add(index);
|
||||
}
|
||||
}
|
||||
float minObjectPixelSizeSq = Math::Square(Graphics::Shadows::MinObjectPixelSize);
|
||||
for (int32 i = 1; i < renderContextBatch.Contexts.Count(); i++)
|
||||
{
|
||||
const RenderContext& renderContext = renderContextBatch.Contexts.Get()[i];
|
||||
@@ -738,7 +739,8 @@ void RenderList::AddDrawCall(const RenderContextBatch& renderContextBatch, DrawP
|
||||
drawModes = modes & renderContext.View.Pass;
|
||||
if (drawModes != DrawPass::None &&
|
||||
(staticFlags & renderContext.View.StaticFlagsMask) == renderContext.View.StaticFlagsCompare &&
|
||||
renderContext.View.CullingFrustum.Intersects(bounds))
|
||||
renderContext.View.CullingFrustum.Intersects(bounds) &&
|
||||
RenderTools::ComputeBoundsScreenRadiusSquared(bounds.Center, bounds.Radius, renderContext.View) * (renderContext.View.ScreenSize.X * renderContext.View.ScreenSize.Y) >= minObjectPixelSizeSq)
|
||||
{
|
||||
renderContext.List->ShadowDepthDrawCallsList.Indices.Add(index);
|
||||
}
|
||||
|
||||
@@ -437,8 +437,8 @@ namespace FlaxEngine.GUI
|
||||
// Caret
|
||||
if (IsFocused && CaretPosition > -1)
|
||||
{
|
||||
float alpha = Mathf.Saturate(Mathf.Cos(_animateTime * CaretFlashSpeed) * 0.5f + 0.7f);
|
||||
alpha = alpha * alpha * alpha * alpha * alpha * alpha;
|
||||
float alpha = Mathf.Saturate(Mathf.Cos(_animateTime * CaretFlashSpeed) * 0.5f + 0.8f);
|
||||
alpha = alpha * alpha;
|
||||
Render2D.FillRectangle(CaretBounds, CaretColor * alpha);
|
||||
}
|
||||
|
||||
|
||||
@@ -310,8 +310,8 @@ namespace FlaxEngine.GUI
|
||||
// Caret
|
||||
if (IsFocused && CaretPosition > -1)
|
||||
{
|
||||
float alpha = Mathf.Saturate(Mathf.Cos(_animateTime * CaretFlashSpeed) * 0.5f + 0.7f);
|
||||
alpha = alpha * alpha * alpha * alpha * alpha * alpha;
|
||||
float alpha = Mathf.Saturate(Mathf.Cos(_animateTime * CaretFlashSpeed) * 0.5f + 0.8f);
|
||||
alpha = alpha * alpha;
|
||||
Render2D.FillRectangle(CaretBounds, CaretColor * alpha);
|
||||
}
|
||||
|
||||
|
||||
@@ -191,14 +191,18 @@ namespace FlaxEngine.GUI
|
||||
/// <summary>
|
||||
/// Gets or sets the maximum number of characters the user can type into the text box control.
|
||||
/// </summary>
|
||||
[EditorOrder(50), Tooltip("The maximum number of characters the user can type into the text box control.")]
|
||||
[EditorOrder(50), Limit(-1), Tooltip("The maximum number of characters the user can type into the text box control.")]
|
||||
public int MaxLength
|
||||
{
|
||||
get => _maxLength;
|
||||
set
|
||||
{
|
||||
if (_maxLength <= 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(MaxLength));
|
||||
// Cap at min of -1 for no max length
|
||||
if (value <= 0)
|
||||
{
|
||||
_maxLength = -1;
|
||||
return;
|
||||
}
|
||||
|
||||
if (_maxLength != value)
|
||||
{
|
||||
@@ -275,7 +279,7 @@ namespace FlaxEngine.GUI
|
||||
/// Gets or sets the speed of the caret flashing animation.
|
||||
/// </summary>
|
||||
[EditorDisplay("Caret Style"), EditorOrder(2021), Tooltip("The speed of the caret flashing animation.")]
|
||||
public float CaretFlashSpeed { get; set; } = 6.0f;
|
||||
public float CaretFlashSpeed { get; set; } = 6.5f;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the speed of the selection background flashing animation.
|
||||
@@ -381,7 +385,7 @@ namespace FlaxEngine.GUI
|
||||
value = value.Replace(DelChar.ToString(), "");
|
||||
|
||||
// Clamp length
|
||||
if (value.Length > MaxLength)
|
||||
if (value.Length > MaxLength && MaxLength != -1)
|
||||
value = value.Substring(0, MaxLength);
|
||||
|
||||
// Ensure to use only single line
|
||||
@@ -514,7 +518,7 @@ namespace FlaxEngine.GUI
|
||||
AutoFocus = true;
|
||||
|
||||
_isMultiline = isMultiline;
|
||||
_maxLength = 2147483646;
|
||||
_maxLength = -1;
|
||||
_selectionStart = _selectionEnd = -1;
|
||||
|
||||
var style = Style.Current;
|
||||
@@ -710,7 +714,7 @@ namespace FlaxEngine.GUI
|
||||
str = str.Replace("\n", "");
|
||||
|
||||
int selectionLength = SelectionLength;
|
||||
int charactersLeft = MaxLength - _text.Length + selectionLength;
|
||||
int charactersLeft = (MaxLength != -1 ? MaxLength : int.MaxValue) - _text.Length + selectionLength;
|
||||
Assert.IsTrue(charactersLeft >= 0);
|
||||
if (charactersLeft == 0)
|
||||
return;
|
||||
|
||||
@@ -1300,10 +1300,10 @@ namespace Flax.Build.Bindings
|
||||
else if (parameterInfo.Type.IsRef && !parameterInfo.Type.IsConst)
|
||||
{
|
||||
// Non-const lvalue reference parameters needs to be passed via temporary value
|
||||
if (parameterInfo.IsOut || parameterInfo.IsRef)
|
||||
contents.Append(indent).AppendFormat("{2}& {0}Temp = {1};", parameterInfo.Name, param, parameterInfo.Type.ToString(false)).AppendLine();
|
||||
else
|
||||
if (parameterInfo.Type.Type is "String" or "StringView" or "StringAnsi" or "StringAnsiView" && parameterInfo.Type.GenericArgs == null)
|
||||
contents.Append(indent).AppendFormat("{2} {0}Temp = {1};", parameterInfo.Name, param, parameterInfo.Type.ToString(false)).AppendLine();
|
||||
else
|
||||
contents.Append(indent).AppendFormat("{2}& {0}Temp = {1};", parameterInfo.Name, param, parameterInfo.Type.ToString(false)).AppendLine();
|
||||
callParams += parameterInfo.Name;
|
||||
callParams += "Temp";
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user