Merge remote-tracking branch 'origin/master' into 1.9
# Conflicts: # Source/Engine/UI/GUI/Common/Button.cs
This commit is contained in:
@@ -296,6 +296,16 @@ namespace FlaxEditor.CustomEditors
|
||||
_values.Set(_parent.Values, value);
|
||||
}
|
||||
|
||||
private bool SyncParent()
|
||||
{
|
||||
// TODO: add attribute for types that want to sync their contents with a parent
|
||||
var type = Values.Type.Type;
|
||||
if (type == typeof(LocalizedString) ||
|
||||
type == typeof(FontReference))
|
||||
return true;
|
||||
return _parent != null && !(_parent is SyncPointEditor);
|
||||
}
|
||||
|
||||
internal virtual void RefreshInternal()
|
||||
{
|
||||
if (_values == null)
|
||||
@@ -317,7 +327,7 @@ namespace FlaxEditor.CustomEditors
|
||||
|
||||
// Propagate values up (eg. when member of structure gets modified, also structure should be updated as a part of the other object)
|
||||
var obj = _parent;
|
||||
while (obj._parent != null && !(obj._parent is SyncPointEditor))
|
||||
while (obj.SyncParent())
|
||||
{
|
||||
obj.Values.Set(obj._parent.Values, obj.Values);
|
||||
obj = obj._parent;
|
||||
|
||||
@@ -72,6 +72,8 @@ namespace FlaxEditor.CustomEditors
|
||||
return new GenericEditor();
|
||||
if (targetType.IsArray)
|
||||
{
|
||||
if (targetType.Type == null)
|
||||
return new ArrayEditor();
|
||||
if (targetType.Type.GetArrayRank() != 1)
|
||||
return new GenericEditor(); // Not-supported multidimensional array
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ using FlaxEditor.CustomEditors.Editors;
|
||||
using FlaxEditor.CustomEditors.Elements;
|
||||
using FlaxEditor.GUI;
|
||||
using FlaxEditor.GUI.ContextMenu;
|
||||
using FlaxEditor.GUI.Input;
|
||||
using FlaxEditor.Scripting;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
@@ -634,26 +635,29 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
LayoutElementsContainer vEl;
|
||||
Color axisColorX = ActorTransformEditor.AxisColorX;
|
||||
Color axisColorY = ActorTransformEditor.AxisColorY;
|
||||
FloatValueBox xV, yV, wV, hV;
|
||||
if (xEq)
|
||||
{
|
||||
xEl = UniformPanelCapsuleForObjectWithText(horUp, "X: ", xItem.GetValues(Values), axisColorX);
|
||||
vEl = UniformPanelCapsuleForObjectWithText(horDown, "Width: ", widthItem.GetValues(Values), axisColorX);
|
||||
xEl = UniformPanelCapsuleForObjectWithText(horUp, "X: ", xItem.GetValues(Values), axisColorX, out xV);
|
||||
vEl = UniformPanelCapsuleForObjectWithText(horDown, "Width: ", widthItem.GetValues(Values), axisColorX, out wV);
|
||||
}
|
||||
else
|
||||
{
|
||||
xEl = UniformPanelCapsuleForObjectWithText(horUp, "Left: ", leftItem.GetValues(Values), axisColorX);
|
||||
vEl = UniformPanelCapsuleForObjectWithText(horDown, "Right: ", rightItem.GetValues(Values), axisColorX);
|
||||
xEl = UniformPanelCapsuleForObjectWithText(horUp, "Left: ", leftItem.GetValues(Values), axisColorX, out xV);
|
||||
vEl = UniformPanelCapsuleForObjectWithText(horDown, "Right: ", rightItem.GetValues(Values), axisColorX, out wV);
|
||||
}
|
||||
if (yEq)
|
||||
{
|
||||
yEl = UniformPanelCapsuleForObjectWithText(horUp, "Y: ", yItem.GetValues(Values), axisColorY);
|
||||
hEl = UniformPanelCapsuleForObjectWithText(horDown, "Height: ", heightItem.GetValues(Values), axisColorY);
|
||||
yEl = UniformPanelCapsuleForObjectWithText(horUp, "Y: ", yItem.GetValues(Values), axisColorY, out yV);
|
||||
hEl = UniformPanelCapsuleForObjectWithText(horDown, "Height: ", heightItem.GetValues(Values), axisColorY, out hV);
|
||||
}
|
||||
else
|
||||
{
|
||||
yEl = UniformPanelCapsuleForObjectWithText(horUp, "Top: ", topItem.GetValues(Values), axisColorY);
|
||||
hEl = UniformPanelCapsuleForObjectWithText(horDown, "Bottom: ", bottomItem.GetValues(Values), axisColorY);
|
||||
yEl = UniformPanelCapsuleForObjectWithText(horUp, "Top: ", topItem.GetValues(Values), axisColorY, out yV);
|
||||
hEl = UniformPanelCapsuleForObjectWithText(horDown, "Bottom: ", bottomItem.GetValues(Values), axisColorY, out hV);
|
||||
}
|
||||
|
||||
// Anchors
|
||||
xEl.Control.AnchorMin = new Float2(0, xEl.Control.AnchorMin.Y);
|
||||
xEl.Control.AnchorMax = new Float2(0.5f, xEl.Control.AnchorMax.Y);
|
||||
|
||||
@@ -665,6 +669,15 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
|
||||
hEl.Control.AnchorMin = new Float2(0.5f, xEl.Control.AnchorMin.Y);
|
||||
hEl.Control.AnchorMax = new Float2(1, xEl.Control.AnchorMax.Y);
|
||||
|
||||
// Navigation path
|
||||
xV.NavTargetRight = yV;
|
||||
yV.NavTargetRight = wV;
|
||||
wV.NavTargetRight = hV;
|
||||
|
||||
yV.NavTargetLeft = xV;
|
||||
wV.NavTargetLeft = yV;
|
||||
hV.NavTargetLeft = wV;
|
||||
}
|
||||
|
||||
private VerticalPanelElement VerticalPanelWithoutMargin(LayoutElementsContainer cont)
|
||||
@@ -684,17 +697,19 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
return grid;
|
||||
}
|
||||
|
||||
private CustomElementsContainer<UniformGridPanel> UniformPanelCapsuleForObjectWithText(LayoutElementsContainer el, string text, ValueContainer values, Color borderColor)
|
||||
private CustomElementsContainer<UniformGridPanel> UniformPanelCapsuleForObjectWithText(LayoutElementsContainer el, string text, ValueContainer values, Color borderColor, out FloatValueBox valueBox)
|
||||
{
|
||||
valueBox = null;
|
||||
var grid = UniformGridTwoByOne(el);
|
||||
grid.CustomControl.SlotPadding = new Margin(5, 5, 1, 1);
|
||||
var label = grid.Label(text, TextAlignment.Far);
|
||||
var editor = grid.Object(values);
|
||||
if (editor is FloatEditor floatEditor && floatEditor.Element is FloatValueElement floatEditorElement)
|
||||
{
|
||||
valueBox = floatEditorElement.ValueBox;
|
||||
var back = FlaxEngine.GUI.Style.Current.TextBoxBackground;
|
||||
floatEditorElement.ValueBox.BorderColor = Color.Lerp(borderColor, back, ActorTransformEditor.AxisGreyOutFactor);
|
||||
floatEditorElement.ValueBox.BorderSelectedColor = borderColor;
|
||||
valueBox.BorderColor = Color.Lerp(borderColor, back, ActorTransformEditor.AxisGreyOutFactor);
|
||||
valueBox.BorderSelectedColor = borderColor;
|
||||
}
|
||||
return grid;
|
||||
}
|
||||
|
||||
@@ -69,7 +69,7 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
|
||||
private void OnValueChanged()
|
||||
{
|
||||
if (IsSetBlocked)
|
||||
if (IsSetBlocked || Values == null)
|
||||
return;
|
||||
|
||||
var isSliding = XElement.IsSliding || YElement.IsSliding;
|
||||
@@ -158,7 +158,7 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
|
||||
private void OnValueChanged()
|
||||
{
|
||||
if (IsSetBlocked)
|
||||
if (IsSetBlocked || Values == null)
|
||||
return;
|
||||
|
||||
var isSliding = XElement.IsSliding || YElement.IsSliding;
|
||||
@@ -247,7 +247,7 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
|
||||
private void OnValueChanged()
|
||||
{
|
||||
if (IsSetBlocked)
|
||||
if (IsSetBlocked || Values == null)
|
||||
return;
|
||||
|
||||
var isSliding = XElement.IsSliding || YElement.IsSliding;
|
||||
|
||||
@@ -143,7 +143,7 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
|
||||
private void OnValueChanged()
|
||||
{
|
||||
if (IsSetBlocked)
|
||||
if (IsSetBlocked || Values == null)
|
||||
return;
|
||||
|
||||
var xValue = XElement.ValueBox.Value;
|
||||
@@ -318,7 +318,7 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
|
||||
private void OnValueChanged()
|
||||
{
|
||||
if (IsSetBlocked)
|
||||
if (IsSetBlocked || Values == null)
|
||||
return;
|
||||
|
||||
var isSliding = XElement.IsSliding || YElement.IsSliding || ZElement.IsSliding;
|
||||
@@ -418,7 +418,7 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
|
||||
private void OnValueChanged()
|
||||
{
|
||||
if (IsSetBlocked)
|
||||
if (IsSetBlocked || Values == null)
|
||||
return;
|
||||
|
||||
var isSliding = XElement.IsSliding || YElement.IsSliding || ZElement.IsSliding;
|
||||
|
||||
@@ -89,7 +89,7 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
|
||||
private void OnValueChanged()
|
||||
{
|
||||
if (IsSetBlocked)
|
||||
if (IsSetBlocked || Values == null)
|
||||
return;
|
||||
|
||||
var isSliding = XElement.IsSliding || YElement.IsSliding || ZElement.IsSliding || WElement.IsSliding;
|
||||
@@ -200,7 +200,7 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
|
||||
private void OnValueChanged()
|
||||
{
|
||||
if (IsSetBlocked)
|
||||
if (IsSetBlocked || Values == null)
|
||||
return;
|
||||
|
||||
var isSliding = XElement.IsSliding || YElement.IsSliding || ZElement.IsSliding || WElement.IsSliding;
|
||||
@@ -311,7 +311,7 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
|
||||
private void OnValueChanged()
|
||||
{
|
||||
if (IsSetBlocked)
|
||||
if (IsSetBlocked || Values == null)
|
||||
return;
|
||||
|
||||
var isSliding = XElement.IsSliding || YElement.IsSliding || ZElement.IsSliding || WElement.IsSliding;
|
||||
|
||||
@@ -218,6 +218,13 @@ namespace FlaxEditor.GUI
|
||||
Render2D.FillRectangle(bounds, style.Selection);
|
||||
Render2D.DrawRectangle(bounds, style.SelectionBorder);
|
||||
}
|
||||
|
||||
// Navigation focus highlight
|
||||
if (IsNavFocused)
|
||||
{
|
||||
var bounds = new Rectangle(Float2.Zero, Size);
|
||||
Render2D.DrawRectangle(bounds, style.BackgroundSelected);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -286,35 +293,7 @@ namespace FlaxEditor.GUI
|
||||
else if (Button1Rect.Contains(location))
|
||||
{
|
||||
Focus();
|
||||
if (Validator.AssetType != ScriptType.Null)
|
||||
{
|
||||
// Show asset picker popup
|
||||
var popup = AssetSearchPopup.Show(this, Button1Rect.BottomLeft, Validator.IsValid, item =>
|
||||
{
|
||||
Validator.SelectedItem = item;
|
||||
RootWindow.Focus();
|
||||
Focus();
|
||||
});
|
||||
if (Validator.SelectedAsset != null)
|
||||
{
|
||||
var selectedAssetName = Path.GetFileNameWithoutExtension(Validator.SelectedAsset.Path);
|
||||
popup.ScrollToAndHighlightItemByName(selectedAssetName);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Show content item picker popup
|
||||
var popup = ContentSearchPopup.Show(this, Button1Rect.BottomLeft, Validator.IsValid, item =>
|
||||
{
|
||||
Validator.SelectedItem = item;
|
||||
RootWindow.Focus();
|
||||
Focus();
|
||||
});
|
||||
if (Validator.SelectedItem != null)
|
||||
{
|
||||
popup.ScrollToAndHighlightItemByName(Validator.SelectedItem.ShortName);
|
||||
}
|
||||
}
|
||||
OnSubmit();
|
||||
}
|
||||
else if (Validator.SelectedAsset != null || Validator.SelectedItem != null)
|
||||
{
|
||||
@@ -412,5 +391,41 @@ namespace FlaxEditor.GUI
|
||||
|
||||
return DragDropEffect.Move;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnSubmit()
|
||||
{
|
||||
base.OnSubmit();
|
||||
|
||||
if (Validator.AssetType != ScriptType.Null)
|
||||
{
|
||||
// Show asset picker popup
|
||||
var popup = AssetSearchPopup.Show(this, Button1Rect.BottomLeft, Validator.IsValid, item =>
|
||||
{
|
||||
Validator.SelectedItem = item;
|
||||
RootWindow.Focus();
|
||||
Focus();
|
||||
});
|
||||
if (Validator.SelectedAsset != null)
|
||||
{
|
||||
var selectedAssetName = Path.GetFileNameWithoutExtension(Validator.SelectedAsset.Path);
|
||||
popup.ScrollToAndHighlightItemByName(selectedAssetName);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Show content item picker popup
|
||||
var popup = ContentSearchPopup.Show(this, Button1Rect.BottomLeft, Validator.IsValid, item =>
|
||||
{
|
||||
Validator.SelectedItem = item;
|
||||
RootWindow.Focus();
|
||||
Focus();
|
||||
});
|
||||
if (Validator.SelectedItem != null)
|
||||
{
|
||||
popup.ScrollToAndHighlightItemByName(Validator.SelectedItem.ShortName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -155,6 +155,11 @@ namespace FlaxEditor.Modules.SourceCodeEditing
|
||||
/// </summary>
|
||||
public readonly CachedTypesCollection All = new CachedAllTypesCollection(8096, ScriptType.Null, type => true, HasAssemblyValidAnyTypes);
|
||||
|
||||
/// <summary>
|
||||
/// The all types collection from all assemblies (including C# system libraries).
|
||||
/// </summary>
|
||||
public readonly CachedTypesCollection AllWithStd = new CachedTypesCollection(8096, ScriptType.Null, type => true, assembly => true);
|
||||
|
||||
/// <summary>
|
||||
/// The all valid types collection for the Visual Script property types (includes basic types like int/float, structures, object references).
|
||||
/// </summary>
|
||||
@@ -574,21 +579,17 @@ namespace FlaxEditor.Modules.SourceCodeEditing
|
||||
private static bool HasAssemblyValidAnyTypes(Assembly assembly)
|
||||
{
|
||||
var codeBase = Utils.GetAssemblyLocation(assembly);
|
||||
if (string.IsNullOrEmpty(codeBase))
|
||||
return true;
|
||||
#if USE_NETCORE
|
||||
if (assembly.ManifestModule.FullyQualifiedName == "<In Memory Module>")
|
||||
return false;
|
||||
|
||||
if (string.IsNullOrEmpty(codeBase))
|
||||
return true;
|
||||
|
||||
// Skip runtime related assemblies
|
||||
string repositoryUrl = assembly.GetCustomAttributes<AssemblyMetadataAttribute>().FirstOrDefault(x => x.Key == "RepositoryUrl")?.Value ?? "";
|
||||
if (repositoryUrl != "https://github.com/dotnet/runtime")
|
||||
return true;
|
||||
#else
|
||||
if (string.IsNullOrEmpty(codeBase))
|
||||
return true;
|
||||
|
||||
// Skip assemblies from in-build Mono directory
|
||||
if (!codeBase.Contains("/Mono/lib/mono/"))
|
||||
return true;
|
||||
|
||||
@@ -211,10 +211,10 @@ namespace FlaxEditor.Options
|
||||
public bool SeparateValueAndUnit { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the option to put a space between numbers and units for unit formatting.
|
||||
/// Gets or sets tree line visibility.
|
||||
/// </summary>
|
||||
[DefaultValue(true)]
|
||||
[EditorDisplay("Interface"), EditorOrder(320)]
|
||||
[EditorDisplay("Interface"), EditorOrder(320), Tooltip("Toggles tree line visibility in places like the Scene or Content Panel.")]
|
||||
public bool ShowTreeLines { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
@@ -369,7 +369,7 @@ namespace FlaxEditor.Options
|
||||
public int NumberOfGameClientsToLaunch = 1;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the visject connection curvature.
|
||||
/// Gets or sets the curvature of the line connecting to connected visject nodes.
|
||||
/// </summary>
|
||||
[DefaultValue(1.0f), Range(0.0f, 2.0f)]
|
||||
[EditorDisplay("Visject"), EditorOrder(550)]
|
||||
|
||||
@@ -74,6 +74,7 @@ namespace FlaxEditor.Surface
|
||||
Visible = false,
|
||||
Parent = this,
|
||||
EndEditOnClick = false, // We have to handle this ourselves, otherwise the textbox instantly loses focus when double-clicking the header
|
||||
HorizontalAlignment = TextAlignment.Center,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -448,6 +448,8 @@ namespace FlaxEditor.Surface
|
||||
sb.Append("virtual ");
|
||||
sb.Append(valueType.Name);
|
||||
sb.Append(' ');
|
||||
if (member.IsMethod)
|
||||
sb.Append(member.DeclaringType.Namespace).Append('.');
|
||||
sb.Append(declaringType.Name);
|
||||
sb.Append('.');
|
||||
sb.Append(name);
|
||||
|
||||
@@ -140,12 +140,12 @@ namespace FlaxEditor.Surface
|
||||
var searchStartTime = DateTime.Now;
|
||||
#endif
|
||||
|
||||
foreach (var scriptType in Editor.Instance.CodeEditing.All.Get())
|
||||
foreach (var scriptType in Editor.Instance.CodeEditing.AllWithStd.Get())
|
||||
{
|
||||
if (!SurfaceUtils.IsValidVisualScriptType(scriptType))
|
||||
continue;
|
||||
|
||||
_iterator(scriptType, _cache, _version);
|
||||
if (SurfaceUtils.IsValidVisualScriptType(scriptType))
|
||||
{
|
||||
_iterator(scriptType, _cache, _version);
|
||||
}
|
||||
}
|
||||
|
||||
// Add group to context menu (on a main thread)
|
||||
|
||||
@@ -17,7 +17,6 @@ using FlaxEditor.Surface.Elements;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
using FlaxEngine.Utilities;
|
||||
using Object = FlaxEngine.Object;
|
||||
|
||||
namespace FlaxEditor.Surface
|
||||
{
|
||||
@@ -36,6 +35,14 @@ namespace FlaxEditor.Surface
|
||||
Archetypes = new List<NodeArchetype>(),
|
||||
};
|
||||
|
||||
private static readonly string[] _blacklistedTypeNames =
|
||||
{
|
||||
"Newtonsoft.Json.",
|
||||
"System.Array",
|
||||
"System.Linq.Expressions.",
|
||||
"System.Reflection.",
|
||||
};
|
||||
|
||||
private static NodesCache _nodesCache = new NodesCache(IterateNodesCache);
|
||||
private DragActors _dragActors;
|
||||
|
||||
@@ -269,8 +276,11 @@ namespace FlaxEditor.Surface
|
||||
{
|
||||
// Skip Newtonsoft.Json stuff
|
||||
var scriptTypeTypeName = scriptType.TypeName;
|
||||
if (scriptTypeTypeName.StartsWith("Newtonsoft.Json."))
|
||||
return;
|
||||
for (var i = 0; i < _blacklistedTypeNames.Length; i++)
|
||||
{
|
||||
if (scriptTypeTypeName.StartsWith(_blacklistedTypeNames[i]))
|
||||
return;
|
||||
}
|
||||
var scriptTypeName = scriptType.Name;
|
||||
|
||||
// Enum
|
||||
|
||||
@@ -4,6 +4,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using FlaxEditor.Gizmo;
|
||||
using FlaxEditor.GUI.ContextMenu;
|
||||
using FlaxEditor.GUI.Input;
|
||||
using FlaxEditor.SceneGraph;
|
||||
using FlaxEditor.Viewport.Cameras;
|
||||
using FlaxEditor.Viewport.Widgets;
|
||||
@@ -298,9 +299,33 @@ namespace FlaxEditor.Viewport
|
||||
}
|
||||
var buttonBB = translateSnappingCM.AddButton("Bounding Box").LinkTooltip("Snaps the selection based on it's bounding volume");
|
||||
buttonBB.Tag = -1.0f;
|
||||
translateSnappingCM.ButtonClicked += button =>
|
||||
var buttonCustom = translateSnappingCM.AddButton("Custom");
|
||||
buttonCustom.LinkTooltip("Custom grid size");
|
||||
const float defaultCustomTranslateSnappingValue = 250.0f;
|
||||
float customTranslateSnappingValue = transformGizmo.TranslationSnapValue;
|
||||
if (customTranslateSnappingValue < 0.0f)
|
||||
customTranslateSnappingValue = defaultCustomTranslateSnappingValue;
|
||||
foreach (var v in TranslateSnapValues)
|
||||
{
|
||||
var v = (float)button.Tag;
|
||||
if (Mathf.Abs(transformGizmo.TranslationSnapValue - v) < 0.001f)
|
||||
{
|
||||
customTranslateSnappingValue = defaultCustomTranslateSnappingValue;
|
||||
break;
|
||||
}
|
||||
}
|
||||
buttonCustom.Tag = customTranslateSnappingValue;
|
||||
var customValue = new FloatValueBox(customTranslateSnappingValue, Style.Current.FontMedium.MeasureText(buttonCustom.Text).X + 5, 2, 70.0f, 0.001f, float.MaxValue, 0.1f)
|
||||
{
|
||||
Parent = buttonCustom
|
||||
};
|
||||
customValue.ValueChanged += () =>
|
||||
{
|
||||
buttonCustom.Tag = customValue.Value;
|
||||
buttonCustom.Click();
|
||||
};
|
||||
translateSnappingCM.ButtonClicked += b =>
|
||||
{
|
||||
var v = (float)b.Tag;
|
||||
transformGizmo.TranslationSnapValue = v;
|
||||
if (v < 0.0f)
|
||||
translateSnapping.Text = "Bounding Box";
|
||||
|
||||
@@ -221,7 +221,7 @@ namespace FlaxEditor.Viewport
|
||||
editor.SceneEditing.SelectionChanged += OnSelectionChanged;
|
||||
|
||||
// Gizmo widgets
|
||||
AddGizmoViewportWidgets(this, TransformGizmo);
|
||||
AddGizmoViewportWidgets(this, TransformGizmo, true);
|
||||
|
||||
// Show grid widget
|
||||
_showGridButton = ViewWidgetShowMenu.AddButton("Grid", () => Grid.Enabled = !Grid.Enabled);
|
||||
|
||||
@@ -32,6 +32,7 @@ namespace FlaxEditor.Windows.Assets
|
||||
_surface.ContextChanged += OnSurfaceContextChanged;
|
||||
|
||||
// Toolstrip
|
||||
SurfaceUtils.PerformCommonSetup(this, _toolstrip, _surface, out _saveButton, out _undoButton, out _redoButton);
|
||||
_toolstrip.AddSeparator();
|
||||
_toolstrip.AddButton(editor.Icons.Docs64, () => Platform.OpenUrl(Utilities.Constants.DocsUrl + "manual/animation/anim-graph/index.html")).LinkTooltip("See documentation to learn more");
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@ namespace FlaxEditor.Windows.Assets
|
||||
};
|
||||
|
||||
// Toolstrip
|
||||
SurfaceUtils.PerformCommonSetup(this, _toolstrip, _surface, out _saveButton, out _undoButton, out _redoButton);
|
||||
_toolstrip.AddSeparator();
|
||||
_toolstrip.AddButton(editor.Icons.Docs64, () => Platform.OpenUrl(Utilities.Constants.DocsUrl + "manual/graphics/materials/index.html")).LinkTooltip("See documentation to learn more");
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ namespace FlaxEditor.Windows.Assets
|
||||
};
|
||||
|
||||
// Toolstrip
|
||||
SurfaceUtils.PerformCommonSetup(this, _toolstrip, _surface, out _saveButton, out _undoButton, out _redoButton);
|
||||
_toolstrip.AddSeparator();
|
||||
_toolstrip.AddButton(editor.Icons.Docs64, () => Platform.OpenUrl(Utilities.Constants.DocsUrl + "manual/particles/index.html")).LinkTooltip("See documentation to learn more");
|
||||
}
|
||||
|
||||
@@ -153,6 +153,14 @@ namespace FlaxEditor.Windows.Assets
|
||||
{
|
||||
var menu = new ContextMenu();
|
||||
|
||||
var copySprite = menu.AddButton("Copy sprite");
|
||||
copySprite.Tag = groupPanel.Tag;
|
||||
copySprite.ButtonClicked += OnCopySpriteClicked;
|
||||
|
||||
var pasteSprite = menu.AddButton("Paste sprite");
|
||||
pasteSprite.Tag = groupPanel.Tag;
|
||||
pasteSprite.ButtonClicked += OnPasteSpriteClicked;
|
||||
|
||||
var deleteSprite = menu.AddButton("Delete sprite");
|
||||
deleteSprite.Tag = groupPanel.Tag;
|
||||
deleteSprite.ButtonClicked += OnDeleteSpriteClicked;
|
||||
@@ -160,6 +168,24 @@ namespace FlaxEditor.Windows.Assets
|
||||
menu.Show(groupPanel, location);
|
||||
}
|
||||
|
||||
private void OnCopySpriteClicked(ContextMenuButton button)
|
||||
{
|
||||
var window = ((PropertiesProxy)ParentEditor.Values[0])._window;
|
||||
var index = (int)button.Tag;
|
||||
var sprite = window.Asset.GetSprite(index);
|
||||
Clipboard.Text = FlaxEngine.Json.JsonSerializer.Serialize(sprite, typeof(Sprite));
|
||||
}
|
||||
|
||||
private void OnPasteSpriteClicked(ContextMenuButton button)
|
||||
{
|
||||
var window = ((PropertiesProxy)ParentEditor.Values[0])._window;
|
||||
var index = (int)button.Tag;
|
||||
var sprite = window.Asset.GetSprite(index);
|
||||
var pasted = FlaxEngine.Json.JsonSerializer.Deserialize<Sprite>(Clipboard.Text);
|
||||
sprite.Area = pasted.Area;
|
||||
window.Asset.SetSprite(index, ref sprite);
|
||||
}
|
||||
|
||||
private void OnDeleteSpriteClicked(ContextMenuButton button)
|
||||
{
|
||||
var window = ((PropertiesProxy)ParentEditor.Values[0])._window;
|
||||
|
||||
@@ -27,9 +27,21 @@ namespace FlaxEditor.Windows.Assets
|
||||
/// </summary>
|
||||
protected readonly Panel _panel;
|
||||
|
||||
private readonly ToolStripButton _saveButton;
|
||||
private readonly ToolStripButton _undoButton;
|
||||
private readonly ToolStripButton _redoButton;
|
||||
/// <summary>
|
||||
/// Save button.
|
||||
/// </summary>
|
||||
protected ToolStripButton _saveButton;
|
||||
|
||||
/// <summary>
|
||||
/// Undo button.
|
||||
/// </summary>
|
||||
protected ToolStripButton _undoButton;
|
||||
|
||||
/// <summary>
|
||||
/// Redo button.
|
||||
/// </summary>
|
||||
protected ToolStripButton _redoButton;
|
||||
|
||||
private bool _showWholeGraphOnLoad = true;
|
||||
|
||||
/// <summary>
|
||||
@@ -61,17 +73,12 @@ 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;
|
||||
_undo.RedoDone += OnUndoRedo;
|
||||
_undo.ActionDone += OnUndoRedo;
|
||||
|
||||
// Toolstrip
|
||||
SurfaceUtils.PerformCommonSetup(this, _toolstrip, _surface, out _saveButton, out _undoButton, out _redoButton);
|
||||
|
||||
// Panel
|
||||
_panel = new Panel(ScrollBars.None)
|
||||
{
|
||||
|
||||
@@ -135,6 +135,7 @@ const Char* SplashScreenQuotes[] =
|
||||
TEXT("Drum roll please"),
|
||||
TEXT("Good Luck Have Fun"),
|
||||
TEXT("GG Well Played"),
|
||||
TEXT("Now with documentation."),
|
||||
};
|
||||
|
||||
SplashScreen::~SplashScreen()
|
||||
|
||||
@@ -396,8 +396,8 @@ void BehaviorTreeMoveToNode::GetAgentSize(Actor* agent, float& outRadius, float&
|
||||
// Estimate actor bounds to extract capsule information
|
||||
const BoundingBox box = agent->GetBox();
|
||||
const BoundingSphere sphere = agent->GetSphere();
|
||||
outRadius = sphere.Radius;
|
||||
outHeight = box.GetSize().Y;
|
||||
outRadius = (float)sphere.Radius;
|
||||
outHeight = (float)box.GetSize().Y;
|
||||
}
|
||||
|
||||
int32 BehaviorTreeMoveToNode::GetStateSize() const
|
||||
@@ -522,7 +522,7 @@ String BehaviorTreeMoveToNode::GetDebugInfo(const BehaviorUpdateContext& context
|
||||
goal = state->GoalLocation.ToString();
|
||||
const Vector3 agentLocation = state->Agent->GetPosition();
|
||||
const Vector3 agentLocationOnPath = agentLocation + state->AgentOffset;
|
||||
float distanceLeft = state->Path.Count() > state->TargetPathIndex ? Vector3::Distance(state->Path[state->TargetPathIndex], agentLocationOnPath) : 0;
|
||||
Real distanceLeft = state->Path.Count() > state->TargetPathIndex ? Vector3::Distance(state->Path[state->TargetPathIndex], agentLocationOnPath) : 0;
|
||||
for (int32 i = state->TargetPathIndex; i < state->Path.Count(); i++)
|
||||
distanceLeft += Vector3::Distance(state->Path[i - 1], state->Path[i]);
|
||||
return String::Format(TEXT("Agent: '{}'\nGoal: '{}'\nDistance: {}"), agent, goal, (int32)distanceLeft);
|
||||
@@ -559,7 +559,7 @@ void BehaviorTreeMoveToNode::State::OnUpdate()
|
||||
const float acceptableHeightPercentage = 1.05f;
|
||||
const float testHeight = agentHeight * acceptableHeightPercentage;
|
||||
const Vector3 toGoal = agentLocationOnPath - pathSegmentEnd;
|
||||
const float toGoalHeightDiff = (toGoal * UpVector).SumValues();
|
||||
const Real toGoalHeightDiff = (toGoal * UpVector).SumValues();
|
||||
if (toGoal.Length() <= testRadius && toGoalHeightDiff <= testHeight)
|
||||
{
|
||||
TargetPathIndex++;
|
||||
|
||||
@@ -465,7 +465,7 @@ namespace FlaxEngine
|
||||
/// <summary>
|
||||
/// A single keyframe that can be injected into linear curve.
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 2)]
|
||||
public struct Keyframe : IComparable, IComparable<Keyframe>
|
||||
{
|
||||
/// <summary>
|
||||
@@ -720,7 +720,7 @@ namespace FlaxEngine
|
||||
/// <summary>
|
||||
/// A single keyframe that can be injected into Bezier curve.
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 2)]
|
||||
public struct Keyframe : IComparable, IComparable<Keyframe>
|
||||
{
|
||||
/// <summary>
|
||||
|
||||
@@ -113,7 +113,7 @@ void InverseKinematics::SolveTwoBoneIK(Transform& rootTransform, Transform& midJ
|
||||
newMidJointPos = rootTransform.Translation + projJointDist * toTargetDir + jointLineDist * bendDirection;
|
||||
}
|
||||
// TODO: fix the new IK impl (https://github.com/FlaxEngine/FlaxEngine/pull/2421) to properly work for character from https://github.com/PrecisionRender/CharacterControllerPro
|
||||
#define OLD 0
|
||||
#define OLD 1
|
||||
// Update root joint orientation
|
||||
{
|
||||
#if OLD
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "Loading/ContentLoadingManager.h"
|
||||
#include "Loading/Tasks/LoadAssetTask.h"
|
||||
#include "Engine/Core/Log.h"
|
||||
#include "Engine/Core/LogContext.h"
|
||||
#include "Engine/Engine/Engine.h"
|
||||
#include "Engine/Threading/Threading.h"
|
||||
#include "Engine/Profiler/ProfilerCPU.h"
|
||||
@@ -596,9 +597,10 @@ bool Asset::IsInternalType() const
|
||||
|
||||
bool Asset::onLoad(LoadAssetTask* task)
|
||||
{
|
||||
if (task->Asset.Get() != this || Platform::AtomicRead(&_loadingTask) == 0)
|
||||
// It may fail when task is cancelled and new one was created later (don't crash but just end with an error)
|
||||
if (task->Asset.Get() != this || Platform::AtomicRead(&_loadingTask) == 0)
|
||||
return true;
|
||||
LogContextScope logContext(GetID());
|
||||
|
||||
Locker.Lock();
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "Storage/JsonStorageProxy.h"
|
||||
#include "Factories/IAssetFactory.h"
|
||||
#include "Engine/Core/Log.h"
|
||||
#include "Engine/Core/LogContext.h"
|
||||
#include "Engine/Core/Types/String.h"
|
||||
#include "Engine/Core/ObjectsRemovalService.h"
|
||||
#include "Engine/Engine/EngineService.h"
|
||||
@@ -970,6 +971,7 @@ Asset* Content::LoadAsync(const Guid& id, const ScriptingTypeHandle& type)
|
||||
if (IsAssetTypeIdInvalid(type, result->GetTypeHandle()) && !result->Is(type))
|
||||
{
|
||||
LOG(Warning, "Different loaded asset type! Asset: \'{0}\'. Expected type: {1}", result->ToString(), type.ToString());
|
||||
LogContext::Print(LogType::Warning);
|
||||
return nullptr;
|
||||
}
|
||||
return result;
|
||||
@@ -1004,6 +1006,7 @@ Asset* Content::LoadAsync(const Guid& id, const ScriptingTypeHandle& type)
|
||||
if (!GetAssetInfo(id, assetInfo))
|
||||
{
|
||||
LOG(Warning, "Invalid or missing asset ({0}, {1}).", id, type.ToString());
|
||||
LogContext::Print(LogType::Warning);
|
||||
LOAD_FAILED();
|
||||
}
|
||||
#if ASSETS_LOADING_EXTRA_VERIFICATION
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#if COMPILE_WITH_ASSETS_IMPORTER
|
||||
|
||||
#include "Engine/Core/Log.h"
|
||||
#include "Engine/Core/Cache.h"
|
||||
#include "Engine/Core/Collections/Sorting.h"
|
||||
#include "Engine/Core/Collections/ArrayExtensions.h"
|
||||
#include "Engine/Serialization/MemoryWriteStream.h"
|
||||
@@ -766,6 +767,7 @@ CreateAssetResult ImportModel::CreatePrefab(CreateAssetContext& context, ModelDa
|
||||
// Link with object from prefab (if reimporting)
|
||||
if (prefab)
|
||||
{
|
||||
rapidjson_flax::StringBuffer buffer;
|
||||
for (Actor* a : nodeActors)
|
||||
{
|
||||
for (const auto& i : prefab->ObjectsCache)
|
||||
@@ -776,6 +778,32 @@ CreateAssetResult ImportModel::CreatePrefab(CreateAssetContext& context, ModelDa
|
||||
if (o->GetName() != a->GetName()) // Name match
|
||||
continue;
|
||||
|
||||
// Preserve local changes made in the prefab
|
||||
{
|
||||
// Serialize
|
||||
buffer.Clear();
|
||||
CompactJsonWriter writer(buffer);
|
||||
writer.StartObject();
|
||||
const void* defaultInstance = o->GetType().GetDefaultInstance();
|
||||
o->Serialize(writer, defaultInstance);
|
||||
writer.EndObject();
|
||||
|
||||
// Parse json
|
||||
rapidjson_flax::Document document;
|
||||
document.Parse(buffer.GetString(), buffer.GetSize());
|
||||
|
||||
// Strip unwanted data
|
||||
document.RemoveMember("ID");
|
||||
document.RemoveMember("ParentID");
|
||||
document.RemoveMember("PrefabID");
|
||||
document.RemoveMember("PrefabObjectID");
|
||||
document.RemoveMember("Name");
|
||||
|
||||
// Deserialize object
|
||||
auto modifier = Cache::ISerializeModifier.Get();
|
||||
a->Deserialize(document, &*modifier);
|
||||
}
|
||||
|
||||
// Mark as this object already exists in prefab so will be preserved when updating it
|
||||
a->LinkPrefab(o->GetPrefabID(), o->GetPrefabObjectID());
|
||||
break;
|
||||
|
||||
@@ -1,9 +1,16 @@
|
||||
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
||||
|
||||
#include "LogContext.h"
|
||||
#include "Engine/Core/Log.h"
|
||||
#include "Engine/Core/Types/Guid.h"
|
||||
#include "Engine/Core/Types/String.h"
|
||||
#include "Engine/Core/Types/StringBuilder.h"
|
||||
#include "Engine/Core/Collections/Array.h"
|
||||
#include "Engine/Scripting/Scripting.h"
|
||||
#include "Engine/Scripting/Script.h"
|
||||
#include "Engine/Content/Asset.h"
|
||||
#include "Engine/Content/Content.h"
|
||||
#include "Engine/Level/Actor.h"
|
||||
#include "Engine/Threading/ThreadLocal.h"
|
||||
|
||||
struct LogContextThreadData
|
||||
@@ -30,7 +37,7 @@ struct LogContextThreadData
|
||||
Count--;
|
||||
}
|
||||
|
||||
LogContextData Peek()
|
||||
LogContextData Peek() const
|
||||
{
|
||||
return Count > 0 ? Ptr[Count - 1] : LogContextData();
|
||||
}
|
||||
@@ -38,12 +45,58 @@ struct LogContextThreadData
|
||||
|
||||
ThreadLocal<LogContextThreadData> GlobalLogContexts;
|
||||
|
||||
String LogContext::GetInfo()
|
||||
void LogContext::Print(LogType verbosity)
|
||||
{
|
||||
LogContextData context = LogContext::Get();
|
||||
if (context.ObjectID != Guid::Empty)
|
||||
return String::Format(TEXT("(Loading source was {0})"), context.ObjectID);
|
||||
return String::Empty;
|
||||
auto& stack = GlobalLogContexts.Get();
|
||||
if (stack.Count == 0)
|
||||
return;
|
||||
const StringView indentation(TEXT(" "));
|
||||
StringBuilder msg;
|
||||
for (int32 index = (int32)stack.Count - 1; index >= 0; index--)
|
||||
{
|
||||
// Build call hierarchy via indentation
|
||||
msg.Clear();
|
||||
for (uint32 i = index; i < stack.Count; i++)
|
||||
msg.Append(indentation);
|
||||
|
||||
LogContextData& context = stack.Ptr[index];
|
||||
if (context.ObjectID != Guid::Empty)
|
||||
{
|
||||
// Object reference context
|
||||
msg.Append(TEXT(" Referenced by "));
|
||||
if (ScriptingObject* object = Scripting::TryFindObject(context.ObjectID))
|
||||
{
|
||||
const StringAnsiView typeName(object->GetType().Fullname);
|
||||
if (Asset* asset = ScriptingObject::Cast<Asset>(object))
|
||||
{
|
||||
msg.AppendFormat(TEXT("asset '{}' ({}, {})"), asset->GetPath(), asset->GetTypeName(), context.ObjectID);
|
||||
}
|
||||
else if (Actor* actor = ScriptingObject::Cast<Actor>(object))
|
||||
{
|
||||
msg.AppendFormat(TEXT("actor '{}' ({}, {})"), actor->GetNamePath(), String(typeName), context.ObjectID);
|
||||
}
|
||||
else if (Script* script = ScriptingObject::Cast<Script>(object))
|
||||
{
|
||||
msg.AppendFormat(TEXT("script '{}' ({}, {})"), script->GetNamePath(), String(typeName), context.ObjectID);
|
||||
}
|
||||
else
|
||||
{
|
||||
msg.AppendFormat(TEXT("object {} ({})"), String(typeName), context.ObjectID);
|
||||
}
|
||||
}
|
||||
else if (Asset* asset = Content::GetAsset(context.ObjectID))
|
||||
{
|
||||
msg.AppendFormat(TEXT("asset '{}' ({}, {})"), asset->GetPath(), asset->GetTypeName(), context.ObjectID);
|
||||
}
|
||||
else
|
||||
{
|
||||
msg.AppendFormat(TEXT("object {}"), context.ObjectID);
|
||||
}
|
||||
}
|
||||
|
||||
// Print message
|
||||
Log::Logger::Write(verbosity, msg.ToStringView());
|
||||
}
|
||||
}
|
||||
|
||||
void LogContext::Push(const Guid& id)
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Engine/Core/Log.h"
|
||||
#include "Engine/Core/Config.h"
|
||||
#include "Engine/Scripting/ScriptingType.h"
|
||||
|
||||
@@ -9,7 +10,7 @@ class String;
|
||||
struct Guid;
|
||||
|
||||
/// <summary>
|
||||
/// Log context data structure. Contains different kinds of context data for different situtations.
|
||||
/// Log context data structure. Contains different kinds of context data for different situations.
|
||||
/// </summary>
|
||||
API_STRUCT(NoDefault) struct FLAXENGINE_API LogContextData
|
||||
{
|
||||
@@ -54,10 +55,10 @@ API_CLASS(Static) class FLAXENGINE_API LogContext
|
||||
API_FUNCTION() static LogContextData Get();
|
||||
|
||||
/// <summary>
|
||||
/// Returns a string which represents the current log context on the stack.
|
||||
/// Prints the current log context to the log. Does nothing it
|
||||
/// </summary>
|
||||
/// <returns>The formatted string representing the current log context.</returns>
|
||||
API_FUNCTION() static String GetInfo();
|
||||
/// <param name="verbosity">The verbosity of the log.</param>
|
||||
API_FUNCTION() static void Print(LogType verbosity);
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -542,10 +542,8 @@ void Quaternion::RotationYawPitchRoll(float yaw, float pitch, float roll, Quater
|
||||
Quaternion Quaternion::GetRotationFromNormal(const Vector3& normal, const Transform& reference)
|
||||
{
|
||||
Float3 up = reference.GetUp();
|
||||
const float dot = Vector3::Dot(normal, up);
|
||||
const Real dot = Vector3::Dot(normal, up);
|
||||
if (Math::NearEqual(Math::Abs(dot), 1))
|
||||
{
|
||||
up = reference.GetRight();
|
||||
}
|
||||
return Quaternion::LookRotation(normal, up);
|
||||
}
|
||||
|
||||
@@ -15,8 +15,8 @@ namespace FlaxEngine.Interop
|
||||
#if FLAX_EDITOR
|
||||
[HideInEditor]
|
||||
#endif
|
||||
[CustomMarshaller(typeof(object), MarshalMode.ManagedToUnmanagedIn, typeof(ManagedHandleMarshaller.ManagedToNative))]
|
||||
[CustomMarshaller(typeof(object), MarshalMode.UnmanagedToManagedOut, typeof(ManagedHandleMarshaller.ManagedToNative))]
|
||||
[CustomMarshaller(typeof(object), MarshalMode.ManagedToUnmanagedIn, typeof(ManagedHandleMarshaller.ManagedToNativeState))]
|
||||
[CustomMarshaller(typeof(object), MarshalMode.UnmanagedToManagedOut, typeof(ManagedHandleMarshaller.ManagedToNativeState))]
|
||||
[CustomMarshaller(typeof(object), MarshalMode.ElementIn, typeof(ManagedHandleMarshaller.ManagedToNative))]
|
||||
[CustomMarshaller(typeof(object), MarshalMode.ManagedToUnmanagedOut, typeof(ManagedHandleMarshaller.NativeToManaged))]
|
||||
[CustomMarshaller(typeof(object), MarshalMode.UnmanagedToManagedIn, typeof(ManagedHandleMarshaller.NativeToManaged))]
|
||||
@@ -31,7 +31,20 @@ namespace FlaxEngine.Interop
|
||||
#endif
|
||||
public static class NativeToManaged
|
||||
{
|
||||
public static object ConvertToManaged(IntPtr unmanaged) => unmanaged == IntPtr.Zero ? null : ManagedHandle.FromIntPtr(unmanaged).Target;
|
||||
public static object ConvertToManaged(IntPtr unmanaged)
|
||||
{
|
||||
if (unmanaged == IntPtr.Zero)
|
||||
return null;
|
||||
object managed = ManagedHandle.FromIntPtr(unmanaged).Target;
|
||||
if (managed is ManagedArray managedArray)
|
||||
{
|
||||
var managedArrayHandle = ManagedHandle.Alloc(managedArray, GCHandleType.Normal);
|
||||
managed = NativeInterop.MarshalToManaged((IntPtr)managedArrayHandle, managedArray.ArrayType);
|
||||
managedArrayHandle.Free();
|
||||
}
|
||||
return managed;
|
||||
}
|
||||
|
||||
public static IntPtr ConvertToUnmanaged(object managed) => managed != null ? ManagedHandle.ToIntPtr(managed, GCHandleType.Weak) : IntPtr.Zero;
|
||||
|
||||
public static void Free(IntPtr unmanaged)
|
||||
@@ -40,6 +53,52 @@ namespace FlaxEngine.Interop
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#if FLAX_EDITOR
|
||||
[HideInEditor]
|
||||
#endif
|
||||
public struct ManagedToNativeState
|
||||
{
|
||||
ManagedArray managedArray;
|
||||
IntPtr handle;
|
||||
|
||||
public void FromManaged(object managed)
|
||||
{
|
||||
if (managed == null)
|
||||
return;
|
||||
if (managed is Array arr)
|
||||
{
|
||||
var type = managed.GetType();
|
||||
var elementType = type.GetElementType();
|
||||
if (NativeInterop.ArrayFactory.GetMarshalledType(elementType) == elementType)
|
||||
{
|
||||
// Use pooled managed array wrapper to be passed around as handle to it
|
||||
(ManagedHandle tmp, managedArray) = ManagedArray.WrapPooledArray(arr);
|
||||
handle = ManagedHandle.ToIntPtr(tmp);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Convert array contents to be properly accessed by the native code (as GCHandles array)
|
||||
managedArray = NativeInterop.ManagedArrayToGCHandleWrappedArray(arr);
|
||||
handle = ManagedHandle.ToIntPtr(ManagedHandle.Alloc(managedArray));
|
||||
managedArray = null; // It's not pooled
|
||||
}
|
||||
}
|
||||
else
|
||||
handle = ManagedHandle.ToIntPtr(managed, GCHandleType.Weak);
|
||||
}
|
||||
|
||||
public IntPtr ToUnmanaged()
|
||||
{
|
||||
return handle;
|
||||
}
|
||||
|
||||
public void Free()
|
||||
{
|
||||
managedArray?.FreePooled();
|
||||
}
|
||||
}
|
||||
|
||||
#if FLAX_EDITOR
|
||||
[HideInEditor]
|
||||
#endif
|
||||
@@ -50,12 +109,6 @@ namespace FlaxEngine.Interop
|
||||
|
||||
public static void Free(IntPtr unmanaged)
|
||||
{
|
||||
// This is a weak handle, no need to free it
|
||||
/*
|
||||
if (unmanaged == IntPtr.Zero)
|
||||
return;
|
||||
ManagedHandle.FromIntPtr(unmanaged).Free();
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
@@ -342,6 +395,7 @@ namespace FlaxEngine.Interop
|
||||
{
|
||||
public static Dictionary<T, U> ConvertToManaged(IntPtr unmanaged) => DictionaryMarshaller<T, U>.ToManaged(unmanaged);
|
||||
public static IntPtr ConvertToUnmanaged(Dictionary<T, U> managed) => DictionaryMarshaller<T, U>.ToNative(managed, GCHandleType.Weak);
|
||||
|
||||
public static void Free(IntPtr unmanaged)
|
||||
{
|
||||
//DictionaryMarshaller<T, U>.Free(unmanaged); // No need to free weak handles
|
||||
@@ -614,6 +668,7 @@ namespace FlaxEngine.Interop
|
||||
{
|
||||
public static string ConvertToManaged(IntPtr unmanaged) => ManagedString.ToManaged(unmanaged);
|
||||
public static unsafe IntPtr ConvertToUnmanaged(string managed) => managed == null ? IntPtr.Zero : ManagedHandle.ToIntPtr(managed, GCHandleType.Weak);
|
||||
|
||||
public static void Free(IntPtr unmanaged)
|
||||
{
|
||||
//ManagedString.Free(unmanaged); // No need to free weak handles
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include "Engine/Core/Collections/CollectionPoolCache.h"
|
||||
#include "Engine/Profiler/ProfilerCPU.h"
|
||||
#include "Engine/Core/Cache.h"
|
||||
#include "Engine/Core/LogContext.h"
|
||||
#include "Engine/Debug/Exceptions/ArgumentException.h"
|
||||
#include "Engine/Engine/EngineService.h"
|
||||
#include "Engine/Scripting/Script.h"
|
||||
@@ -122,6 +123,7 @@ Actor* PrefabManager::SpawnPrefab(Prefab* prefab, const Transform& transform, Ac
|
||||
}
|
||||
auto& data = *prefab->Data;
|
||||
SceneObjectsFactory::Context context(modifier.Value);
|
||||
LogContextScope logContext(prefabId);
|
||||
|
||||
// Deserialize prefab objects
|
||||
auto prevIdMapping = Scripting::ObjectsLookupIdMapping.Get();
|
||||
|
||||
@@ -1302,6 +1302,11 @@ bool NetworkReplicator::HasObject(const ScriptingObject* obj)
|
||||
return false;
|
||||
}
|
||||
|
||||
void NetworkReplicator::MapObjectId(Guid& objectId)
|
||||
{
|
||||
IdsRemappingTable.TryGet(objectId, objectId);
|
||||
}
|
||||
|
||||
ScriptingObject* NetworkReplicator::ResolveForeignObject(Guid objectId)
|
||||
{
|
||||
if (const auto& object = ResolveObject(objectId))
|
||||
|
||||
@@ -116,6 +116,12 @@ public:
|
||||
/// <param name="obj">The network object.</param>
|
||||
/// <returns>True if object exists in networking, otherwise false.</returns>
|
||||
API_FUNCTION() static bool HasObject(const ScriptingObject* obj);
|
||||
|
||||
/// <summary>
|
||||
/// Maps object ID into server or client ID (depending on the source ID). Leaves source value unchanged if that specific ID is unused.
|
||||
/// </summary>
|
||||
/// <param name="objectId">The network object identifier to map. Contains result ID once the method completes.</param>
|
||||
API_FUNCTION() static void MapObjectId(API_PARAM(Ref) Guid& objectId);
|
||||
|
||||
/// <summary>
|
||||
/// Resolves foreign Guid into a local ScriptingObject
|
||||
|
||||
@@ -494,28 +494,28 @@ void PhysicsScene::CollectResults()
|
||||
bool PhysicsScene::LineCast(const Vector3& start, const Vector3& end, uint32 layerMask, bool hitTriggers)
|
||||
{
|
||||
Vector3 directionToEnd = end - start;
|
||||
const float distanceToEnd = directionToEnd.Length();
|
||||
const Real distanceToEnd = directionToEnd.Length();
|
||||
if (distanceToEnd >= ZeroTolerance)
|
||||
directionToEnd /= distanceToEnd;
|
||||
return PhysicsBackend::RayCast(_scene, start, directionToEnd, distanceToEnd, layerMask, hitTriggers);
|
||||
return PhysicsBackend::RayCast(_scene, start, directionToEnd, (float)distanceToEnd, layerMask, hitTriggers);
|
||||
}
|
||||
|
||||
bool PhysicsScene::LineCast(const Vector3& start, const Vector3& end, RayCastHit& hitInfo, uint32 layerMask, bool hitTriggers)
|
||||
{
|
||||
Vector3 directionToEnd = end - start;
|
||||
const float distanceToEnd = directionToEnd.Length();
|
||||
const Real distanceToEnd = directionToEnd.Length();
|
||||
if (distanceToEnd >= ZeroTolerance)
|
||||
directionToEnd /= distanceToEnd;
|
||||
return PhysicsBackend::RayCast(_scene, start, directionToEnd, hitInfo, distanceToEnd, layerMask, hitTriggers);
|
||||
return PhysicsBackend::RayCast(_scene, start, directionToEnd, hitInfo, (float)distanceToEnd, layerMask, hitTriggers);
|
||||
}
|
||||
|
||||
bool PhysicsScene::LineCastAll(const Vector3& start, const Vector3& end, Array<RayCastHit>& results, uint32 layerMask, bool hitTriggers)
|
||||
{
|
||||
Vector3 directionToEnd = end - start;
|
||||
const float distanceToEnd = directionToEnd.Length();
|
||||
const Real distanceToEnd = directionToEnd.Length();
|
||||
if (distanceToEnd >= ZeroTolerance)
|
||||
directionToEnd /= distanceToEnd;
|
||||
return PhysicsBackend::RayCastAll(_scene, start, directionToEnd, results, distanceToEnd, layerMask, hitTriggers);
|
||||
return PhysicsBackend::RayCastAll(_scene, start, directionToEnd, results, (float)distanceToEnd, layerMask, hitTriggers);
|
||||
}
|
||||
|
||||
bool PhysicsScene::RayCast(const Vector3& origin, const Vector3& direction, const float maxDistance, uint32 layerMask, bool hitTriggers)
|
||||
|
||||
@@ -339,11 +339,10 @@ int32 Font::HitTestText(const StringView& text, const Float2& location, const Te
|
||||
float baseLinesDistance = static_cast<float>(_height) * layout.BaseLinesGapScale * scale;
|
||||
|
||||
// Offset position to match lines origin space
|
||||
Float2 rootOffset = layout.Bounds.Location + lines.First().Location;
|
||||
Float2 testPoint = location - rootOffset;
|
||||
Float2 testPoint = location - layout.Bounds.Location;
|
||||
|
||||
// Get line which may intersect with the position (it's possible because lines have fixed height)
|
||||
int32 lineIndex = Math::Clamp(Math::FloorToInt(testPoint.Y / baseLinesDistance), 0, lines.Count() - 1);
|
||||
int32 lineIndex = Math::Clamp(Math::FloorToInt((testPoint.Y - lines.First().Location.Y) / baseLinesDistance), 0, lines.Count() - 1);
|
||||
const FontLineCache& line = lines[lineIndex];
|
||||
float x = line.Location.X;
|
||||
|
||||
@@ -411,7 +410,6 @@ Float2 Font::GetCharPosition(const StringView& text, int32 index, const TextLayo
|
||||
ASSERT(lines.HasItems());
|
||||
float scale = layout.Scale / FontManager::FontScale;
|
||||
float baseLinesDistance = static_cast<float>(_height) * layout.BaseLinesGapScale * scale;
|
||||
Float2 rootOffset = layout.Bounds.Location + lines.First().Location;
|
||||
|
||||
// Find line with that position
|
||||
FontCharacterEntry previous;
|
||||
@@ -423,7 +421,7 @@ Float2 Font::GetCharPosition(const StringView& text, int32 index, const TextLayo
|
||||
// Check if desire position is somewhere inside characters in line range
|
||||
if (Math::IsInRange(index, line.FirstCharIndex, line.LastCharIndex))
|
||||
{
|
||||
float x = line.Location.X;
|
||||
Float2 charPos = line.Location;
|
||||
|
||||
// Check all characters in the line
|
||||
for (int32 currentIndex = line.FirstCharIndex; currentIndex < index; currentIndex++)
|
||||
@@ -436,21 +434,21 @@ Float2 Font::GetCharPosition(const StringView& text, int32 index, const TextLayo
|
||||
// Apply kerning
|
||||
if (!isWhitespace && previous.IsValid)
|
||||
{
|
||||
x += entry.Font->GetKerning(previous.Character, entry.Character);
|
||||
charPos.X += entry.Font->GetKerning(previous.Character, entry.Character);
|
||||
}
|
||||
previous = entry;
|
||||
|
||||
// Move
|
||||
x += entry.AdvanceX * scale;
|
||||
charPos.X += entry.AdvanceX * scale;
|
||||
}
|
||||
|
||||
// Upper left corner of the character
|
||||
return rootOffset + Float2(x, static_cast<float>(lineIndex * baseLinesDistance));
|
||||
return layout.Bounds.Location + charPos;
|
||||
}
|
||||
}
|
||||
|
||||
// Position after last character in the last line
|
||||
return rootOffset + Float2(lines.Last().Size.X, static_cast<float>((lines.Count() - 1) * baseLinesDistance));
|
||||
return layout.Bounds.Location + lines.Last().Location + Float2(lines.Last().Size.X, 0.0f);
|
||||
}
|
||||
|
||||
void Font::FlushFaceSize() const
|
||||
|
||||
@@ -90,7 +90,7 @@ namespace FlaxEngine
|
||||
/// <summary>
|
||||
/// The size of the font characters.
|
||||
/// </summary>
|
||||
[EditorOrder(10), Limit(1, 500, 0.1f), Tooltip("The size of the font characters.")]
|
||||
[EditorOrder(10), Limit(1, 500, 0.5f), Tooltip("The size of the font characters.")]
|
||||
public float Size
|
||||
{
|
||||
get => _size;
|
||||
|
||||
@@ -841,10 +841,11 @@ void Render2D::PushClip(const Rectangle& clipRect)
|
||||
{
|
||||
RENDER2D_CHECK_RENDERING_STATE;
|
||||
|
||||
const auto& mask = ClipLayersStack.Peek();
|
||||
RotatedRectangle clipRectTransformed;
|
||||
ApplyTransform(clipRect, clipRectTransformed);
|
||||
const Rectangle bounds = Rectangle::Shared(clipRectTransformed.ToBoundingRect(), ClipLayersStack.Peek().Bounds);
|
||||
ClipLayersStack.Push({ clipRectTransformed, bounds });
|
||||
const Rectangle bounds = Rectangle::Shared(clipRectTransformed.ToBoundingRect(), mask.Bounds);
|
||||
ClipLayersStack.Push({ RotatedRectangle::Shared(clipRectTransformed, mask.Bounds), bounds });
|
||||
|
||||
OnClipScissors();
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
struct RotatedRectangle
|
||||
{
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// The transformed top left corner.
|
||||
/// </summary>
|
||||
@@ -27,7 +26,6 @@ public:
|
||||
Float2 ExtentY;
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RotatedRectangle"/> struct.
|
||||
/// </summary>
|
||||
@@ -60,7 +58,6 @@ public:
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Convert rotated rectangle to the axis-aligned rectangle that builds rotated rectangle bounding box.
|
||||
/// </summary>
|
||||
@@ -95,8 +92,24 @@ public:
|
||||
return false;
|
||||
}
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
/// Calculates a rectangle that contains the shared part of both rectangles.
|
||||
/// </summary>
|
||||
/// <param name="a">The first rectangle.</param>
|
||||
/// <param name="b">The second rectangle.</param>
|
||||
/// <returns>Rectangle that contains shared part of a and b rectangles.</returns>
|
||||
static RotatedRectangle Shared(const RotatedRectangle& a, const Rectangle& b)
|
||||
{
|
||||
// Clip the rotated rectangle bounds within the given AABB
|
||||
RotatedRectangle result = a;
|
||||
result.TopLeft = Float2::Max(a.TopLeft, b.GetTopLeft());
|
||||
// TODO: do a little better math below (in case of actually rotated rectangle)
|
||||
result.ExtentX.X = Math::Min(result.TopLeft.X + result.ExtentX.X, b.GetRight()) - result.TopLeft.X;
|
||||
result.ExtentY.Y = Math::Min(result.TopLeft.Y + result.ExtentY.Y, b.GetBottom()) - result.TopLeft.Y;
|
||||
return result;
|
||||
}
|
||||
|
||||
public:
|
||||
bool operator ==(const RotatedRectangle& other) const
|
||||
{
|
||||
return
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace FlaxEngine
|
||||
{
|
||||
partial struct SpriteHandle
|
||||
partial struct SpriteHandle : IEquatable<SpriteHandle>
|
||||
{
|
||||
/// <summary>
|
||||
/// Invalid sprite handle.
|
||||
@@ -107,5 +108,53 @@ namespace FlaxEngine
|
||||
Atlas.SetSprite(Index, ref sprite);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests for equality between two objects.
|
||||
/// </summary>
|
||||
/// <param name="left">The first value to compare.</param>
|
||||
/// <param name="right">The second value to compare.</param>
|
||||
/// <returns><c>true</c> if <paramref name="left" /> has the same value as <paramref name="right" />; otherwise, <c>false</c>.</returns>
|
||||
public static bool operator ==(SpriteHandle left, SpriteHandle right)
|
||||
{
|
||||
return left.Index == right.Index && left.Atlas == right.Atlas;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests for inequality between two objects.
|
||||
/// </summary>
|
||||
/// <param name="left">The first value to compare.</param>
|
||||
/// <param name="right">The second value to compare.</param>
|
||||
/// <returns><c>true</c> if <paramref name="left" /> has a different value than <paramref name="right" />; otherwise, <c>false</c>.</returns>
|
||||
public static bool operator !=(SpriteHandle left, SpriteHandle right)
|
||||
{
|
||||
return left.Index != right.Index || left.Atlas != right.Atlas;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool Equals(SpriteHandle other)
|
||||
{
|
||||
return Index == other.Index && Atlas == other.Atlas;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return obj is SpriteHandle other && Equals(other);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return HashCode.Combine(Atlas, Index);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToString()
|
||||
{
|
||||
if (Atlas)
|
||||
return $"{System.IO.Path.GetFileNameWithoutExtension(Atlas.Path)}[{Index}]";
|
||||
return "Invalid";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -318,6 +318,11 @@ void ProbesRendererService::Update()
|
||||
// Calculate time delta since last update
|
||||
auto timeNow = Time::Update.UnscaledTime;
|
||||
auto timeSinceUpdate = timeNow - _lastProbeUpdate;
|
||||
if (timeSinceUpdate < 0)
|
||||
{
|
||||
_lastProbeUpdate = timeNow;
|
||||
timeSinceUpdate = 0;
|
||||
}
|
||||
|
||||
// Check if render job is done
|
||||
if (isUpdateSynced())
|
||||
@@ -351,8 +356,9 @@ void ProbesRendererService::Update()
|
||||
auto dt = (float)Time::Update.UnscaledDeltaTime.GetTotalSeconds();
|
||||
for (int32 i = 0; i < _probesToBake.Count(); i++)
|
||||
{
|
||||
_probesToBake[i].Timeout -= dt;
|
||||
if (_probesToBake[i].Timeout <= 0)
|
||||
auto& e = _probesToBake[i];
|
||||
e.Timeout -= dt;
|
||||
if (e.Timeout <= 0)
|
||||
{
|
||||
firstValidEntryIndex = i;
|
||||
break;
|
||||
@@ -417,6 +423,9 @@ void ProbesRenderer::OnRender(RenderTask* task, GPUContext* context)
|
||||
if (_current.Actor == nullptr)
|
||||
{
|
||||
// Probe has been unlinked (or deleted)
|
||||
_task->Enabled = false;
|
||||
_updateFrameNumber = 0;
|
||||
_current.Type = EntryType::Invalid;
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -28,21 +28,9 @@ public:
|
||||
|
||||
struct Entry
|
||||
{
|
||||
EntryType Type;
|
||||
EntryType Type = EntryType::Invalid;
|
||||
ScriptingObjectReference<Actor> Actor;
|
||||
float Timeout;
|
||||
|
||||
Entry()
|
||||
{
|
||||
Type = EntryType::Invalid;
|
||||
}
|
||||
|
||||
Entry(const Entry& other)
|
||||
{
|
||||
Type = other.Type;
|
||||
Actor = other.Actor;
|
||||
Timeout = other.Timeout;
|
||||
}
|
||||
float Timeout = 0.0f;
|
||||
|
||||
bool UseTextureData() const;
|
||||
int32 GetResolution() const;
|
||||
|
||||
@@ -887,7 +887,8 @@ ScriptingObject* Scripting::FindObject(Guid id, const MClass* type)
|
||||
// Check type
|
||||
if (!type || result->Is(type))
|
||||
return result;
|
||||
LOG(Warning, "Found scripting object with ID={0} of type {1} that doesn't match type {2}. {3}", id, String(result->GetType().Fullname), String(type->GetFullName()), LogContext::GetInfo());
|
||||
LOG(Warning, "Found scripting object with ID={0} of type {1} that doesn't match type {2}", id, String(result->GetType().Fullname), String(type->GetFullName()));
|
||||
LogContext::Print(LogType::Warning);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -906,7 +907,8 @@ ScriptingObject* Scripting::FindObject(Guid id, const MClass* type)
|
||||
return asset;
|
||||
}
|
||||
|
||||
LOG(Warning, "Unable to find scripting object with ID={0}. Required type {1}. {2}", id, String(type->GetFullName()), LogContext::GetInfo());
|
||||
LOG(Warning, "Unable to find scripting object with ID={0}. Required type {1}", id, String(type->GetFullName()));
|
||||
LogContext::Print(LogType::Warning);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
@@ -737,7 +737,10 @@ DEFINE_INTERNAL_CALL(MObject*) ObjectInternal_FindObject(Guid* id, MTypeObject*
|
||||
if (klass && !obj->Is(klass))
|
||||
{
|
||||
if (!skipLog)
|
||||
LOG(Warning, "Found scripting object with ID={0} of type {1} that doesn't match type {2}. {3}", *id, String(obj->GetType().Fullname), String(klass->GetFullName()), LogContext::GetInfo());
|
||||
{
|
||||
LOG(Warning, "Found scripting object with ID={0} of type {1} that doesn't match type {2}", *id, String(obj->GetType().Fullname), String(klass->GetFullName()));
|
||||
LogContext::Print(LogType::Warning);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
return obj->GetOrCreateManagedInstance();
|
||||
@@ -746,9 +749,10 @@ DEFINE_INTERNAL_CALL(MObject*) ObjectInternal_FindObject(Guid* id, MTypeObject*
|
||||
if (!skipLog)
|
||||
{
|
||||
if (klass)
|
||||
LOG(Warning, "Unable to find scripting object with ID={0}. Required type {1}. {2}", *id, String(klass->GetFullName()), LogContext::GetInfo());
|
||||
LOG(Warning, "Unable to find scripting object with ID={0} of type {1}", *id, String(klass->GetFullName()));
|
||||
else
|
||||
LOG(Warning, "Unable to find scripting object with ID={0}", *id);
|
||||
LogContext::Print(LogType::Warning);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -183,11 +183,15 @@ void MaterialGenerator::ProcessGroupMaterial(Box* box, Node* node, Value& value)
|
||||
break;
|
||||
// Pre-skinned Local Position
|
||||
case 13:
|
||||
value = _treeType == MaterialTreeType::VertexShader ? Value(VariantType::Float3, TEXT("input.PreSkinnedPosition")) : Value::Zero;
|
||||
value = Value(VariantType::Float3, TEXT("input.PreSkinnedPosition"));
|
||||
if (_treeType != MaterialTreeType::VertexShader)
|
||||
value = VsToPs(node, box).AsFloat3();
|
||||
break;
|
||||
// Pre-skinned Local Normal
|
||||
case 14:
|
||||
value = _treeType == MaterialTreeType::VertexShader ? Value(VariantType::Float3, TEXT("input.PreSkinnedNormal")) : Value::Zero;
|
||||
value = Value(VariantType::Float3, TEXT("input.PreSkinnedNormal"));
|
||||
if (_treeType != MaterialTreeType::VertexShader)
|
||||
value = VsToPs(node, box).AsFloat3();
|
||||
break;
|
||||
// Depth
|
||||
case 15:
|
||||
@@ -211,38 +215,8 @@ void MaterialGenerator::ProcessGroupMaterial(Box* box, Node* node, Value& value)
|
||||
break;
|
||||
// Interpolate VS To PS
|
||||
case 20:
|
||||
{
|
||||
const auto input = node->GetBox(0);
|
||||
|
||||
// If used in VS then pass the value from the input box
|
||||
if (_treeType == MaterialTreeType::VertexShader)
|
||||
{
|
||||
value = tryGetValue(input, Value::Zero).AsFloat4();
|
||||
break;
|
||||
}
|
||||
|
||||
// Check if can use more interpolants
|
||||
if (_vsToPsInterpolants.Count() == 16)
|
||||
{
|
||||
OnError(node, box, TEXT("Too many VS to PS interpolants used."));
|
||||
value = Value::Zero;
|
||||
break;
|
||||
}
|
||||
|
||||
// Check if can use interpolants
|
||||
const auto layer = GetRootLayer();
|
||||
if (!layer || layer->Domain == MaterialDomain::Decal || layer->Domain == MaterialDomain::PostProcess)
|
||||
{
|
||||
OnError(node, box, TEXT("VS to PS interpolants are not supported in Decal or Post Process materials."));
|
||||
value = Value::Zero;
|
||||
break;
|
||||
}
|
||||
|
||||
// Indicate the interpolator slot usage
|
||||
value = Value(VariantType::Float4, String::Format(TEXT("input.CustomVSToPS[{0}]"), _vsToPsInterpolants.Count()));
|
||||
_vsToPsInterpolants.Add(input);
|
||||
value = VsToPs(node, node->GetBox(0));
|
||||
break;
|
||||
}
|
||||
// Terrain Holes Mask
|
||||
case 21:
|
||||
{
|
||||
|
||||
@@ -832,4 +832,32 @@ void MaterialGenerator::WriteCustomGlobalCode(const Array<const MaterialGraph::N
|
||||
}
|
||||
}
|
||||
|
||||
ShaderGenerator::Value MaterialGenerator::VsToPs(Node* node, Box* input)
|
||||
{
|
||||
// If used in VS then pass the value from the input box
|
||||
if (_treeType == MaterialTreeType::VertexShader)
|
||||
{
|
||||
return tryGetValue(input, Value::Zero).AsFloat4();
|
||||
}
|
||||
|
||||
// Check if can use more interpolants
|
||||
if (_vsToPsInterpolants.Count() == 16)
|
||||
{
|
||||
OnError(node, input, TEXT("Too many VS to PS interpolants used."));
|
||||
return Value::Zero;
|
||||
}
|
||||
|
||||
// Check if can use interpolants
|
||||
const auto layer = GetRootLayer();
|
||||
if (!layer || layer->Domain == MaterialDomain::Decal || layer->Domain == MaterialDomain::PostProcess)
|
||||
{
|
||||
OnError(node, input, TEXT("VS to PS interpolants are not supported in Decal or Post Process materials."));
|
||||
return Value::Zero;
|
||||
}
|
||||
|
||||
// Indicate the interpolator slot usage
|
||||
_vsToPsInterpolants.Add(input);
|
||||
return Value(VariantType::Float4, String::Format(TEXT("input.CustomVSToPS[{0}]"), _vsToPsInterpolants.Count() - 1));
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -205,6 +205,7 @@ private:
|
||||
MaterialValue AccessParticleAttribute(Node* caller, const StringView& name, ParticleAttributeValueTypes valueType, const Char* index = nullptr, ParticleAttributeSpace space = ParticleAttributeSpace::AsIs);
|
||||
void prepareLayer(MaterialLayer* layer, bool allowVisibleParams);
|
||||
void WriteCustomGlobalCode(const Array<const MaterialGraph::Node*, InlinedAllocation<8>>& nodes, int32 templateInputsMapping);
|
||||
Value VsToPs(Node* node, Box* input);
|
||||
|
||||
public:
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ namespace FlaxEngine.GUI
|
||||
/// Button control
|
||||
/// </summary>
|
||||
[ActorToolbox("GUI")]
|
||||
public class Button : ContainerControl
|
||||
public class Button : Label
|
||||
{
|
||||
/// <summary>
|
||||
/// The default height for the buttons.
|
||||
@@ -20,65 +20,16 @@ namespace FlaxEngine.GUI
|
||||
/// </summary>
|
||||
protected bool _isPressed;
|
||||
|
||||
/// <summary>
|
||||
/// The font.
|
||||
/// </summary>
|
||||
protected FontReference _font;
|
||||
|
||||
/// <summary>
|
||||
/// The text.
|
||||
/// </summary>
|
||||
protected LocalizedString _text = new LocalizedString();
|
||||
|
||||
/// <summary>
|
||||
/// Button text property.
|
||||
/// </summary>
|
||||
[EditorOrder(10), Tooltip("The button label text.")]
|
||||
public LocalizedString Text
|
||||
{
|
||||
get => _text;
|
||||
set => _text = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the font used to draw button text.
|
||||
/// </summary>
|
||||
[EditorDisplay("Text Style"), EditorOrder(2022), ExpandGroups]
|
||||
public FontReference Font
|
||||
{
|
||||
get => _font;
|
||||
set => _font = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the custom material used to render the text. It must has domain set to GUI and have a public texture parameter named Font used to sample font atlas texture with font characters data.
|
||||
/// [Deprecated on 18.09.2024, expires on 18.09.2026]
|
||||
/// </summary>
|
||||
[EditorDisplay("Text Style"), EditorOrder(2021), Tooltip("Custom material used to render the text. It must has domain set to GUI and have a public texture parameter named Font used to sample font atlas texture with font characters data.")]
|
||||
public MaterialBase TextMaterial { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the color used to draw button text.
|
||||
/// </summary>
|
||||
[EditorDisplay("Text Style"), EditorOrder(2020)]
|
||||
public Color TextColor;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the horizontal text alignment within the control bounds.
|
||||
/// </summary>
|
||||
[EditorDisplay("Text Style"), EditorOrder(2027)]
|
||||
public TextAlignment HorizontalAlignment { get; set; } = TextAlignment.Center;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the vertical text alignment within the control bounds.
|
||||
/// </summary>
|
||||
[EditorDisplay("Text Style"), EditorOrder(2028)]
|
||||
public TextAlignment VerticalAlignment { get; set; } = TextAlignment.Center;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the brush used for background drawing.
|
||||
/// </summary>
|
||||
[EditorDisplay("Background Style"), EditorOrder(1999), Tooltip("The brush used for background drawing."), ExpandGroups]
|
||||
public IBrush BackgroundBrush { get; set; }
|
||||
[Serialize, Obsolete("Use Material property instead."), NoUndo]
|
||||
public MaterialBase TextMaterial
|
||||
{
|
||||
get => Material;
|
||||
set => Material = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the background color when button is highlighted.
|
||||
@@ -97,7 +48,7 @@ namespace FlaxEngine.GUI
|
||||
/// </summary>
|
||||
[EditorDisplay("Border Style"), EditorOrder(2010), ExpandGroups]
|
||||
public bool HasBorder { get; set; } = true;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the border thickness.
|
||||
/// </summary>
|
||||
@@ -165,6 +116,7 @@ namespace FlaxEngine.GUI
|
||||
public Button(float x, float y, float width = 120, float height = DefaultHeight)
|
||||
: base(x, y, width, height)
|
||||
{
|
||||
AutoFocus = true;
|
||||
var style = Style.Current;
|
||||
if (style != null)
|
||||
{
|
||||
@@ -242,17 +194,14 @@ namespace FlaxEngine.GUI
|
||||
/// <inheritdoc />
|
||||
public override void DrawSelf()
|
||||
{
|
||||
// Cache data
|
||||
Rectangle clientRect = new Rectangle(Float2.Zero, Size);
|
||||
bool enabled = EnabledInHierarchy;
|
||||
Color backgroundColor = BackgroundColor;
|
||||
Color borderColor = BorderColor;
|
||||
Color textColor = TextColor;
|
||||
if (!enabled)
|
||||
{
|
||||
backgroundColor *= 0.5f;
|
||||
borderColor *= 0.5f;
|
||||
textColor *= 0.6f;
|
||||
}
|
||||
else if (_isPressed)
|
||||
{
|
||||
@@ -274,7 +223,10 @@ namespace FlaxEngine.GUI
|
||||
Render2D.DrawRectangle(clientRect, borderColor, BorderThickness);
|
||||
|
||||
// Draw text
|
||||
Render2D.DrawText(_font?.GetFont(), TextMaterial, _text, clientRect, textColor, HorizontalAlignment, VerticalAlignment);
|
||||
backgroundColor = BackgroundColor;
|
||||
BackgroundColor = Color.Transparent; // Skip background drawing in Control
|
||||
base.DrawSelf();
|
||||
BackgroundColor = backgroundColor;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -339,7 +291,7 @@ namespace FlaxEngine.GUI
|
||||
OnClick();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
if (button == MouseButton.Left && !_isPressed)
|
||||
{
|
||||
OnPressBegin();
|
||||
|
||||
@@ -135,5 +135,14 @@ namespace FlaxEngine.GUI
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnSubmit()
|
||||
{
|
||||
base.OnSubmit();
|
||||
|
||||
// Execute default user interaction via mouse click
|
||||
Clicked?.Invoke(this, MouseButton.Left);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,6 +42,7 @@ namespace FlaxEngine.GUI
|
||||
private bool _autoFitText;
|
||||
private Float2 _textSize;
|
||||
private Float2 _autoFitTextRange = new Float2(0.1f, 100.0f);
|
||||
private Margin _margin;
|
||||
|
||||
/// <summary>
|
||||
/// The font.
|
||||
@@ -57,9 +58,9 @@ namespace FlaxEngine.GUI
|
||||
get => _text;
|
||||
set
|
||||
{
|
||||
if (_text != value)
|
||||
_text = value;
|
||||
if (_autoWidth || _autoHeight || _autoFitText)
|
||||
{
|
||||
_text = value;
|
||||
_textSize = Float2.Zero;
|
||||
PerformLayout();
|
||||
}
|
||||
@@ -129,15 +130,11 @@ namespace FlaxEngine.GUI
|
||||
get => _font;
|
||||
set
|
||||
{
|
||||
if (_font != value)
|
||||
_font = value;
|
||||
if (_autoWidth || _autoHeight || _autoFitText)
|
||||
{
|
||||
_font = value;
|
||||
|
||||
if (_autoWidth || _autoHeight || _autoFitText)
|
||||
{
|
||||
_textSize = Float2.Zero;
|
||||
PerformLayout();
|
||||
}
|
||||
_textSize = Float2.Zero;
|
||||
PerformLayout();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -152,7 +149,18 @@ namespace FlaxEngine.GUI
|
||||
/// Gets or sets the margin for the text within the control bounds.
|
||||
/// </summary>
|
||||
[EditorOrder(70), Tooltip("The margin for the text within the control bounds.")]
|
||||
public Margin Margin { get; set; }
|
||||
public Margin Margin
|
||||
{
|
||||
get => _margin;
|
||||
set
|
||||
{
|
||||
_margin = value;
|
||||
if (_autoWidth || _autoHeight)
|
||||
{
|
||||
PerformLayout();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether clip text during rendering.
|
||||
@@ -161,9 +169,9 @@ namespace FlaxEngine.GUI
|
||||
public bool ClipText { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether set automatic width based on text contents.
|
||||
/// Gets or sets a value indicating whether set automatic width based on text contents. Control size is modified relative to the Pivot.
|
||||
/// </summary>
|
||||
[EditorOrder(85), DefaultValue(false), Tooltip("If checked, the control width will be based on text contents.")]
|
||||
[EditorOrder(85), DefaultValue(false), Tooltip("If checked, the control width will be based on text contents. Control size is modified relative to the Pivot.")]
|
||||
public bool AutoWidth
|
||||
{
|
||||
get => _autoWidth;
|
||||
@@ -178,9 +186,9 @@ namespace FlaxEngine.GUI
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether set automatic height based on text contents.
|
||||
/// Gets or sets a value indicating whether set automatic height based on text contents. Control size is modified relative to the Pivot.
|
||||
/// </summary>
|
||||
[EditorOrder(90), DefaultValue(false), Tooltip("If checked, the control height will be based on text contents.")]
|
||||
[EditorOrder(90), DefaultValue(false), Tooltip("If checked, the control height will be based on text contents. Control size is modified relative to the Pivot.")]
|
||||
public bool AutoHeight
|
||||
{
|
||||
get => _autoHeight;
|
||||
@@ -226,13 +234,8 @@ namespace FlaxEngine.GUI
|
||||
/// Initializes a new instance of the <see cref="Label"/> class.
|
||||
/// </summary>
|
||||
public Label()
|
||||
: base(0, 0, 100, 20)
|
||||
: this(0, 0, 100, 20)
|
||||
{
|
||||
AutoFocus = false;
|
||||
var style = Style.Current;
|
||||
Font = new FontReference(style.FontMedium);
|
||||
TextColor = style.Foreground;
|
||||
TextColorHighlighted = style.Foreground;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -241,9 +244,12 @@ namespace FlaxEngine.GUI
|
||||
{
|
||||
AutoFocus = false;
|
||||
var style = Style.Current;
|
||||
Font = new FontReference(style.FontMedium);
|
||||
TextColor = style.Foreground;
|
||||
TextColorHighlighted = style.Foreground;
|
||||
if (style != null)
|
||||
{
|
||||
Font = new FontReference(style.FontMedium);
|
||||
TextColor = style.Foreground;
|
||||
TextColorHighlighted = style.Foreground;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -251,31 +257,24 @@ namespace FlaxEngine.GUI
|
||||
{
|
||||
base.DrawSelf();
|
||||
|
||||
var rect = new Rectangle(new Float2(Margin.Left, Margin.Top), Size - Margin.Size);
|
||||
|
||||
if (ClipText)
|
||||
Render2D.PushClip(new Rectangle(Float2.Zero, Size));
|
||||
|
||||
var rect = new Rectangle(new Float2(Margin.Left, Margin.Top), Size - Margin.Size);
|
||||
var color = IsMouseOver || IsNavFocused ? TextColorHighlighted : TextColor;
|
||||
|
||||
if (!EnabledInHierarchy)
|
||||
color *= 0.6f;
|
||||
|
||||
var scale = 1.0f;
|
||||
var hAlignment = HorizontalAlignment;
|
||||
var wAlignment = VerticalAlignment;
|
||||
if (_autoFitText)
|
||||
if (_autoFitText && !_textSize.IsZero)
|
||||
{
|
||||
if (!_textSize.IsZero)
|
||||
{
|
||||
scale = (rect.Size / _textSize).MinValue;
|
||||
scale = Mathf.Clamp(scale, _autoFitTextRange.X, _autoFitTextRange.Y);
|
||||
}
|
||||
scale = (rect.Size / _textSize).MinValue;
|
||||
scale = Mathf.Clamp(scale, _autoFitTextRange.X, _autoFitTextRange.Y);
|
||||
}
|
||||
|
||||
Font font = GetFont();
|
||||
var text = ConvertedText();
|
||||
|
||||
Render2D.DrawText(font, Material, text, rect, color, hAlignment, wAlignment, Wrapping, BaseLinesGapScale, scale);
|
||||
|
||||
if (ClipText)
|
||||
@@ -336,7 +335,9 @@ namespace FlaxEngine.GUI
|
||||
size.X = _textSize.X + Margin.Width;
|
||||
if (_autoHeight)
|
||||
size.Y = _textSize.Y + Margin.Height;
|
||||
var pivotRelative = PivotRelative;
|
||||
Size = size;
|
||||
PivotRelative = pivotRelative;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -291,24 +291,7 @@ namespace FlaxEngine.GUI
|
||||
{
|
||||
float alpha = Mathf.Saturate(Mathf.Cos(_animateTime * CaretFlashSpeed) * 0.5f + 0.7f);
|
||||
alpha = alpha * alpha * alpha * alpha * alpha * alpha;
|
||||
if (CaretPosition == 0)
|
||||
{
|
||||
var bounds = CaretBounds;
|
||||
if (_layout.VerticalAlignment == TextAlignment.Center)
|
||||
bounds.Y = _layout.Bounds.Y + _layout.Bounds.Height * 0.5f - bounds.Height * 0.5f;
|
||||
else if (_layout.VerticalAlignment == TextAlignment.Far)
|
||||
bounds.Y = _layout.Bounds.Y + _layout.Bounds.Height - bounds.Height;
|
||||
|
||||
if (_layout.HorizontalAlignment == TextAlignment.Center)
|
||||
bounds.X = _layout.Bounds.X + _layout.Bounds.Width * 0.5f - bounds.Width * 0.5f;
|
||||
else if (_layout.HorizontalAlignment == TextAlignment.Far)
|
||||
bounds.X = _layout.Bounds.X + _layout.Bounds.Width - bounds.Width;
|
||||
Render2D.FillRectangle(bounds, CaretColor * alpha);
|
||||
}
|
||||
else
|
||||
{
|
||||
Render2D.FillRectangle(CaretBounds, CaretColor * alpha);
|
||||
}
|
||||
Render2D.FillRectangle(CaretBounds, CaretColor * alpha);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -234,6 +234,11 @@ namespace FlaxEngine.GUI
|
||||
if (_bounds.Size.Equals(ref value))
|
||||
return;
|
||||
var bounds = new Rectangle(_bounds.Location, value);
|
||||
if (_pivotRelativeSizing)
|
||||
{
|
||||
var delta = _bounds.Size - value;
|
||||
bounds.Location += delta * Pivot;
|
||||
}
|
||||
SetBounds(ref bounds);
|
||||
}
|
||||
}
|
||||
@@ -566,7 +571,7 @@ namespace FlaxEngine.GUI
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the anchor preset for the control. Can be use to auto-place the control for a given preset or can preserve the current control bounds.
|
||||
/// Sets the anchor preset for the control. Can be used to auto-place the control for a given preset or can preserve the current control bounds.
|
||||
/// </summary>
|
||||
/// <param name="anchorPreset">The anchor preset to set.</param>
|
||||
/// <param name="preserveBounds">True if preserve current control bounds, otherwise will align control position accordingly to the anchor location.</param>
|
||||
|
||||
@@ -81,6 +81,7 @@ namespace FlaxEngine.GUI
|
||||
// Style
|
||||
|
||||
private Color _backgroundColor = Color.Transparent;
|
||||
private IBrush _backgroundBrush = null;
|
||||
|
||||
// Tooltip
|
||||
|
||||
@@ -174,6 +175,25 @@ namespace FlaxEngine.GUI
|
||||
set => _backgroundColor = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets control background brush used to fill the contents. Uses Background Color property as tint color.
|
||||
/// </summary>
|
||||
[EditorDisplay("Background Style"), EditorOrder(2001)]
|
||||
public IBrush BackgroundBrush
|
||||
{
|
||||
get => _backgroundBrush;
|
||||
set
|
||||
{
|
||||
_backgroundBrush = value;
|
||||
|
||||
#if FLAX_EDITOR
|
||||
// Auto-reset background color so brush is visible as it uses it for tint
|
||||
if (value != null && _backgroundColor == Color.Transparent && FlaxEditor.CustomEditors.CustomEditor.IsSettingValue)
|
||||
_backgroundColor = Color.White;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the anchor preset used by the control anchors (based on <see cref="AnchorMin"/> and <see cref="AnchorMax"/>).
|
||||
/// </summary>
|
||||
@@ -416,9 +436,14 @@ namespace FlaxEngine.GUI
|
||||
public virtual void Draw()
|
||||
{
|
||||
// Paint Background
|
||||
if (_backgroundColor.A > 0.0f)
|
||||
var rect = new Rectangle(Float2.Zero, _bounds.Size);
|
||||
if (BackgroundBrush != null)
|
||||
{
|
||||
Render2D.FillRectangle(new Rectangle(Float2.Zero, Size), _backgroundColor);
|
||||
BackgroundBrush.Draw(rect, _backgroundColor);
|
||||
}
|
||||
else if (_backgroundColor.A > 0.0f)
|
||||
{
|
||||
Render2D.FillRectangle(rect, _backgroundColor);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -614,6 +639,18 @@ namespace FlaxEngine.GUI
|
||||
case NavDirection.Down: return NavTargetDown;
|
||||
case NavDirection.Left: return NavTargetLeft;
|
||||
case NavDirection.Right: return NavTargetRight;
|
||||
case NavDirection.Next:
|
||||
if (NavTargetRight != null)
|
||||
return NavTargetRight;
|
||||
if (NavTargetDown != null)
|
||||
return NavTargetDown;
|
||||
return null;
|
||||
case NavDirection.Previous:
|
||||
if (NavTargetLeft != null)
|
||||
return NavTargetLeft;
|
||||
if (NavTargetUp != null)
|
||||
return NavTargetUp;
|
||||
return null;
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,8 @@ namespace FlaxEngine.GUI
|
||||
base.PerformLayoutBeforeChildren();
|
||||
|
||||
// Pre-set height of all controls
|
||||
if (!ControlChildSize)
|
||||
return;
|
||||
float h = Height - _margin.Height;
|
||||
for (int i = 0; i < _children.Count; i++)
|
||||
{
|
||||
@@ -40,6 +42,7 @@ namespace FlaxEngine.GUI
|
||||
float left = _margin.Left;
|
||||
float right = _margin.Right;
|
||||
float h = Height - _margin.Height;
|
||||
float maxHeight = h;
|
||||
bool hasAnyLeft = false, hasAnyRight = false;
|
||||
for (int i = 0; i < _children.Count; i++)
|
||||
{
|
||||
@@ -47,18 +50,20 @@ namespace FlaxEngine.GUI
|
||||
if (c.Visible)
|
||||
{
|
||||
var w = c.Width;
|
||||
var ch = ControlChildSize ? h : c.Height;
|
||||
if (Mathf.IsZero(c.AnchorMin.X) && Mathf.IsZero(c.AnchorMax.X))
|
||||
{
|
||||
c.Bounds = new Rectangle(left + _offset.X, _margin.Top + _offset.Y, w, h);
|
||||
c.Bounds = new Rectangle(left + _offset.X, _margin.Top + _offset.Y, w, ch);
|
||||
left = c.Right + _spacing;
|
||||
hasAnyLeft = true;
|
||||
}
|
||||
else if (Mathf.IsOne(c.AnchorMin.X) && Mathf.IsOne(c.AnchorMax.X))
|
||||
{
|
||||
right += w + _spacing;
|
||||
c.Bounds = new Rectangle(Width - right + _offset.X, _margin.Top + _offset.Y, w, h);
|
||||
c.Bounds = new Rectangle(Width - right + _offset.X, _margin.Top + _offset.Y, w, ch);
|
||||
hasAnyRight = true;
|
||||
}
|
||||
maxHeight = Mathf.Max(maxHeight, ch);
|
||||
}
|
||||
}
|
||||
if (hasAnyLeft)
|
||||
@@ -68,7 +73,13 @@ namespace FlaxEngine.GUI
|
||||
|
||||
// Update size
|
||||
if (_autoSize)
|
||||
Width = left + right;
|
||||
{
|
||||
var size = Size;
|
||||
size.X = left + right;
|
||||
if (!ControlChildSize)
|
||||
size.Y = maxHeight;
|
||||
Size = size;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace FlaxEngine.GUI
|
||||
{
|
||||
/// <summary>
|
||||
@@ -133,7 +135,7 @@ namespace FlaxEngine.GUI
|
||||
/// <summary>
|
||||
/// Gets or sets the value indicating whenever the panel size will be based on a children dimensions.
|
||||
/// </summary>
|
||||
[EditorOrder(30), Tooltip("If checked, the panel size will be based on a children dimensions.")]
|
||||
[EditorOrder(30), DefaultValue(true), Tooltip("If checked, the panel size will be based on a children dimensions.")]
|
||||
public bool AutoSize
|
||||
{
|
||||
get => _autoSize;
|
||||
@@ -147,6 +149,12 @@ namespace FlaxEngine.GUI
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the value indicating whenever the panel can resize children controls (eg. auto-fit width/height).
|
||||
/// </summary>
|
||||
[EditorOrder(35), DefaultValue(true), Tooltip("If checked, the panel can resize children controls (eg. auto-fit width/height).")]
|
||||
public bool ControlChildSize { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the panel area margin.
|
||||
/// </summary>
|
||||
|
||||
@@ -22,6 +22,8 @@ namespace FlaxEngine.GUI
|
||||
base.PerformLayoutBeforeChildren();
|
||||
|
||||
// Pre-set width of all controls
|
||||
if (!ControlChildSize)
|
||||
return;
|
||||
float w = Width - _margin.Width;
|
||||
for (int i = 0; i < _children.Count; i++)
|
||||
{
|
||||
@@ -40,6 +42,7 @@ namespace FlaxEngine.GUI
|
||||
float top = _margin.Top;
|
||||
float bottom = _margin.Bottom;
|
||||
float w = Width - _margin.Width;
|
||||
float maxWidth = w;
|
||||
bool hasAnyTop = false, hasAnyBottom = false;
|
||||
for (int i = 0; i < _children.Count; i++)
|
||||
{
|
||||
@@ -47,18 +50,20 @@ namespace FlaxEngine.GUI
|
||||
if (c.Visible)
|
||||
{
|
||||
var h = c.Height;
|
||||
var cw = ControlChildSize ? w : c.Width;
|
||||
if (Mathf.IsZero(c.AnchorMin.Y) && Mathf.IsZero(c.AnchorMax.Y))
|
||||
{
|
||||
c.Bounds = new Rectangle(_margin.Left + _offset.X, top + _offset.Y, w, h);
|
||||
c.Bounds = new Rectangle(_margin.Left + _offset.X, top + _offset.Y, cw, h);
|
||||
top = c.Bottom + _spacing;
|
||||
hasAnyTop = true;
|
||||
}
|
||||
else if (Mathf.IsOne(c.AnchorMin.Y) && Mathf.IsOne(c.AnchorMax.Y))
|
||||
{
|
||||
bottom += h + _spacing;
|
||||
c.Bounds = new Rectangle(_margin.Left + _offset.X, Height - bottom + _offset.Y, w, h);
|
||||
c.Bounds = new Rectangle(_margin.Left + _offset.X, Height - bottom + _offset.Y, cw, h);
|
||||
hasAnyBottom = true;
|
||||
}
|
||||
maxWidth = Mathf.Max(maxWidth, cw);
|
||||
}
|
||||
}
|
||||
if (hasAnyTop)
|
||||
@@ -68,7 +73,13 @@ namespace FlaxEngine.GUI
|
||||
|
||||
// Update size
|
||||
if (_autoSize)
|
||||
Height = top + bottom;
|
||||
{
|
||||
var size = Size;
|
||||
size.Y = top + bottom;
|
||||
if (!ControlChildSize)
|
||||
size.X = maxWidth;
|
||||
Size = size;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,13 +43,9 @@ float4 PointInParallelogram(float2 p, float2 a, float4 bc)
|
||||
void PerformClipping(float2 clipOrigin, float2 windowPos, float4 clipExtents)
|
||||
{
|
||||
#if CLIPPING_ENABLE
|
||||
|
||||
// Clip pixels which are outside of the clipping rect
|
||||
// Clip pixels which are outside the clipping rect
|
||||
float4 clipTest = PointInParallelogram(windowPos, clipOrigin, clipExtents);
|
||||
|
||||
// Clip pixels which are outside of the clipping rect
|
||||
clip(clipTest);
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -1478,8 +1478,8 @@ namespace Flax.Build.Bindings
|
||||
public static class NativeToManaged
|
||||
{
|
||||
public static {{classInfo.Name}} ConvertToManaged(IntPtr unmanaged) => Unsafe.As<{{classInfo.Name}}>(ManagedHandleMarshaller.NativeToManaged.ConvertToManaged(unmanaged));
|
||||
public static IntPtr ConvertToUnmanaged({{classInfo.Name}} managed) => ManagedHandleMarshaller.ManagedToNative.ConvertToUnmanaged(managed);
|
||||
public static void Free(IntPtr unmanaged) => ManagedHandleMarshaller.NativeToManaged.Free(unmanaged);
|
||||
public static IntPtr ConvertToUnmanaged({{classInfo.Name}} managed) => managed != null ? ManagedHandle.ToIntPtr(managed, GCHandleType.Weak) : IntPtr.Zero;
|
||||
public static void Free(IntPtr unmanaged) {}
|
||||
}
|
||||
#if FLAX_EDITOR
|
||||
[HideInEditor]
|
||||
@@ -1487,8 +1487,8 @@ namespace Flax.Build.Bindings
|
||||
public static class ManagedToNative
|
||||
{
|
||||
public static {{classInfo.Name}} ConvertToManaged(IntPtr unmanaged) => Unsafe.As<{{classInfo.Name}}>(ManagedHandleMarshaller.NativeToManaged.ConvertToManaged(unmanaged));
|
||||
public static IntPtr ConvertToUnmanaged({{classInfo.Name}} managed) => ManagedHandleMarshaller.ManagedToNative.ConvertToUnmanaged(managed);
|
||||
public static void Free(IntPtr unmanaged) => ManagedHandleMarshaller.ManagedToNative.Free(unmanaged);
|
||||
public static IntPtr ConvertToUnmanaged({{classInfo.Name}} managed) => managed != null ? ManagedHandle.ToIntPtr(managed, GCHandleType.Weak) : IntPtr.Zero;
|
||||
public static void Free(IntPtr unmanaged) {}
|
||||
}
|
||||
#if FLAX_EDITOR
|
||||
[HideInEditor]
|
||||
|
||||
@@ -242,6 +242,11 @@ namespace Flax.Build.Bindings
|
||||
}
|
||||
|
||||
// Forward type
|
||||
if (token.Value == "enum")
|
||||
{
|
||||
context.Tokenizer.SkipUntil(TokenType.Identifier);
|
||||
token = context.Tokenizer.CurrentToken;
|
||||
}
|
||||
if (token.Value == "class")
|
||||
{
|
||||
context.Tokenizer.SkipUntil(TokenType.Identifier);
|
||||
|
||||
Reference in New Issue
Block a user