Merge remote-tracking branch 'origin/master' into 1.8
# Conflicts: # Flax.flaxproj
This commit is contained in:
@@ -86,6 +86,7 @@ namespace FlaxEditor.Content
|
||||
Folder.ParentFolder = parent.Folder;
|
||||
Parent = parent;
|
||||
}
|
||||
IconColor = Style.Current.Foreground;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -157,7 +157,7 @@ namespace FlaxEditor.CustomEditors
|
||||
var values = _values;
|
||||
var presenter = _presenter;
|
||||
var layout = _layout;
|
||||
if (layout.Editors.Count != 1)
|
||||
if (layout.Editors.Count > 1)
|
||||
{
|
||||
// There are more editors using the same layout so rebuild parent editor to prevent removing others editors
|
||||
_parent?.RebuildLayout();
|
||||
|
||||
@@ -695,7 +695,41 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
private void SetType(ref ScriptType controlType, UIControl uiControl)
|
||||
{
|
||||
string previousName = uiControl.Control?.GetType().Name ?? nameof(UIControl);
|
||||
uiControl.Control = (Control)controlType.CreateInstance();
|
||||
|
||||
var oldControlType = (Control)uiControl.Control;
|
||||
var newControlType = (Control)controlType.CreateInstance();
|
||||
|
||||
// copy old control data to new control
|
||||
if (oldControlType != null)
|
||||
{
|
||||
newControlType.Visible = oldControlType.Visible;
|
||||
newControlType.Enabled = oldControlType.Enabled;
|
||||
newControlType.AutoFocus = oldControlType.AutoFocus;
|
||||
|
||||
newControlType.AnchorMin = oldControlType.AnchorMin;
|
||||
newControlType.AnchorMax = oldControlType.AnchorMax;
|
||||
newControlType.Offsets = oldControlType.Offsets;
|
||||
|
||||
newControlType.LocalLocation = oldControlType.LocalLocation;
|
||||
newControlType.Scale = oldControlType.Scale;
|
||||
newControlType.Bounds = oldControlType.Bounds;
|
||||
newControlType.Width = oldControlType.Width;
|
||||
newControlType.Height = oldControlType.Height;
|
||||
newControlType.Center = oldControlType.Center;
|
||||
newControlType.PivotRelative = oldControlType.PivotRelative;
|
||||
|
||||
newControlType.Pivot = oldControlType.Pivot;
|
||||
newControlType.Shear = oldControlType.Shear;
|
||||
newControlType.Rotation = oldControlType.Rotation;
|
||||
}
|
||||
if (oldControlType is ContainerControl oldContainer && newControlType is ContainerControl newContainer)
|
||||
{
|
||||
newContainer.CullChildren = oldContainer.CullChildren;
|
||||
newContainer.ClipChildren = oldContainer.ClipChildren;
|
||||
}
|
||||
|
||||
uiControl.Control = newControlType;
|
||||
|
||||
if (uiControl.Name.StartsWith(previousName))
|
||||
{
|
||||
string newName = controlType.Name + uiControl.Name.Substring(previousName.Length);
|
||||
|
||||
@@ -113,7 +113,7 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
public override void Initialize(LayoutElementsContainer layout)
|
||||
{
|
||||
// No support for different collections for now
|
||||
if (HasDifferentValues || HasDifferentTypes)
|
||||
if (HasDifferentTypes)
|
||||
return;
|
||||
|
||||
var size = Count;
|
||||
|
||||
@@ -28,14 +28,16 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
var group = layout.Group("Entry");
|
||||
_group = group;
|
||||
|
||||
if (ParentEditor == null)
|
||||
if (ParentEditor == null || HasDifferentTypes)
|
||||
return;
|
||||
var entry = (ModelInstanceEntry)Values[0];
|
||||
var entryIndex = ParentEditor.ChildrenEditors.IndexOf(this);
|
||||
var materialLabel = new PropertyNameLabel("Material");
|
||||
materialLabel.TooltipText = "The mesh surface material used for the rendering.";
|
||||
if (ParentEditor.ParentEditor?.Values[0] is ModelInstanceActor modelInstance)
|
||||
var parentEditorValues = ParentEditor.ParentEditor?.Values;
|
||||
if (parentEditorValues?[0] is ModelInstanceActor modelInstance)
|
||||
{
|
||||
// TODO: store _modelInstance and _material in array for each selected model instance actor
|
||||
_entryIndex = entryIndex;
|
||||
_modelInstance = modelInstance;
|
||||
var slots = modelInstance.MaterialSlots;
|
||||
@@ -56,6 +58,8 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
|
||||
// Create material picker
|
||||
var materialValue = new CustomValueContainer(new ScriptType(typeof(MaterialBase)), _material, (instance, index) => _material, (instance, index, value) => _material = value as MaterialBase);
|
||||
for (var i = 1; i < parentEditorValues.Count; i++)
|
||||
materialValue.Add(_material);
|
||||
var materialEditor = (AssetRefEditor)_group.Property(materialLabel, materialValue);
|
||||
materialEditor.Values.SetDefaultValue(defaultValue);
|
||||
materialEditor.RefreshDefaultValue();
|
||||
|
||||
@@ -173,8 +173,8 @@ namespace FlaxEditor.GUI
|
||||
else
|
||||
{
|
||||
// No element selected
|
||||
Render2D.FillRectangle(iconRect, new Color(0.2f));
|
||||
Render2D.DrawText(style.FontMedium, "No asset\nselected", iconRect, Color.Wheat, TextAlignment.Center, TextAlignment.Center, TextWrapping.NoWrap, 1.0f, Height / DefaultIconSize);
|
||||
Render2D.FillRectangle(iconRect, style.BackgroundNormal);
|
||||
Render2D.DrawText(style.FontMedium, "No asset\nselected", iconRect, Color.Orange, TextAlignment.Center, TextAlignment.Center, TextWrapping.NoWrap, 1.0f, Height / DefaultIconSize);
|
||||
}
|
||||
|
||||
// Check if drag is over
|
||||
|
||||
@@ -14,6 +14,8 @@ namespace FlaxEditor.GUI.Input
|
||||
[HideInEditor]
|
||||
public class ColorValueBox : Control
|
||||
{
|
||||
private bool _isMouseDown;
|
||||
|
||||
/// <summary>
|
||||
/// Delegate function used for the color picker events handling.
|
||||
/// </summary>
|
||||
@@ -134,11 +136,22 @@ namespace FlaxEditor.GUI.Input
|
||||
Render2D.DrawRectangle(r, IsMouseOver || IsNavFocused ? style.BackgroundSelected : Color.Black);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool OnMouseDown(Float2 location, MouseButton button)
|
||||
{
|
||||
_isMouseDown = true;
|
||||
return base.OnMouseDown(location, button);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool OnMouseUp(Float2 location, MouseButton button)
|
||||
{
|
||||
Focus();
|
||||
OnSubmit();
|
||||
if (_isMouseDown)
|
||||
{
|
||||
_isMouseDown = false;
|
||||
Focus();
|
||||
OnSubmit();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -100,9 +100,10 @@ namespace FlaxEditor.GUI
|
||||
AutoResize = true;
|
||||
Offsets = new Margin(0, 0, 0, IconSize);
|
||||
|
||||
_mouseOverColor = style.Foreground;
|
||||
_selectedColor = style.Foreground;
|
||||
_defaultColor = style.ForegroundGrey;
|
||||
// Ignoring style on purpose (style would make sense if the icons were white, but they are colored)
|
||||
_mouseOverColor = new Color(0.8f, 0.8f, 0.8f, 1f);
|
||||
_selectedColor = Color.White;
|
||||
_defaultColor = new Color(0.7f, 0.7f, 0.7f, 0.5f);
|
||||
|
||||
for (int i = 0; i < platforms.Length; i++)
|
||||
{
|
||||
|
||||
@@ -98,7 +98,7 @@ namespace FlaxEditor.GUI
|
||||
rect.Width -= leftDepthMargin;
|
||||
|
||||
Render2D.PushClip(rect);
|
||||
Render2D.DrawText(style.FontMedium, text, rect, Color.White, column.CellAlignment, TextAlignment.Center);
|
||||
Render2D.DrawText(style.FontMedium, text, rect, style.Foreground, column.CellAlignment, TextAlignment.Center);
|
||||
Render2D.PopClip();
|
||||
|
||||
x += width;
|
||||
|
||||
@@ -239,7 +239,7 @@ namespace FlaxEditor.GUI.Tabs
|
||||
/// </summary>
|
||||
public Tab SelectedTab
|
||||
{
|
||||
get => _selectedIndex == -1 && Children.Count > _selectedIndex + 1 ? null : Children[_selectedIndex + 1] as Tab;
|
||||
get => _selectedIndex < 0 || Children.Count <= _selectedIndex ? null : Children[_selectedIndex + 1] as Tab;
|
||||
set => SelectedTabIndex = value != null ? Children.IndexOf(value) - 1 : -1;
|
||||
}
|
||||
|
||||
|
||||
@@ -627,10 +627,11 @@ namespace FlaxEditor.GUI.Timeline
|
||||
Parent = this
|
||||
};
|
||||
|
||||
var style = Style.Current;
|
||||
var headerTopArea = new ContainerControl
|
||||
{
|
||||
AutoFocus = false,
|
||||
BackgroundColor = Style.Current.LightBackground,
|
||||
BackgroundColor = style.LightBackground,
|
||||
AnchorPreset = AnchorPresets.HorizontalStretchTop,
|
||||
Offsets = new Margin(0, 0, 0, HeaderTopAreaHeight),
|
||||
Parent = _splitter.Panel1
|
||||
@@ -683,7 +684,7 @@ namespace FlaxEditor.GUI.Timeline
|
||||
{
|
||||
AutoFocus = false,
|
||||
ClipChildren = false,
|
||||
BackgroundColor = Style.Current.LightBackground,
|
||||
BackgroundColor = style.LightBackground,
|
||||
AnchorPreset = AnchorPresets.HorizontalStretchBottom,
|
||||
Offsets = new Margin(0, 0, -playbackButtonsSize, playbackButtonsSize),
|
||||
Parent = _splitter.Panel1
|
||||
@@ -845,7 +846,7 @@ namespace FlaxEditor.GUI.Timeline
|
||||
_timeIntervalsHeader = new TimeIntervalsHeader(this)
|
||||
{
|
||||
AutoFocus = false,
|
||||
BackgroundColor = Style.Current.Background.RGBMultiplied(0.9f),
|
||||
BackgroundColor = style.Background.RGBMultiplied(0.9f),
|
||||
AnchorPreset = AnchorPresets.HorizontalStretchTop,
|
||||
Offsets = new Margin(0, 0, 0, HeaderTopAreaHeight),
|
||||
Parent = _splitter.Panel2
|
||||
@@ -854,7 +855,7 @@ namespace FlaxEditor.GUI.Timeline
|
||||
{
|
||||
AutoFocus = false,
|
||||
ClipChildren = false,
|
||||
BackgroundColor = Style.Current.Background.RGBMultiplied(0.7f),
|
||||
BackgroundColor = style.Background.RGBMultiplied(0.7f),
|
||||
AnchorPreset = AnchorPresets.StretchAll,
|
||||
Offsets = new Margin(0, 0, HeaderTopAreaHeight, 0),
|
||||
Parent = _splitter.Panel2
|
||||
|
||||
@@ -330,14 +330,15 @@ bool ManagedEditor::CanReloadScripts()
|
||||
|
||||
bool ManagedEditor::CanAutoBuildCSG()
|
||||
{
|
||||
if (!ManagedEditorOptions.AutoRebuildCSG)
|
||||
return false;
|
||||
|
||||
// Skip calls from non-managed thread (eg. physics worker)
|
||||
if (!MCore::Thread::IsAttached())
|
||||
return false;
|
||||
|
||||
if (!HasManagedInstance())
|
||||
return false;
|
||||
if (!ManagedEditorOptions.AutoRebuildCSG)
|
||||
return false;
|
||||
if (Internal_CanAutoBuildCSG == nullptr)
|
||||
{
|
||||
Internal_CanAutoBuildCSG = GetClass()->GetMethod("Internal_CanAutoBuildCSG");
|
||||
@@ -348,14 +349,15 @@ bool ManagedEditor::CanAutoBuildCSG()
|
||||
|
||||
bool ManagedEditor::CanAutoBuildNavMesh()
|
||||
{
|
||||
if (!ManagedEditorOptions.AutoRebuildNavMesh)
|
||||
return false;
|
||||
|
||||
// Skip calls from non-managed thread (eg. physics worker)
|
||||
if (!MCore::Thread::IsAttached())
|
||||
return false;
|
||||
|
||||
if (!HasManagedInstance())
|
||||
return false;
|
||||
if (!ManagedEditorOptions.AutoRebuildNavMesh)
|
||||
return false;
|
||||
if (Internal_CanAutoBuildNavMesh == nullptr)
|
||||
{
|
||||
Internal_CanAutoBuildNavMesh = GetClass()->GetMethod("Internal_CanAutoBuildNavMesh");
|
||||
|
||||
@@ -124,6 +124,7 @@ namespace FlaxEditor.Modules
|
||||
if (!Editor.StateMachine.CurrentState.CanEditScene)
|
||||
return;
|
||||
undo = Editor.Undo;
|
||||
Editor.Scene.MarkSceneEdited(actor.Scene);
|
||||
}
|
||||
|
||||
// Record undo for prefab creating (backend links the target instance with the prefab)
|
||||
|
||||
@@ -208,13 +208,20 @@ namespace FlaxEditor.Options
|
||||
|
||||
// If a non-default style was chosen, switch to that style
|
||||
string styleName = themeOptions.SelectedStyle;
|
||||
if (styleName != "Default" && themeOptions.Styles.TryGetValue(styleName, out var style) && style != null)
|
||||
if (styleName != ThemeOptions.DefaultName && styleName != ThemeOptions.LightDefault && themeOptions.Styles.TryGetValue(styleName, out var style) && style != null)
|
||||
{
|
||||
Style.Current = style;
|
||||
}
|
||||
else
|
||||
{
|
||||
Style.Current = CreateDefaultStyle();
|
||||
if (styleName == ThemeOptions.LightDefault)
|
||||
{
|
||||
Style.Current = CreateLightStyle();
|
||||
}
|
||||
else
|
||||
{
|
||||
Style.Current = CreateDefaultStyle();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -224,7 +231,6 @@ namespace FlaxEditor.Options
|
||||
/// <returns>The style object.</returns>
|
||||
public Style CreateDefaultStyle()
|
||||
{
|
||||
// Metro Style colors
|
||||
var options = Options;
|
||||
var style = new Style
|
||||
{
|
||||
@@ -233,6 +239,7 @@ namespace FlaxEditor.Options
|
||||
Foreground = Color.FromBgra(0xFFFFFFFF),
|
||||
ForegroundGrey = Color.FromBgra(0xFFA9A9B3),
|
||||
ForegroundDisabled = Color.FromBgra(0xFF787883),
|
||||
ForegroundViewport = Color.FromBgra(0xFFFFFFFF),
|
||||
BackgroundHighlighted = Color.FromBgra(0xFF54545C),
|
||||
BorderHighlighted = Color.FromBgra(0xFF6A6A75),
|
||||
BackgroundSelected = Color.FromBgra(0xFF007ACC),
|
||||
@@ -274,7 +281,58 @@ namespace FlaxEditor.Options
|
||||
SharedTooltip = new Tooltip(),
|
||||
};
|
||||
style.DragWindow = style.BackgroundSelected * 0.7f;
|
||||
return style;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the light style (2nd default).
|
||||
/// </summary>
|
||||
/// <returns>The style object.</returns>
|
||||
public Style CreateLightStyle()
|
||||
{
|
||||
var options = Options;
|
||||
var style = new Style
|
||||
{
|
||||
Background = new Color(0.92f, 0.92f, 0.92f, 1f),
|
||||
LightBackground = new Color(0.84f, 0.84f, 0.88f, 1f),
|
||||
DragWindow = new Color(0.0f, 0.26f, 0.43f, 0.70f),
|
||||
Foreground = new Color(0.0f, 0.0f, 0.0f, 1f),
|
||||
ForegroundGrey = new Color(0.30f, 0.30f, 0.31f, 1f),
|
||||
ForegroundDisabled = new Color(0.45f, 0.45f, 0.49f, 1f),
|
||||
ForegroundViewport = new Color(1.0f, 1.0f, 1.0f, 1f),
|
||||
BackgroundHighlighted = new Color(0.59f, 0.59f, 0.64f, 1f),
|
||||
BorderHighlighted = new Color(0.50f, 0.50f, 0.55f, 1f),
|
||||
BackgroundSelected = new Color(0.00f, 0.46f, 0.78f, 0.78f),
|
||||
BorderSelected = new Color(0.11f, 0.57f, 0.88f, 0.65f),
|
||||
BackgroundNormal = new Color(0.67f, 0.67f, 0.75f, 1f),
|
||||
BorderNormal = new Color(0.59f, 0.59f, 0.64f, 1f),
|
||||
TextBoxBackground = new Color(0.75f, 0.75f, 0.81f, 1f),
|
||||
TextBoxBackgroundSelected = new Color(0.73f, 0.73f, 0.80f, 1f),
|
||||
CollectionBackgroundColor = new Color(0.85f, 0.85f, 0.88f, 1f),
|
||||
ProgressNormal = new Color(0.03f, 0.65f, 0.12f, 1f),
|
||||
|
||||
// Fonts
|
||||
FontTitle = options.Interface.TitleFont.GetFont(),
|
||||
FontLarge = options.Interface.LargeFont.GetFont(),
|
||||
FontMedium = options.Interface.MediumFont.GetFont(),
|
||||
FontSmall = options.Interface.SmallFont.GetFont(),
|
||||
|
||||
// Icons
|
||||
ArrowDown = Editor.Icons.ArrowDown12,
|
||||
ArrowRight = Editor.Icons.ArrowRight12,
|
||||
Search = Editor.Icons.Search12,
|
||||
Settings = Editor.Icons.Settings12,
|
||||
Cross = Editor.Icons.Cross12,
|
||||
CheckBoxIntermediate = Editor.Icons.CheckBoxIntermediate12,
|
||||
CheckBoxTick = Editor.Icons.CheckBoxTick12,
|
||||
StatusBarSizeGrip = Editor.Icons.WindowDrag12,
|
||||
Translate = Editor.Icons.Translate32,
|
||||
Rotate = Editor.Icons.Rotate32,
|
||||
Scale = Editor.Icons.Scale32,
|
||||
Scalar = Editor.Icons.Scalar32,
|
||||
|
||||
SharedTooltip = new Tooltip(),
|
||||
};
|
||||
return style;
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,9 @@ namespace FlaxEditor.Options
|
||||
[CustomEditor(typeof(ThemeOptionsEditor))]
|
||||
public sealed class ThemeOptions
|
||||
{
|
||||
internal const string DefaultName = "Default";
|
||||
internal const string LightDefault = "LightDefault";
|
||||
|
||||
internal class ThemeOptionsEditor : Editor<ThemeOptions>
|
||||
{
|
||||
private LabelElement _infoLabel;
|
||||
@@ -63,13 +66,14 @@ namespace FlaxEditor.Options
|
||||
private void ReloadOptions(ComboBox obj)
|
||||
{
|
||||
var themeOptions = (ThemeOptions)ParentEditor.Values[0];
|
||||
var options = new string[themeOptions.Styles.Count + 1];
|
||||
options[0] = "Default";
|
||||
var options = new string[themeOptions.Styles.Count + 2];
|
||||
options[0] = DefaultName;
|
||||
options[1] = LightDefault;
|
||||
|
||||
int i = 0;
|
||||
foreach (var styleName in themeOptions.Styles.Keys)
|
||||
{
|
||||
options[i + 1] = styleName;
|
||||
options[i + 2] = styleName;
|
||||
i++;
|
||||
}
|
||||
_combobox.ComboBox.SetItems(options);
|
||||
|
||||
@@ -25,7 +25,6 @@ namespace FlaxEditor.Progress.Handlers
|
||||
ScriptsBuilder.ScriptsReloadCalled += () => OnUpdate(0.8f, "Reloading scripts...");
|
||||
ScriptsBuilder.ScriptsReloadBegin += OnScriptsReloadBegin;
|
||||
ScriptsBuilder.ScriptsReloadEnd += OnScriptsReloadEnd;
|
||||
ScriptsBuilder.ScriptsReload += OnScriptsReload;
|
||||
}
|
||||
|
||||
private void OnScriptsReloadBegin()
|
||||
@@ -38,14 +37,6 @@ namespace FlaxEditor.Progress.Handlers
|
||||
Editor.Instance.Scene.ClearRefsToSceneObjects(true);
|
||||
}
|
||||
|
||||
private void OnScriptsReload()
|
||||
{
|
||||
#if !USE_NETCORE
|
||||
// Clear types cache
|
||||
Newtonsoft.Json.JsonSerializer.ClearCache();
|
||||
#endif
|
||||
}
|
||||
|
||||
private void OnCompilationFailed()
|
||||
{
|
||||
OnFail("Scripts compilation failed");
|
||||
|
||||
@@ -207,9 +207,9 @@ void RiderCodeEditor::FindEditors(Array<CodeEditor*>* output)
|
||||
FileSystem::GetChildDirectories(subDirectories, TEXT("/opt/"));
|
||||
|
||||
// Versions installed via JetBrains Toolbox
|
||||
SearchDirectory(&installations, localAppDataPath / TEXT(".local/share/JetBrains/Toolbox/apps/rider/"));
|
||||
FileSystem::GetChildDirectories(subDirectories, localAppDataPath / TEXT(".local/share/JetBrains/Toolbox/apps/Rider/ch-0"));
|
||||
FileSystem::GetChildDirectories(subDirectories, localAppDataPath / TEXT(".local/share/JetBrains/Toolbox/apps/Rider/ch-1")); // Beta versions
|
||||
SearchDirectory(&installations, localAppDataPath / TEXT("JetBrains/Toolbox/apps/rider/"));
|
||||
FileSystem::GetChildDirectories(subDirectories, localAppDataPath / TEXT("JetBrains/Toolbox/apps/Rider/ch-0"));
|
||||
FileSystem::GetChildDirectories(subDirectories, localAppDataPath / TEXT("JetBrains/Toolbox/apps/Rider/ch-1")); // Beta versions
|
||||
|
||||
// Detect Flatpak installations
|
||||
SearchDirectory(&installations,
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
|
||||
|
||||
using FlaxEngine;
|
||||
using FlaxEditor.Utilities;
|
||||
using FlaxEngine.Utilities;
|
||||
|
||||
namespace FlaxEditor.States
|
||||
|
||||
@@ -882,6 +882,60 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
NodeElementArchetype.Factory.Output(1, "Inv Size", typeof(Float2), 1),
|
||||
}
|
||||
},
|
||||
new NodeArchetype
|
||||
{
|
||||
TypeID = 40,
|
||||
Title = "Rectangle Mask",
|
||||
Description = "Creates a rectangle mask",
|
||||
Flags = NodeFlags.MaterialGraph,
|
||||
Size = new Float2(150, 40),
|
||||
ConnectionsHints = ConnectionsHint.Vector,
|
||||
DefaultValues = new object[]
|
||||
{
|
||||
new Float2(0.5f, 0.5f),
|
||||
},
|
||||
Elements = new[]
|
||||
{
|
||||
NodeElementArchetype.Factory.Input(0, "UV", true, typeof(Float2), 0),
|
||||
NodeElementArchetype.Factory.Input(1, "Rectangle", true, typeof(Float2), 1, 0),
|
||||
NodeElementArchetype.Factory.Output(0, string.Empty, typeof(float), 2),
|
||||
}
|
||||
},
|
||||
new NodeArchetype
|
||||
{
|
||||
TypeID = 41,
|
||||
Title = "FWidth",
|
||||
Description = "Creates a partial derivative (fwidth)",
|
||||
Flags = NodeFlags.MaterialGraph,
|
||||
Size = new Float2(150, 20),
|
||||
ConnectionsHints = ConnectionsHint.Numeric,
|
||||
IndependentBoxes = new[] { 0 },
|
||||
DependentBoxes = new[] { 1 },
|
||||
Elements = new[]
|
||||
{
|
||||
NodeElementArchetype.Factory.Input(0, "Value", true, null, 0),
|
||||
NodeElementArchetype.Factory.Output(0, string.Empty, null, 1),
|
||||
}
|
||||
},
|
||||
new NodeArchetype
|
||||
{
|
||||
TypeID = 42,
|
||||
Title = "AA Step",
|
||||
Description = "Smooth version of step function with less aliasing",
|
||||
Flags = NodeFlags.MaterialGraph,
|
||||
Size = new Float2(150, 40),
|
||||
ConnectionsHints = ConnectionsHint.Vector,
|
||||
DefaultValues = new object[]
|
||||
{
|
||||
0.5f
|
||||
},
|
||||
Elements = new[]
|
||||
{
|
||||
NodeElementArchetype.Factory.Input(0, "Value", true, typeof(float), 0),
|
||||
NodeElementArchetype.Factory.Input(1, "Gradient", true, typeof(float), 1, 0),
|
||||
NodeElementArchetype.Factory.Output(0, string.Empty, typeof(float), 2),
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,21 +18,25 @@ namespace FlaxEngine.Tools
|
||||
/// <summary>
|
||||
/// Brush radius (world-space).
|
||||
/// </summary>
|
||||
[Limit(0)]
|
||||
public float BrushSize = 50.0f;
|
||||
|
||||
/// <summary>
|
||||
/// Brush paint intensity.
|
||||
/// </summary>
|
||||
[Limit(0)]
|
||||
public float BrushStrength = 2.0f;
|
||||
|
||||
/// <summary>
|
||||
/// Brush paint falloff. Hardens or softens painting.
|
||||
/// </summary>
|
||||
[Limit(0)]
|
||||
public float BrushFalloff = 1.5f;
|
||||
|
||||
/// <summary>
|
||||
/// Value to paint with. Hold Ctrl hey to paint with inverse value (1 - value).
|
||||
/// </summary>
|
||||
[Limit(0, 1, 0.01f)]
|
||||
public float PaintValue = 0.0f;
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -7,6 +7,8 @@ using FlaxEngine.GUI;
|
||||
using FlaxEditor.Viewport.Widgets;
|
||||
using FlaxEditor.GUI.ContextMenu;
|
||||
using Object = FlaxEngine.Object;
|
||||
using FlaxEditor.GUI;
|
||||
using FlaxEditor.Scripting;
|
||||
|
||||
namespace FlaxEditor.Viewport.Previews
|
||||
{
|
||||
@@ -49,6 +51,8 @@ namespace FlaxEditor.Viewport.Previews
|
||||
private Image _guiMaterialControl;
|
||||
private readonly MaterialBase[] _postFxMaterialsCache = new MaterialBase[1];
|
||||
private ContextMenu _modelWidgetButtonMenu;
|
||||
private AssetPicker _customModelPicker;
|
||||
private Model _customModel;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the material asset to preview. It can be <see cref="FlaxEngine.Material"/> or <see cref="FlaxEngine.MaterialInstance"/>.
|
||||
@@ -74,15 +78,66 @@ namespace FlaxEditor.Viewport.Previews
|
||||
get => _selectedModelIndex;
|
||||
set
|
||||
{
|
||||
if (value == -1) // Using Custom Model
|
||||
return;
|
||||
if (value < 0 || value > Models.Length)
|
||||
throw new ArgumentOutOfRangeException();
|
||||
|
||||
if (_customModelPicker != null)
|
||||
_customModelPicker.Validator.SelectedAsset = null;
|
||||
_selectedModelIndex = value;
|
||||
_previewModel.Model = FlaxEngine.Content.LoadAsyncInternal<Model>("Editor/Primitives/" + Models[value]);
|
||||
_previewModel.Transform = Transforms[value];
|
||||
}
|
||||
}
|
||||
|
||||
// Used to automatically update which entry is checked.
|
||||
// TODO: Maybe a better system with predicate bool checks could be used?
|
||||
private void ResetModelContextMenu()
|
||||
{
|
||||
_modelWidgetButtonMenu.ItemsContainer.DisposeChildren();
|
||||
|
||||
// Fill out all models
|
||||
for (int i = 0; i < Models.Length; i++)
|
||||
{
|
||||
var index = i;
|
||||
var button = _modelWidgetButtonMenu.AddButton(Models[index]);
|
||||
button.ButtonClicked += _ => SelectedModelIndex = index;
|
||||
button.Checked = SelectedModelIndex == index && _customModel == null;
|
||||
button.Tag = index;
|
||||
}
|
||||
|
||||
_modelWidgetButtonMenu.AddSeparator();
|
||||
_customModelPicker = new AssetPicker(new ScriptType(typeof(Model)), Float2.Zero);
|
||||
|
||||
// Label button
|
||||
var customModelPickerLabel = _modelWidgetButtonMenu.AddButton("Custom Model:");
|
||||
customModelPickerLabel.CloseMenuOnClick = false;
|
||||
customModelPickerLabel.Checked = _customModel != null;
|
||||
|
||||
// Container button
|
||||
var customModelPickerButton = _modelWidgetButtonMenu.AddButton("");
|
||||
customModelPickerButton.Height = _customModelPicker.Height + 4;
|
||||
customModelPickerButton.CloseMenuOnClick = false;
|
||||
_customModelPicker.Parent = customModelPickerButton;
|
||||
_customModelPicker.Validator.SelectedAsset = _customModel;
|
||||
_customModelPicker.SelectedItemChanged += () =>
|
||||
{
|
||||
_customModel = _customModelPicker.Validator.SelectedAsset as Model;
|
||||
if (_customModelPicker.Validator.SelectedAsset == null)
|
||||
{
|
||||
SelectedModelIndex = 0;
|
||||
ResetModelContextMenu();
|
||||
return;
|
||||
}
|
||||
|
||||
_previewModel.Model = _customModel;
|
||||
_previewModel.Transform = Transforms[0];
|
||||
SelectedModelIndex = -1;
|
||||
ResetModelContextMenu();
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="MaterialPreview"/> class.
|
||||
/// </summary>
|
||||
@@ -107,17 +162,7 @@ namespace FlaxEditor.Viewport.Previews
|
||||
{
|
||||
if (!control.Visible)
|
||||
return;
|
||||
_modelWidgetButtonMenu.ItemsContainer.DisposeChildren();
|
||||
|
||||
// Fill out all models
|
||||
for (int i = 0; i < Models.Length; i++)
|
||||
{
|
||||
var index = i;
|
||||
var button = _modelWidgetButtonMenu.AddButton(Models[index]);
|
||||
button.ButtonClicked += _ => SelectedModelIndex = index;
|
||||
button.Checked = SelectedModelIndex == index;
|
||||
button.Tag = index;
|
||||
}
|
||||
ResetModelContextMenu();
|
||||
};
|
||||
new ViewportWidgetButton("Model", SpriteHandle.Invalid, _modelWidgetButtonMenu)
|
||||
{
|
||||
|
||||
@@ -115,7 +115,7 @@ namespace FlaxEditor.Viewport.Widgets
|
||||
if (Icon.IsValid)
|
||||
{
|
||||
// Draw icon
|
||||
Render2D.DrawSprite(Icon, iconRect, style.Foreground);
|
||||
Render2D.DrawSprite(Icon, iconRect, style.ForegroundViewport);
|
||||
|
||||
// Update text rectangle
|
||||
textRect.Location.X += iconSize;
|
||||
@@ -123,7 +123,7 @@ namespace FlaxEditor.Viewport.Widgets
|
||||
}
|
||||
|
||||
// Draw text
|
||||
Render2D.DrawText(style.FontMedium, _text, textRect, style.Foreground * (IsMouseOver ? 1.0f : 0.9f), TextAlignment.Center, TextAlignment.Center);
|
||||
Render2D.DrawText(style.FontMedium, _text, textRect, style.ForegroundViewport * (IsMouseOver ? 1.0f : 0.9f), TextAlignment.Center, TextAlignment.Center);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -62,7 +62,9 @@ namespace FlaxEditor.Windows.Profiler
|
||||
_memoryUsageChart.SelectedSampleChanged += OnSelectedSampleChanged;
|
||||
|
||||
// Table
|
||||
var headerColor = Style.Current.LightBackground;
|
||||
var style = Style.Current;
|
||||
var headerColor = style.LightBackground;
|
||||
var textColor = style.Foreground;
|
||||
_table = new Table
|
||||
{
|
||||
Columns = new[]
|
||||
@@ -73,22 +75,26 @@ namespace FlaxEditor.Windows.Profiler
|
||||
CellAlignment = TextAlignment.Near,
|
||||
Title = "Resource",
|
||||
TitleBackgroundColor = headerColor,
|
||||
TitleColor = textColor,
|
||||
},
|
||||
new ColumnDefinition
|
||||
{
|
||||
Title = "Type",
|
||||
CellAlignment = TextAlignment.Center,
|
||||
TitleBackgroundColor = headerColor,
|
||||
TitleColor = textColor,
|
||||
},
|
||||
new ColumnDefinition
|
||||
{
|
||||
Title = "References",
|
||||
TitleBackgroundColor = headerColor,
|
||||
TitleColor = textColor,
|
||||
},
|
||||
new ColumnDefinition
|
||||
{
|
||||
Title = "Memory Usage",
|
||||
TitleBackgroundColor = headerColor,
|
||||
TitleColor = textColor,
|
||||
FormatValue = v => Utilities.Utils.FormatBytesCount((ulong)v),
|
||||
},
|
||||
},
|
||||
|
||||
@@ -92,7 +92,9 @@ namespace FlaxEditor.Windows.Profiler
|
||||
};
|
||||
|
||||
// Table
|
||||
var headerColor = Style.Current.LightBackground;
|
||||
var style = Style.Current;
|
||||
var headerColor = style.LightBackground;
|
||||
var textColor = style.Foreground;
|
||||
_table = new Table
|
||||
{
|
||||
Columns = new[]
|
||||
@@ -103,36 +105,42 @@ namespace FlaxEditor.Windows.Profiler
|
||||
CellAlignment = TextAlignment.Near,
|
||||
Title = "Event",
|
||||
TitleBackgroundColor = headerColor,
|
||||
TitleColor = textColor,
|
||||
},
|
||||
new ColumnDefinition
|
||||
{
|
||||
Title = "Total",
|
||||
TitleBackgroundColor = headerColor,
|
||||
FormatValue = FormatCellPercentage,
|
||||
TitleColor = textColor,
|
||||
},
|
||||
new ColumnDefinition
|
||||
{
|
||||
Title = "Self",
|
||||
TitleBackgroundColor = headerColor,
|
||||
FormatValue = FormatCellPercentage,
|
||||
TitleColor = textColor,
|
||||
},
|
||||
new ColumnDefinition
|
||||
{
|
||||
Title = "Time ms",
|
||||
TitleBackgroundColor = headerColor,
|
||||
FormatValue = FormatCellMs,
|
||||
TitleColor = textColor,
|
||||
},
|
||||
new ColumnDefinition
|
||||
{
|
||||
Title = "Self ms",
|
||||
TitleBackgroundColor = headerColor,
|
||||
FormatValue = FormatCellMs,
|
||||
TitleColor = textColor,
|
||||
},
|
||||
new ColumnDefinition
|
||||
{
|
||||
Title = "Memory",
|
||||
TitleBackgroundColor = headerColor,
|
||||
FormatValue = FormatCellBytes,
|
||||
TitleColor = textColor,
|
||||
},
|
||||
},
|
||||
Parent = layout,
|
||||
|
||||
@@ -63,7 +63,9 @@ namespace FlaxEditor.Windows.Profiler
|
||||
};
|
||||
|
||||
// Table
|
||||
var headerColor = Style.Current.LightBackground;
|
||||
var style = Style.Current;
|
||||
var headerColor = style.LightBackground;
|
||||
var textColor = style.Foreground;
|
||||
_table = new Table
|
||||
{
|
||||
Columns = new[]
|
||||
@@ -74,35 +76,41 @@ namespace FlaxEditor.Windows.Profiler
|
||||
CellAlignment = TextAlignment.Near,
|
||||
Title = "Event",
|
||||
TitleBackgroundColor = headerColor,
|
||||
TitleColor = textColor,
|
||||
},
|
||||
new ColumnDefinition
|
||||
{
|
||||
Title = "Total",
|
||||
TitleBackgroundColor = headerColor,
|
||||
TitleColor = textColor,
|
||||
FormatValue = (x) => ((float)x).ToString("0.0") + '%',
|
||||
},
|
||||
new ColumnDefinition
|
||||
{
|
||||
Title = "GPU ms",
|
||||
TitleBackgroundColor = headerColor,
|
||||
TitleColor = textColor,
|
||||
FormatValue = (x) => ((float)x).ToString("0.000"),
|
||||
},
|
||||
new ColumnDefinition
|
||||
{
|
||||
Title = "Draw Calls",
|
||||
TitleBackgroundColor = headerColor,
|
||||
TitleColor = textColor,
|
||||
FormatValue = FormatCountLong,
|
||||
},
|
||||
new ColumnDefinition
|
||||
{
|
||||
Title = "Triangles",
|
||||
TitleBackgroundColor = headerColor,
|
||||
TitleColor = textColor,
|
||||
FormatValue = FormatCountLong,
|
||||
},
|
||||
new ColumnDefinition
|
||||
{
|
||||
Title = "Vertices",
|
||||
TitleBackgroundColor = headerColor,
|
||||
TitleColor = textColor,
|
||||
FormatValue = FormatCountLong,
|
||||
},
|
||||
},
|
||||
|
||||
@@ -63,7 +63,9 @@ namespace FlaxEditor.Windows.Profiler
|
||||
_memoryUsageChart.SelectedSampleChanged += OnSelectedSampleChanged;
|
||||
|
||||
// Table
|
||||
var headerColor = Style.Current.LightBackground;
|
||||
var style = Style.Current;
|
||||
var headerColor = style.LightBackground;
|
||||
var textColor = style.Foreground;
|
||||
_table = new Table
|
||||
{
|
||||
Columns = new[]
|
||||
@@ -74,18 +76,21 @@ namespace FlaxEditor.Windows.Profiler
|
||||
CellAlignment = TextAlignment.Near,
|
||||
Title = "Resource",
|
||||
TitleBackgroundColor = headerColor,
|
||||
TitleColor = textColor,
|
||||
},
|
||||
new ColumnDefinition
|
||||
{
|
||||
Title = "Type",
|
||||
CellAlignment = TextAlignment.Center,
|
||||
TitleBackgroundColor = headerColor,
|
||||
TitleColor = textColor,
|
||||
},
|
||||
new ColumnDefinition
|
||||
{
|
||||
Title = "Memory Usage",
|
||||
TitleBackgroundColor = headerColor,
|
||||
FormatValue = v => Utilities.Utils.FormatBytesCount((ulong)v),
|
||||
TitleColor = textColor,
|
||||
},
|
||||
},
|
||||
Parent = layout,
|
||||
|
||||
@@ -252,7 +252,9 @@ namespace FlaxEditor.Windows.Profiler
|
||||
|
||||
private static Table InitTable(ContainerControl parent, string name)
|
||||
{
|
||||
var headerColor = Style.Current.LightBackground;
|
||||
var style = Style.Current;
|
||||
var headerColor = style.LightBackground;
|
||||
var textColor = style.Foreground;
|
||||
var table = new Table
|
||||
{
|
||||
Columns = new[]
|
||||
@@ -263,28 +265,33 @@ namespace FlaxEditor.Windows.Profiler
|
||||
CellAlignment = TextAlignment.Near,
|
||||
Title = name,
|
||||
TitleBackgroundColor = headerColor,
|
||||
TitleColor = textColor,
|
||||
},
|
||||
new ColumnDefinition
|
||||
{
|
||||
Title = "Count",
|
||||
TitleBackgroundColor = headerColor,
|
||||
TitleColor = textColor,
|
||||
},
|
||||
new ColumnDefinition
|
||||
{
|
||||
Title = "Data Size",
|
||||
TitleBackgroundColor = headerColor,
|
||||
TitleColor = textColor,
|
||||
FormatValue = FormatCellBytes,
|
||||
},
|
||||
new ColumnDefinition
|
||||
{
|
||||
Title = "Message Size",
|
||||
TitleBackgroundColor = headerColor,
|
||||
TitleColor = textColor,
|
||||
FormatValue = FormatCellBytes,
|
||||
},
|
||||
new ColumnDefinition
|
||||
{
|
||||
Title = "Receivers",
|
||||
TitleBackgroundColor = headerColor,
|
||||
TitleColor = textColor,
|
||||
},
|
||||
},
|
||||
Splits = new[]
|
||||
|
||||
@@ -105,7 +105,7 @@ namespace FlaxEditor.Windows.Profiler
|
||||
if (_selectedSampleIndex != -1)
|
||||
{
|
||||
float selectedX = Width - (_samples.Count - _selectedSampleIndex - 1) * PointsOffset;
|
||||
Render2D.DrawLine(new Float2(selectedX, 0), new Float2(selectedX, chartHeight), Color.White, 1.5f);
|
||||
Render2D.DrawLine(new Float2(selectedX, 0), new Float2(selectedX, chartHeight), style.Foreground, 1.5f);
|
||||
}
|
||||
|
||||
int samplesInViewCount = Math.Min((int)(Width / PointsOffset), _samples.Count) - 1;
|
||||
@@ -138,8 +138,8 @@ namespace FlaxEditor.Windows.Profiler
|
||||
var headerRect = new Rectangle(0, chartHeight, Width, TitleHeight);
|
||||
var headerTextRect = new Rectangle(2, chartHeight, Width - 4, TitleHeight);
|
||||
Render2D.FillRectangle(headerRect, style.BackgroundNormal);
|
||||
Render2D.DrawText(style.FontMedium, Title, headerTextRect, Color.White * 0.8f, TextAlignment.Near, TextAlignment.Center);
|
||||
Render2D.DrawText(style.FontMedium, _sample, headerTextRect, Color.White, TextAlignment.Far, TextAlignment.Center);
|
||||
Render2D.DrawText(style.FontMedium, Title, headerTextRect, style.ForegroundGrey, TextAlignment.Near, TextAlignment.Center);
|
||||
Render2D.DrawText(style.FontMedium, _sample, headerTextRect, style.Foreground, TextAlignment.Far, TextAlignment.Center);
|
||||
}
|
||||
|
||||
private void OnClick(ref Float2 location)
|
||||
|
||||
@@ -90,7 +90,7 @@ namespace FlaxEditor.Windows.Profiler
|
||||
if (_nameLength < bounds.Width + 4)
|
||||
{
|
||||
Render2D.PushClip(bounds);
|
||||
Render2D.DrawText(style.FontMedium, _name, bounds, Color.White, TextAlignment.Center, TextAlignment.Center);
|
||||
Render2D.DrawText(style.FontMedium, _name, bounds, Style.Current.Foreground, TextAlignment.Center, TextAlignment.Center);
|
||||
Render2D.PopClip();
|
||||
}
|
||||
}
|
||||
@@ -115,7 +115,7 @@ namespace FlaxEditor.Windows.Profiler
|
||||
var style = Style.Current;
|
||||
var rect = new Rectangle(Float2.Zero, Size);
|
||||
Render2D.PushClip(rect);
|
||||
Render2D.DrawText(style.FontMedium, Name, rect, Color.White, TextAlignment.Center, TextAlignment.Center, TextWrapping.WrapChars);
|
||||
Render2D.DrawText(style.FontMedium, Name, rect, Style.Current.Foreground, TextAlignment.Center, TextAlignment.Center, TextWrapping.WrapChars);
|
||||
Render2D.PopClip();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -150,7 +150,13 @@ void BehaviorKnowledge::InitMemory(BehaviorTree* tree)
|
||||
RelevantNodes.Resize(tree->Graph.NodesCount, false);
|
||||
RelevantNodes.SetAll(false);
|
||||
if (!Memory && tree->Graph.NodesStatesSize)
|
||||
{
|
||||
Memory = Allocator::Allocate(tree->Graph.NodesStatesSize);
|
||||
#if !BUILD_RELEASE
|
||||
// Clear memory to make it easier to spot missing data issues (eg. zero GCHandle in C# BT node due to missing state init)
|
||||
Platform::MemoryClear(Memory, tree->Graph.NodesStatesSize);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void BehaviorKnowledge::FreeMemory()
|
||||
|
||||
@@ -202,7 +202,7 @@ namespace FlaxEngine
|
||||
public T Get(BehaviorKnowledge knowledge)
|
||||
{
|
||||
if (knowledge != null && knowledge.Get(Path, out var value))
|
||||
return (T)value;
|
||||
return Utilities.VariantUtils.Cast<T>(value);
|
||||
return default;
|
||||
}
|
||||
|
||||
@@ -218,7 +218,7 @@ namespace FlaxEngine
|
||||
object tmp = null;
|
||||
bool result = knowledge != null && knowledge.Get(Path, out tmp);
|
||||
if (result)
|
||||
value = (T)tmp;
|
||||
value = Utilities.VariantUtils.Cast<T>(tmp);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -95,12 +95,16 @@ namespace FlaxEngine
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public ref T GetState<T>(IntPtr memory) where T : struct
|
||||
{
|
||||
var ptr = IntPtr.Add(memory, _memoryOffset).ToPointer();
|
||||
var handle = GCHandle.FromIntPtr(Unsafe.Read<IntPtr>(ptr));
|
||||
var ptr = Unsafe.Read<IntPtr>(IntPtr.Add(memory, _memoryOffset).ToPointer());
|
||||
#if !BUILD_RELEASE
|
||||
if (ptr == IntPtr.Zero)
|
||||
throw new Exception($"Missing state '{typeof(T).FullName}' for node '{GetType().FullName}'");
|
||||
#endif
|
||||
var handle = GCHandle.FromIntPtr(ptr);
|
||||
var state = handle.Target;
|
||||
#if !BUILD_RELEASE
|
||||
if (state == null)
|
||||
throw new NullReferenceException();
|
||||
throw new Exception($"Missing state '{typeof(T).FullName}' for node '{GetType().FullName}'");
|
||||
#endif
|
||||
return ref Unsafe.Unbox<T>(state);
|
||||
}
|
||||
@@ -111,8 +115,10 @@ namespace FlaxEngine
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void FreeState(IntPtr memory)
|
||||
{
|
||||
var ptr = IntPtr.Add(memory, _memoryOffset).ToPointer();
|
||||
var handle = GCHandle.FromIntPtr(Unsafe.Read<IntPtr>(ptr));
|
||||
var ptr = Unsafe.Read<IntPtr>(IntPtr.Add(memory, _memoryOffset).ToPointer());
|
||||
if (ptr == IntPtr.Zero)
|
||||
return;
|
||||
var handle = GCHandle.FromIntPtr(ptr);
|
||||
handle.Free();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,6 +85,8 @@ BehaviorUpdateResult BehaviorTreeNode::InvokeUpdate(const BehaviorUpdateContext&
|
||||
result = BehaviorUpdateResult::Failed;
|
||||
else
|
||||
result = Update(context);
|
||||
if ((int32)result < 0 || (int32)result > (int32)BehaviorUpdateResult::Failed)
|
||||
result = BehaviorUpdateResult::Failed; // Invalid value is a failure
|
||||
|
||||
// Post-process result from decorators
|
||||
for (BehaviorTreeDecorator* decorator : _decorators)
|
||||
|
||||
@@ -225,6 +225,7 @@ bool AudioClip::ExtractDataRaw(Array<byte>& resultData, AudioDataInfo& resultDat
|
||||
|
||||
void AudioClip::CancelStreaming()
|
||||
{
|
||||
Asset::CancelStreaming();
|
||||
CancelStreamingTasks();
|
||||
}
|
||||
|
||||
|
||||
@@ -522,6 +522,14 @@ void Asset::InitAsVirtual()
|
||||
|
||||
void Asset::CancelStreaming()
|
||||
{
|
||||
// Cancel loading task but go over asset locker to prevent case if other load threads still loads asset while it's reimported on other thread
|
||||
Locker.Lock();
|
||||
ContentLoadTask* loadTask = _loadingTask;
|
||||
Locker.Unlock();
|
||||
if (loadTask)
|
||||
{
|
||||
loadTask->Cancel();
|
||||
}
|
||||
}
|
||||
|
||||
#if USE_EDITOR
|
||||
|
||||
@@ -783,6 +783,7 @@ void Model::InitAsVirtual()
|
||||
|
||||
void Model::CancelStreaming()
|
||||
{
|
||||
Asset::CancelStreaming();
|
||||
CancelStreamingTasks();
|
||||
}
|
||||
|
||||
|
||||
@@ -969,6 +969,7 @@ void SkinnedModel::InitAsVirtual()
|
||||
|
||||
void SkinnedModel::CancelStreaming()
|
||||
{
|
||||
Asset::CancelStreaming();
|
||||
CancelStreamingTasks();
|
||||
}
|
||||
|
||||
|
||||
@@ -48,6 +48,8 @@ protected:
|
||||
// [ContentLoadTask]
|
||||
Result run() override
|
||||
{
|
||||
if (IsCancelRequested())
|
||||
return Result::Ok;
|
||||
PROFILE_CPU();
|
||||
|
||||
AssetReference<BinaryAsset> ref = _asset.Get();
|
||||
@@ -67,8 +69,6 @@ protected:
|
||||
{
|
||||
if (IsCancelRequested())
|
||||
return Result::Ok;
|
||||
|
||||
// Load it
|
||||
#if TRACY_ENABLE
|
||||
ZoneScoped;
|
||||
ZoneName(*name, name.Length());
|
||||
|
||||
@@ -1302,15 +1302,15 @@ void FlaxStorage::CloseFileHandles()
|
||||
// In those situations all the async tasks using this storage should be cancelled externally
|
||||
|
||||
// Ensure that no one is using this resource
|
||||
int32 waitTime = 10;
|
||||
int32 waitTime = 100;
|
||||
while (Platform::AtomicRead(&_chunksLock) != 0 && waitTime-- > 0)
|
||||
Platform::Sleep(10);
|
||||
Platform::Sleep(1);
|
||||
if (Platform::AtomicRead(&_chunksLock) != 0)
|
||||
{
|
||||
// File can be locked by some streaming tasks (eg. AudioClip::StreamingTask or StreamModelLODTask)
|
||||
Entry e;
|
||||
for (int32 i = 0; i < GetEntriesCount(); i++)
|
||||
{
|
||||
Entry e;
|
||||
GetEntry(i, e);
|
||||
Asset* asset = Content::GetAsset(e.ID);
|
||||
if (asset)
|
||||
@@ -1320,8 +1320,12 @@ void FlaxStorage::CloseFileHandles()
|
||||
}
|
||||
}
|
||||
}
|
||||
waitTime = 100;
|
||||
while (Platform::AtomicRead(&_chunksLock) != 0 && waitTime-- > 0)
|
||||
Platform::Sleep(1);
|
||||
ASSERT(_chunksLock == 0);
|
||||
|
||||
// Close file handles (from all threads)
|
||||
_file.DeleteAll();
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "Engine/Core/Config/Settings.h"
|
||||
#include "Engine/Serialization/Serialization.h"
|
||||
#include "Engine/Serialization/SerializationFwd.h"
|
||||
|
||||
/// <summary>
|
||||
/// Specifies the display mode of a game window.
|
||||
|
||||
@@ -2821,7 +2821,10 @@ void Variant::Inline()
|
||||
type = VariantType::Types::Vector4;
|
||||
}
|
||||
if (type != VariantType::Null)
|
||||
{
|
||||
ASSERT(sizeof(data) >= AsBlob.Length);
|
||||
Platform::MemoryCopy(data, AsBlob.Data, AsBlob.Length);
|
||||
}
|
||||
}
|
||||
if (type != VariantType::Null)
|
||||
{
|
||||
@@ -2912,6 +2915,60 @@ void Variant::Inline()
|
||||
}
|
||||
}
|
||||
|
||||
void Variant::InvertInline()
|
||||
{
|
||||
byte data[sizeof(Matrix)];
|
||||
switch (Type.Type)
|
||||
{
|
||||
case VariantType::Bool:
|
||||
case VariantType::Int:
|
||||
case VariantType::Uint:
|
||||
case VariantType::Int64:
|
||||
case VariantType::Uint64:
|
||||
case VariantType::Float:
|
||||
case VariantType::Double:
|
||||
case VariantType::Pointer:
|
||||
case VariantType::String:
|
||||
case VariantType::Float2:
|
||||
case VariantType::Float3:
|
||||
case VariantType::Float4:
|
||||
case VariantType::Color:
|
||||
#if !USE_LARGE_WORLDS
|
||||
case VariantType::BoundingSphere:
|
||||
case VariantType::BoundingBox:
|
||||
case VariantType::Ray:
|
||||
#endif
|
||||
case VariantType::Guid:
|
||||
case VariantType::Quaternion:
|
||||
case VariantType::Rectangle:
|
||||
case VariantType::Int2:
|
||||
case VariantType::Int3:
|
||||
case VariantType::Int4:
|
||||
case VariantType::Int16:
|
||||
case VariantType::Uint16:
|
||||
case VariantType::Double2:
|
||||
case VariantType::Double3:
|
||||
case VariantType::Double4:
|
||||
static_assert(sizeof(data) >= sizeof(AsData), "Invalid memory size.");
|
||||
Platform::MemoryCopy(data, AsData, sizeof(AsData));
|
||||
break;
|
||||
#if USE_LARGE_WORLDS
|
||||
case VariantType::BoundingSphere:
|
||||
case VariantType::BoundingBox:
|
||||
case VariantType::Ray:
|
||||
#endif
|
||||
case VariantType::Transform:
|
||||
case VariantType::Matrix:
|
||||
ASSERT(sizeof(data) >= AsBlob.Length);
|
||||
Platform::MemoryCopy(data, AsBlob.Data, AsBlob.Length);
|
||||
break;
|
||||
default:
|
||||
return; // Not used
|
||||
}
|
||||
SetType(VariantType(VariantType::Structure, InBuiltTypesTypeNames[Type.Type]));
|
||||
CopyStructure(data);
|
||||
}
|
||||
|
||||
Variant Variant::NewValue(const StringAnsiView& typeName)
|
||||
{
|
||||
Variant v;
|
||||
|
||||
@@ -372,6 +372,9 @@ public:
|
||||
// Inlines potential value type into in-built format (eg. Vector3 stored as Structure, or String stored as ManagedObject).
|
||||
void Inline();
|
||||
|
||||
// Inverts the inlined value from in-built format into generic storage (eg. Float3 from inlined format into Structure).
|
||||
void InvertInline();
|
||||
|
||||
// Allocates the Variant of the specific type (eg. structure or object or value).
|
||||
static Variant NewValue(const StringAnsiView& typeName);
|
||||
|
||||
|
||||
@@ -1022,6 +1022,8 @@ namespace FlaxEngine.Interop
|
||||
pair.Value.Free();
|
||||
classAttributesCacheCollectible.Clear();
|
||||
|
||||
FlaxEngine.Json.JsonSerializer.ResetCache();
|
||||
|
||||
// Unload the ALC
|
||||
bool unloading = true;
|
||||
scriptingAssemblyLoadContext.Unloading += (alc) => { unloading = false; };
|
||||
|
||||
@@ -278,44 +278,70 @@ namespace FlaxEngine.Interop
|
||||
if (typeCache.TryGetValue(typeName, out Type type))
|
||||
return type;
|
||||
|
||||
type = Type.GetType(typeName, ResolveAssemblyByName, null);
|
||||
type = Type.GetType(typeName, ResolveAssembly, null);
|
||||
if (type == null)
|
||||
{
|
||||
foreach (var assembly in scriptingAssemblyLoadContext.Assemblies)
|
||||
{
|
||||
type = assembly.GetType(typeName);
|
||||
if (type != null)
|
||||
break;
|
||||
}
|
||||
}
|
||||
type = ResolveSlow(typeName);
|
||||
|
||||
if (type == null)
|
||||
{
|
||||
string oldTypeName = typeName;
|
||||
string fullTypeName = typeName;
|
||||
typeName = typeName.Substring(0, typeName.IndexOf(','));
|
||||
type = Type.GetType(typeName, ResolveAssemblyByName, null);
|
||||
type = Type.GetType(typeName, ResolveAssembly, null);
|
||||
if (type == null)
|
||||
{
|
||||
foreach (var assembly in scriptingAssemblyLoadContext.Assemblies)
|
||||
{
|
||||
type = assembly.GetType(typeName);
|
||||
if (type != null)
|
||||
break;
|
||||
}
|
||||
}
|
||||
typeName = oldTypeName;
|
||||
type = ResolveSlow(typeName);
|
||||
|
||||
typeName = fullTypeName;
|
||||
}
|
||||
|
||||
typeCache.Add(typeName, type);
|
||||
|
||||
return type;
|
||||
|
||||
static Type ResolveSlow(string typeName)
|
||||
{
|
||||
foreach (var assembly in scriptingAssemblyLoadContext.Assemblies)
|
||||
{
|
||||
var type = assembly.GetType(typeName);
|
||||
if (type != null)
|
||||
return type;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static Assembly ResolveAssembly(AssemblyName name) => ResolveScriptingAssemblyByName(name, allowPartial: false);
|
||||
}
|
||||
|
||||
private static Assembly ResolveAssemblyByName(AssemblyName assemblyName)
|
||||
/// <summary>Find <paramref name="assemblyName"/> among the scripting assemblies.</summary>
|
||||
/// <param name="assemblyName">The name to find</param>
|
||||
/// <param name="allowPartial">If true, partial names should be allowed to be resolved.</param>
|
||||
/// <returns>The resolved assembly, or null if none could be found.</returns>
|
||||
internal static Assembly ResolveScriptingAssemblyByName(AssemblyName assemblyName, bool allowPartial = false)
|
||||
{
|
||||
foreach (Assembly assembly in scriptingAssemblyLoadContext.Assemblies)
|
||||
if (assembly.GetName() == assemblyName)
|
||||
var lc = scriptingAssemblyLoadContext;
|
||||
|
||||
if (lc is null)
|
||||
return null;
|
||||
|
||||
foreach (Assembly assembly in lc.Assemblies)
|
||||
{
|
||||
var curName = assembly.GetName();
|
||||
|
||||
if (curName == assemblyName)
|
||||
return assembly;
|
||||
}
|
||||
|
||||
if (allowPartial) // Check partial names if full name isn't found
|
||||
{
|
||||
string partialName = assemblyName.Name;
|
||||
|
||||
foreach (Assembly assembly in lc.Assemblies)
|
||||
{
|
||||
var curName = assembly.GetName();
|
||||
|
||||
if (curName.Name == partialName)
|
||||
return assembly;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "../Enums.h"
|
||||
#include "Engine/Core/Math/Math.h"
|
||||
|
||||
/// <summary>
|
||||
/// Material domain type. Material domain defines the target usage of the material shader.
|
||||
@@ -86,7 +85,7 @@ API_ENUM() enum class MaterialBlendMode : byte
|
||||
API_ENUM() enum class MaterialShadingModel : byte
|
||||
{
|
||||
/// <summary>
|
||||
/// The unlit material. Emissive channel is used as an output color. Can perform custom lighting operations or just glow. Won't be affected by the lighting pipeline.
|
||||
/// The unlit material. The emissive channel is used as an output color. Can perform custom lighting operations or just glow. Won't be affected by the lighting pipeline.
|
||||
/// </summary>
|
||||
Unlit = 0,
|
||||
|
||||
@@ -96,7 +95,7 @@ API_ENUM() enum class MaterialShadingModel : byte
|
||||
Lit = 1,
|
||||
|
||||
/// <summary>
|
||||
/// The subsurface material. Intended for materials like vax or skin that need light scattering to transport simulation through the object.
|
||||
/// The subsurface material. Intended for materials like wax or skin that need light scattering to transport simulation through the object.
|
||||
/// </summary>
|
||||
Subsurface = 2,
|
||||
|
||||
@@ -366,12 +365,12 @@ API_ENUM() enum class MaterialDecalBlendingMode : byte
|
||||
API_ENUM() enum class MaterialTransparentLightingMode : byte
|
||||
{
|
||||
/// <summary>
|
||||
/// Default directional lighting evaluated per-pixel at the material surface. Use it for semi-transparent surfaces - with both diffuse and specular lighting component active.
|
||||
/// Default directional lighting evaluated per-pixel at the material surface. Use it for semi-transparent surfaces - with both diffuse and specular lighting components active.
|
||||
/// </summary>
|
||||
Surface = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Non-directional lighting evaluated per-pixel at material surface. Use it for volumetric objects such as smoke, rain or dust - only diffuse lighting term is active (no specular highlights).
|
||||
/// Non-directional lighting evaluated per-pixel at material surface. Use it for volumetric objects such as smoke, rain or dust - only the diffuse lighting term is active (no specular highlights).
|
||||
/// </summary>
|
||||
SurfaceNonDirectional = 1,
|
||||
};
|
||||
|
||||
@@ -660,6 +660,7 @@ uint64 TextureBase::GetMemoryUsage() const
|
||||
|
||||
void TextureBase::CancelStreaming()
|
||||
{
|
||||
Asset::CancelStreaming();
|
||||
_texture.CancelStreamingTasks();
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include "Engine/Graphics/GPUDevice.h"
|
||||
#include "Engine/Graphics/GPUResource.h"
|
||||
#include "Engine/Core/Collections/Dictionary.h"
|
||||
#include "../GPUDeviceDX.h"
|
||||
#include "../IncludeDirectXHeaders.h"
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "Engine/Graphics/GPUPipelineState.h"
|
||||
#include "GPUDeviceDX12.h"
|
||||
#include "Types.h"
|
||||
#include "Engine/Core/Collections/Dictionary.h"
|
||||
#include "../IncludeDirectXHeaders.h"
|
||||
|
||||
class GPUTextureViewDX12;
|
||||
|
||||
@@ -13,7 +13,7 @@ class FLAXENGINE_API DirectionalLight : public LightWithShadow
|
||||
DECLARE_SCENE_OBJECT(DirectionalLight);
|
||||
public:
|
||||
/// <summary>
|
||||
/// The number of cascades used for slicing the range of depth covered by the light. Values are 1, 2 or 4 cascades; a typical scene uses 4 cascades.
|
||||
/// The number of cascades used for slicing the range of depth covered by the light during shadow rendering. Values are 1, 2 or 4 cascades; a typical scene uses 4 cascades.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="EditorOrder(65), DefaultValue(4), Limit(1, 4), EditorDisplay(\"Shadow\")")
|
||||
int32 CascadeCount = 4;
|
||||
|
||||
@@ -29,7 +29,7 @@ public:
|
||||
float Brightness = 3.14f;
|
||||
|
||||
/// <summary>
|
||||
/// Controls light visibility range. The distance at which the light be completely faded. Use value 0 to always draw light.
|
||||
/// Controls light visibility range. The distance at which the light becomes completely faded. Use a value of 0 to always draw light.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="EditorOrder(35), Limit(0, float.MaxValue, 10.0f), EditorDisplay(\"Light\")")
|
||||
float ViewDistance = 0.0f;
|
||||
@@ -87,19 +87,19 @@ public:
|
||||
float MinRoughness = 0.04f;
|
||||
|
||||
/// <summary>
|
||||
/// The light shadows casting distance from view.
|
||||
/// Shadows casting distance from view.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="EditorOrder(80), EditorDisplay(\"Shadow\", \"Distance\"), Limit(0, 1000000)")
|
||||
float ShadowsDistance = 5000.0f;
|
||||
|
||||
/// <summary>
|
||||
/// The light shadows fade off distance
|
||||
/// Shadows fade off distance.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="EditorOrder(90), EditorDisplay(\"Shadow\", \"Fade Distance\"), Limit(0.0f, 10000.0f, 0.1f)")
|
||||
float ShadowsFadeDistance = 500.0f;
|
||||
|
||||
/// <summary>
|
||||
/// The light shadows edges sharpness
|
||||
/// TheShadows edges sharpness.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="EditorOrder(70), EditorDisplay(\"Shadow\", \"Sharpness\"), Limit(1.0f, 10.0f, 0.001f)")
|
||||
float ShadowsSharpness = 1.0f;
|
||||
@@ -129,7 +129,7 @@ public:
|
||||
float ContactShadowsLength = 0.0f;
|
||||
|
||||
/// <summary>
|
||||
/// Shadows casting mode by this visual element
|
||||
/// Describes how a visual element casts shadows.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="EditorOrder(60), EditorDisplay(\"Shadow\", \"Mode\")")
|
||||
ShadowsCastingMode ShadowsMode = ShadowsCastingMode::All;
|
||||
|
||||
@@ -149,9 +149,14 @@ float Spline::GetSplineDuration() const
|
||||
float Spline::GetSplineLength() const
|
||||
{
|
||||
float sum = 0.0f;
|
||||
const int32 slices = 20;
|
||||
const float step = 1.0f / (float)slices;
|
||||
constexpr int32 slices = 20;
|
||||
constexpr float step = 1.0f / (float)slices;
|
||||
Vector3 prevPoint = Vector3::Zero;
|
||||
if (Curve.GetKeyframes().Count() != 0)
|
||||
{
|
||||
const auto& a = Curve[0];
|
||||
prevPoint = a.Value.Translation * _transform.Scale;
|
||||
}
|
||||
for (int32 i = 1; i < Curve.GetKeyframes().Count(); i++)
|
||||
{
|
||||
const auto& a = Curve[i - 1];
|
||||
@@ -176,6 +181,37 @@ float Spline::GetSplineLength() const
|
||||
return Math::Sqrt(sum);
|
||||
}
|
||||
|
||||
float Spline::GetSplineSegmentLength(int32 index) const
|
||||
{
|
||||
if (index == 0)
|
||||
return 0.0f;
|
||||
CHECK_RETURN(index > 0 && index < GetSplinePointsCount(), 0.0f);
|
||||
float sum = 0.0f;
|
||||
constexpr int32 slices = 20;
|
||||
constexpr float step = 1.0f / (float)slices;
|
||||
const auto& a = Curve[index - 1];
|
||||
const auto& b = Curve[index];
|
||||
Vector3 startPoint = a.Value.Translation * _transform.Scale;
|
||||
{
|
||||
const float length = Math::Abs(b.Time - a.Time);
|
||||
Vector3 leftTangent, rightTangent;
|
||||
AnimationUtils::GetTangent(a.Value.Translation, a.TangentOut.Translation, length, leftTangent);
|
||||
AnimationUtils::GetTangent(b.Value.Translation, b.TangentIn.Translation, length, rightTangent);
|
||||
|
||||
// TODO: implement sth more analytical than brute-force solution
|
||||
for (int32 slice = 0; slice < slices; slice++)
|
||||
{
|
||||
const float t = (float)slice * step;
|
||||
Vector3 pos;
|
||||
AnimationUtils::Bezier(a.Value.Translation, leftTangent, rightTangent, b.Value.Translation, t, pos);
|
||||
pos *= _transform.Scale;
|
||||
sum += (float)Vector3::DistanceSquared(pos, startPoint);
|
||||
startPoint = pos;
|
||||
}
|
||||
}
|
||||
return Math::Sqrt(sum);
|
||||
}
|
||||
|
||||
float Spline::GetSplineTime(int32 index) const
|
||||
{
|
||||
CHECK_RETURN(index >= 0 && index < GetSplinePointsCount(), 0.0f)
|
||||
|
||||
@@ -167,6 +167,13 @@ public:
|
||||
/// </summary>
|
||||
API_PROPERTY() float GetSplineLength() const;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the length of the spline segment (distance between pair of two points).
|
||||
/// </summary>
|
||||
/// <param name="index">The index of the segment end index. Zero-based, smaller than GetSplinePointsCount().</param>
|
||||
/// <returns>The spline segment length.</returns>
|
||||
API_FUNCTION() float GetSplineSegmentLength(int32 index) const;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the time of the spline keyframe.
|
||||
/// </summary>
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
#include "Engine/Content/JsonAsset.h"
|
||||
#include "Engine/Threading/Threading.h"
|
||||
#if USE_EDITOR
|
||||
#include "Editor/Editor.h"
|
||||
#include "Editor/Managed/ManagedEditor.h"
|
||||
#include "Engine/Level/Level.h"
|
||||
#include "Engine/Level/Scene/Scene.h"
|
||||
#endif
|
||||
@@ -220,6 +222,17 @@ void NavigationSettings::Apply()
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#if USE_EDITOR
|
||||
if (!Editor::IsPlayMode && Editor::Managed && Editor::Managed->CanAutoBuildNavMesh())
|
||||
{
|
||||
// Rebuild all navmeshs after apply changes on navigation
|
||||
for (auto scene : Level::Scenes)
|
||||
{
|
||||
Navigation::BuildNavMesh(scene);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void NavigationSettings::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier)
|
||||
|
||||
@@ -301,11 +301,8 @@ void NetworkTransform::Deserialize(NetworkStream* stream)
|
||||
_buffer.Clear();
|
||||
_bufferHasDeltas = true;
|
||||
}
|
||||
// TODO: items are added in order to do batch removal
|
||||
for (int32 i = 0; i < _buffer.Count() && _buffer[i].SequenceIndex < sequenceIndex; i++)
|
||||
{
|
||||
_buffer.RemoveAtKeepOrder(i);
|
||||
}
|
||||
while (_buffer.Count() != 0 && _buffer[0].SequenceIndex < sequenceIndex)
|
||||
_buffer.RemoveAtKeepOrder(0);
|
||||
|
||||
// Use received authoritative actor transformation but re-apply all deltas not yet processed by the server due to lag (reconciliation)
|
||||
for (auto& e : _buffer)
|
||||
|
||||
@@ -35,7 +35,11 @@ void RigidBody::SetIsKinematic(const bool value)
|
||||
return;
|
||||
_isKinematic = value;
|
||||
if (_actor)
|
||||
{
|
||||
PhysicsBackend::SetRigidDynamicActorFlag(_actor, PhysicsBackend::RigidDynamicFlags::Kinematic, value);
|
||||
if (!value && _isActive && _startAwake)
|
||||
WakeUp();
|
||||
}
|
||||
}
|
||||
|
||||
void RigidBody::SetLinearDamping(float value)
|
||||
|
||||
@@ -137,7 +137,11 @@ void SimulationEventCallback::onContact(const PxContactPairHeader& pairHeader, c
|
||||
|
||||
c.ThisActor = static_cast<PhysicsColliderActor*>(pair.shapes[0]->userData);
|
||||
c.OtherActor = static_cast<PhysicsColliderActor*>(pair.shapes[1]->userData);
|
||||
ASSERT_LOW_LAYER(c.ThisActor && c.OtherActor);
|
||||
if (c.ThisActor == nullptr || c.OtherActor == nullptr)
|
||||
{
|
||||
// One of the actors was deleted (eg. via RigidBody destroyed by gameplay) then skip processing this collision
|
||||
continue;
|
||||
}
|
||||
|
||||
// Extract contact points
|
||||
while (i.hasNextPatch())
|
||||
|
||||
@@ -14,8 +14,8 @@ class Texture;
|
||||
/// </summary>
|
||||
API_CLASS(sealed, Namespace="FlaxEditor.Content.Settings") class FLAXENGINE_API AndroidPlatformSettings : public SettingsBase
|
||||
{
|
||||
DECLARE_SCRIPTING_TYPE_MINIMAL(AndroidPlatformSettings);
|
||||
public:
|
||||
DECLARE_SCRIPTING_TYPE_MINIMAL(AndroidPlatformSettings);
|
||||
API_AUTO_SERIALIZATION();
|
||||
|
||||
/// <summary>
|
||||
/// The application package name (eg. com.company.product). Custom tokens: ${PROJECT_NAME}, ${COMPANY_NAME}.
|
||||
@@ -35,20 +35,10 @@ public:
|
||||
API_FIELD(Attributes="EditorOrder(1030), EditorDisplay(\"Other\")")
|
||||
SoftObjectReference<Texture> OverrideIcon;
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Gets the instance of the settings asset (default value if missing). Object returned by this method is always loaded with valid data to use.
|
||||
/// </summary>
|
||||
static AndroidPlatformSettings* Get();
|
||||
|
||||
// [SettingsBase]
|
||||
void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) final override
|
||||
{
|
||||
DESERIALIZE(PackageName);
|
||||
DESERIALIZE(Permissions);
|
||||
DESERIALIZE(OverrideIcon);
|
||||
}
|
||||
};
|
||||
|
||||
#if PLATFORM_ANDROID
|
||||
|
||||
@@ -16,6 +16,7 @@ class Texture;
|
||||
API_CLASS(Abstract, Namespace="FlaxEditor.Content.Settings") class FLAXENGINE_API ApplePlatformSettings : public SettingsBase
|
||||
{
|
||||
DECLARE_SCRIPTING_TYPE_MINIMAL(ApplePlatformSettings);
|
||||
API_AUTO_SERIALIZATION();
|
||||
|
||||
/// <summary>
|
||||
/// The app identifier (reversed DNS, eg. com.company.product). Custom tokens: ${PROJECT_NAME}, ${COMPANY_NAME}.
|
||||
@@ -28,14 +29,6 @@ API_CLASS(Abstract, Namespace="FlaxEditor.Content.Settings") class FLAXENGINE_AP
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="EditorOrder(1000), EditorDisplay(\"Other\")")
|
||||
SoftObjectReference<Texture> OverrideIcon;
|
||||
|
||||
public:
|
||||
// [SettingsBase]
|
||||
void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) override
|
||||
{
|
||||
DESERIALIZE(AppIdentifier);
|
||||
DESERIALIZE(OverrideIcon);
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -16,7 +16,8 @@ class Texture;
|
||||
API_CLASS(Namespace="FlaxEditor.Content.Settings") class FLAXENGINE_API GDKPlatformSettings : public SettingsBase
|
||||
{
|
||||
DECLARE_SCRIPTING_TYPE_MINIMAL(GDKPlatformSettings);
|
||||
public:
|
||||
API_AUTO_SERIALIZATION();
|
||||
|
||||
/// <summary>
|
||||
/// Game identity name stored in game package manifest (for store). If empty the product name will be used from Game Settings.
|
||||
/// </summary>
|
||||
@@ -118,28 +119,6 @@ public:
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="EditorOrder(420), EditorDisplay(\"Media Capture\")")
|
||||
bool BlockGameDVR = false;
|
||||
|
||||
public:
|
||||
// [SettingsBase]
|
||||
void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) override
|
||||
{
|
||||
DESERIALIZE(Name);
|
||||
DESERIALIZE(PublisherName);
|
||||
DESERIALIZE(PublisherDisplayName);
|
||||
DESERIALIZE(Square150x150Logo);
|
||||
DESERIALIZE(Square480x480Logo);
|
||||
DESERIALIZE(Square44x44Logo);
|
||||
DESERIALIZE(SplashScreenImage);
|
||||
DESERIALIZE(StoreLogo);
|
||||
DESERIALIZE(BackgroundColor);
|
||||
DESERIALIZE(TitleId);
|
||||
DESERIALIZE(StoreId);
|
||||
DESERIALIZE(RequiresXboxLive);
|
||||
DESERIALIZE(SCID);
|
||||
DESERIALIZE(GameDVRSystemComponent);
|
||||
DESERIALIZE(BlockBroadcast);
|
||||
DESERIALIZE(BlockGameDVR);
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -14,8 +14,8 @@ class Texture;
|
||||
/// </summary>
|
||||
API_CLASS(sealed, Namespace="FlaxEditor.Content.Settings") class FLAXENGINE_API LinuxPlatformSettings : public SettingsBase
|
||||
{
|
||||
DECLARE_SCRIPTING_TYPE_MINIMAL(LinuxPlatformSettings);
|
||||
public:
|
||||
DECLARE_SCRIPTING_TYPE_MINIMAL(LinuxPlatformSettings);
|
||||
API_AUTO_SERIALIZATION();
|
||||
|
||||
/// <summary>
|
||||
/// The default game window mode.
|
||||
@@ -65,25 +65,10 @@ public:
|
||||
API_FIELD(Attributes="EditorOrder(2000), DefaultValue(true), EditorDisplay(\"Graphics\")")
|
||||
bool SupportVulkan = true;
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Gets the instance of the settings asset (default value if missing). Object returned by this method is always loaded with valid data to use.
|
||||
/// </summary>
|
||||
static LinuxPlatformSettings* Get();
|
||||
|
||||
// [SettingsBase]
|
||||
void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) final override
|
||||
{
|
||||
DESERIALIZE(WindowMode);
|
||||
DESERIALIZE(ScreenWidth);
|
||||
DESERIALIZE(ScreenHeight);
|
||||
DESERIALIZE(RunInBackground);
|
||||
DESERIALIZE(ResizableWindow);
|
||||
DESERIALIZE(ForceSingleInstance);
|
||||
DESERIALIZE(OverrideIcon);
|
||||
DESERIALIZE(SupportVulkan);
|
||||
}
|
||||
};
|
||||
|
||||
#if PLATFORM_LINUX
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
API_CLASS(Sealed, Namespace="FlaxEditor.Content.Settings") class FLAXENGINE_API MacPlatformSettings : public ApplePlatformSettings
|
||||
{
|
||||
DECLARE_SCRIPTING_TYPE_MINIMAL(MacPlatformSettings);
|
||||
API_AUTO_SERIALIZATION();
|
||||
|
||||
/// <summary>
|
||||
/// The default game window mode.
|
||||
@@ -43,22 +44,10 @@ API_CLASS(Sealed, Namespace="FlaxEditor.Content.Settings") class FLAXENGINE_API
|
||||
API_FIELD(Attributes="EditorOrder(1010), EditorDisplay(\"Other\", \"Run In Background\")")
|
||||
bool RunInBackground = false;
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
/// Gets the instance of the settings asset (default value if missing). Object returned by this method is always loaded with valid data to use.
|
||||
/// </summary>
|
||||
static MacPlatformSettings* Get();
|
||||
|
||||
// [SettingsBase]
|
||||
void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) final override
|
||||
{
|
||||
ApplePlatformSettings::Deserialize(stream, modifier);
|
||||
DESERIALIZE(WindowMode);
|
||||
DESERIALIZE(ScreenWidth);
|
||||
DESERIALIZE(ScreenHeight);
|
||||
DESERIALIZE(ResizableWindow);
|
||||
DESERIALIZE(RunInBackground);
|
||||
}
|
||||
};
|
||||
|
||||
#if PLATFORM_MAC
|
||||
|
||||
@@ -11,8 +11,8 @@
|
||||
/// </summary>
|
||||
API_CLASS(sealed, Namespace="FlaxEditor.Content.Settings") class FLAXENGINE_API UWPPlatformSettings : public SettingsBase
|
||||
{
|
||||
DECLARE_SCRIPTING_TYPE_MINIMAL(UWPPlatformSettings);
|
||||
public:
|
||||
DECLARE_SCRIPTING_TYPE_MINIMAL(UWPPlatformSettings);
|
||||
API_AUTO_SERIALIZATION();
|
||||
|
||||
/// <summary>
|
||||
/// The preferred launch windowing mode.
|
||||
@@ -66,8 +66,6 @@ public:
|
||||
All = Landscape | LandscapeFlipped | Portrait | PortraitFlipped
|
||||
};
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// The preferred launch windowing mode. Always fullscreen on Xbox.
|
||||
/// </summary>
|
||||
@@ -98,22 +96,10 @@ public:
|
||||
API_FIELD(Attributes="EditorOrder(2010), DefaultValue(false), EditorDisplay(\"Graphics\", \"Support DirectX 10\")")
|
||||
bool SupportDX10 = false;
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Gets the instance of the settings asset (default value if missing). Object returned by this method is always loaded with valid data to use.
|
||||
/// </summary>
|
||||
static UWPPlatformSettings* Get();
|
||||
|
||||
// [SettingsBase]
|
||||
void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) final override
|
||||
{
|
||||
DESERIALIZE(PreferredLaunchWindowingMode);
|
||||
DESERIALIZE(AutoRotationPreferences);
|
||||
DESERIALIZE(CertificateLocation);
|
||||
DESERIALIZE(SupportDX11);
|
||||
DESERIALIZE(SupportDX10);
|
||||
}
|
||||
};
|
||||
|
||||
#if PLATFORM_UWP
|
||||
|
||||
@@ -14,8 +14,8 @@ class Texture;
|
||||
/// </summary>
|
||||
API_CLASS(sealed, Namespace="FlaxEditor.Content.Settings") class FLAXENGINE_API WindowsPlatformSettings : public SettingsBase
|
||||
{
|
||||
DECLARE_SCRIPTING_TYPE_MINIMAL(WindowsPlatformSettings);
|
||||
public:
|
||||
DECLARE_SCRIPTING_TYPE_MINIMAL(WindowsPlatformSettings);
|
||||
API_AUTO_SERIALIZATION();
|
||||
|
||||
/// <summary>
|
||||
/// The default game window mode.
|
||||
@@ -83,28 +83,10 @@ public:
|
||||
API_FIELD(Attributes="EditorOrder(2030), DefaultValue(false), EditorDisplay(\"Graphics\")")
|
||||
bool SupportVulkan = false;
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Gets the instance of the settings asset (default value if missing). Object returned by this method is always loaded with valid data to use.
|
||||
/// </summary>
|
||||
static WindowsPlatformSettings* Get();
|
||||
|
||||
// [SettingsBase]
|
||||
void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) final override
|
||||
{
|
||||
DESERIALIZE(WindowMode);
|
||||
DESERIALIZE(ScreenWidth);
|
||||
DESERIALIZE(ScreenHeight);
|
||||
DESERIALIZE(RunInBackground);
|
||||
DESERIALIZE(ResizableWindow);
|
||||
DESERIALIZE(ForceSingleInstance);
|
||||
DESERIALIZE(OverrideIcon);
|
||||
DESERIALIZE(SupportDX12);
|
||||
DESERIALIZE(SupportDX11);
|
||||
DESERIALIZE(SupportDX10);
|
||||
DESERIALIZE(SupportVulkan);
|
||||
}
|
||||
};
|
||||
|
||||
#if PLATFORM_WINDOWS
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
API_CLASS(Sealed, Namespace="FlaxEditor.Content.Settings") class FLAXENGINE_API iOSPlatformSettings : public ApplePlatformSettings
|
||||
{
|
||||
DECLARE_SCRIPTING_TYPE_MINIMAL(ApplePlatformSettings);
|
||||
API_AUTO_SERIALIZATION();
|
||||
|
||||
/// <summary>
|
||||
/// The app export destination methods.
|
||||
@@ -79,17 +80,6 @@ API_CLASS(Sealed, Namespace="FlaxEditor.Content.Settings") class FLAXENGINE_API
|
||||
/// Gets the instance of the settings asset (default value if missing). Object returned by this method is always loaded with valid data to use.
|
||||
/// </summary>
|
||||
static iOSPlatformSettings* Get();
|
||||
|
||||
// [SettingsBase]
|
||||
void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) final override
|
||||
{
|
||||
ApplePlatformSettings::Deserialize(stream, modifier);
|
||||
DESERIALIZE(AppTeamId);
|
||||
DESERIALIZE(AppVersion);
|
||||
DESERIALIZE(ExportMethod);
|
||||
DESERIALIZE(SupportedInterfaceOrientationsiPhone);
|
||||
DESERIALIZE(SupportedInterfaceOrientationsiPad);
|
||||
}
|
||||
};
|
||||
|
||||
#if PLATFORM_IOS
|
||||
|
||||
@@ -87,6 +87,16 @@ bool FontAsset::Init()
|
||||
return error;
|
||||
}
|
||||
|
||||
FontFlags FontAsset::GetStyle() const
|
||||
{
|
||||
FontFlags flags = FontFlags::None;
|
||||
if ((_face->style_flags & FT_STYLE_FLAG_ITALIC) != 0)
|
||||
flags |= FontFlags::Italic;
|
||||
if ((_face->style_flags & FT_STYLE_FLAG_BOLD) != 0)
|
||||
flags |= FontFlags::Bold;
|
||||
return flags;
|
||||
}
|
||||
|
||||
void FontAsset::SetOptions(const FontOptions& value)
|
||||
{
|
||||
_options = value;
|
||||
|
||||
@@ -128,6 +128,11 @@ public:
|
||||
return _options;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the font style flags.
|
||||
/// </summary>
|
||||
API_PROPERTY() FontFlags GetStyle() const;
|
||||
|
||||
/// <summary>
|
||||
/// Sets the font options.
|
||||
/// </summary>
|
||||
|
||||
@@ -108,19 +108,19 @@ DECLARE_SCRIPTING_TYPE_MINIMAL(LightmapSettings);
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Controls how much all lights will contribute indirect lighting.
|
||||
/// Controls how much all lights will contribute to indirect lighting.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="EditorOrder(0), Limit(0, 100.0f, 0.1f)")
|
||||
float IndirectLightingIntensity = 1.0f;
|
||||
|
||||
/// <summary>
|
||||
/// Global scale for objects in lightmap to increase quality
|
||||
/// Global scale for objects in the lightmap to increase quality
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="EditorOrder(10), Limit(0, 100.0f, 0.1f)")
|
||||
float GlobalObjectsScale = 1.0f;
|
||||
|
||||
/// <summary>
|
||||
/// Amount of pixels space between charts in lightmap atlas
|
||||
/// Amount of pixel space between charts in lightmap atlas
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="EditorOrder(20), Limit(0, 16, 0.1f)")
|
||||
int32 ChartsPadding = 3;
|
||||
|
||||
@@ -906,6 +906,7 @@ void ManagedBinaryModule::OnLoaded(MAssembly* assembly)
|
||||
#if !COMPILE_WITHOUT_CSHARP
|
||||
PROFILE_CPU();
|
||||
ASSERT(ClassToTypeIndex.IsEmpty());
|
||||
ScopeLock lock(Locker);
|
||||
|
||||
const auto& classes = assembly->GetClasses();
|
||||
|
||||
|
||||
@@ -64,8 +64,8 @@ struct MConverter<T, typename TEnableIf<TAnd<TIsPODType<T>, TNot<TIsBaseOf<class
|
||||
|
||||
void Unbox(T& result, MObject* data)
|
||||
{
|
||||
CHECK(data);
|
||||
Platform::MemoryCopy(&result, MCore::Object::Unbox(data), sizeof(T));
|
||||
if (data)
|
||||
Platform::MemoryCopy(&result, MCore::Object::Unbox(data), sizeof(T));
|
||||
}
|
||||
|
||||
void ToManagedArray(MArray* result, const Span<T>& data)
|
||||
|
||||
@@ -719,6 +719,7 @@ void GetAssemblyName(void* assemblyHandle, StringAnsi& name, StringAnsi& fullnam
|
||||
|
||||
DEFINE_INTERNAL_CALL(void) NativeInterop_CreateClass(NativeClassDefinitions* managedClass, void* assemblyHandle)
|
||||
{
|
||||
ScopeLock lock(BinaryModule::Locker);
|
||||
MAssembly* assembly = GetAssembly(assemblyHandle);
|
||||
if (assembly == nullptr)
|
||||
{
|
||||
@@ -732,7 +733,18 @@ DEFINE_INTERNAL_CALL(void) NativeInterop_CreateClass(NativeClassDefinitions* man
|
||||
MClass* klass = New<MClass>(assembly, managedClass->typeHandle, managedClass->name, managedClass->fullname, managedClass->namespace_, managedClass->typeAttributes);
|
||||
if (assembly != nullptr)
|
||||
{
|
||||
const_cast<MAssembly::ClassesDictionary&>(assembly->GetClasses()).Add(klass->GetFullName(), klass);
|
||||
auto& classes = const_cast<MAssembly::ClassesDictionary&>(assembly->GetClasses());
|
||||
MClass* oldKlass;
|
||||
if (classes.TryGet(klass->GetFullName(), oldKlass))
|
||||
{
|
||||
LOG(Warning, "Class '{0}' was already added to assembly '{1}'", String(klass->GetFullName()), String(assembly->GetName()));
|
||||
Delete(klass);
|
||||
klass = oldKlass;
|
||||
}
|
||||
else
|
||||
{
|
||||
classes.Add(klass->GetFullName(), klass);
|
||||
}
|
||||
}
|
||||
managedClass->nativePointer = klass;
|
||||
}
|
||||
@@ -873,7 +885,7 @@ MClass::MClass(const MAssembly* parentAssembly, void* handle, const char* name,
|
||||
static void* TypeIsEnumPtr = GetStaticMethodPointer(TEXT("TypeIsEnum"));
|
||||
_isEnum = CallStaticMethod<bool, void*>(TypeIsEnumPtr, handle);
|
||||
|
||||
CachedClassHandles.Add(handle, this);
|
||||
CachedClassHandles[handle] = this;
|
||||
}
|
||||
|
||||
bool MAssembly::ResolveMissingFile(String& assemblyPath) const
|
||||
@@ -1551,6 +1563,7 @@ const Array<MObject*>& MProperty::GetAttributes() const
|
||||
|
||||
MAssembly* GetAssembly(void* assemblyHandle)
|
||||
{
|
||||
ScopeLock lock(BinaryModule::Locker);
|
||||
MAssembly* assembly;
|
||||
if (CachedAssemblyHandles.TryGet(assemblyHandle, assembly))
|
||||
return assembly;
|
||||
@@ -1559,6 +1572,7 @@ MAssembly* GetAssembly(void* assemblyHandle)
|
||||
|
||||
MClass* GetClass(MType* typeHandle)
|
||||
{
|
||||
ScopeLock lock(BinaryModule::Locker);
|
||||
MClass* klass = nullptr;
|
||||
CachedClassHandles.TryGet(typeHandle, klass);
|
||||
return nullptr;
|
||||
@@ -1568,6 +1582,7 @@ MClass* GetOrCreateClass(MType* typeHandle)
|
||||
{
|
||||
if (!typeHandle)
|
||||
return nullptr;
|
||||
ScopeLock lock(BinaryModule::Locker);
|
||||
MClass* klass;
|
||||
if (!CachedClassHandles.TryGet(typeHandle, klass))
|
||||
{
|
||||
@@ -1579,7 +1594,12 @@ MClass* GetOrCreateClass(MType* typeHandle)
|
||||
klass = New<MClass>(assembly, classInfo.typeHandle, classInfo.name, classInfo.fullname, classInfo.namespace_, classInfo.typeAttributes);
|
||||
if (assembly != nullptr)
|
||||
{
|
||||
const_cast<MAssembly::ClassesDictionary&>(assembly->GetClasses()).Add(klass->GetFullName(), klass);
|
||||
auto& classes = const_cast<MAssembly::ClassesDictionary&>(assembly->GetClasses());
|
||||
if (classes.ContainsKey(klass->GetFullName()))
|
||||
{
|
||||
LOG(Warning, "Class '{0}' was already added to assembly '{1}'", String(klass->GetFullName()), String(assembly->GetName()));
|
||||
}
|
||||
classes[klass->GetFullName()] = klass;
|
||||
}
|
||||
|
||||
if (typeHandle != classInfo.typeHandle)
|
||||
|
||||
@@ -436,6 +436,7 @@ bool Scripting::Load()
|
||||
PROFILE_CPU();
|
||||
// Note: this action can be called from main thread (due to Mono problems with assemblies actions from other threads)
|
||||
ASSERT(IsInMainThread());
|
||||
ScopeLock lock(BinaryModule::Locker);
|
||||
|
||||
#if USE_CSHARP
|
||||
// Load C# core assembly
|
||||
|
||||
@@ -268,6 +268,7 @@ namespace FlaxEngine
|
||||
Foreground = Color.FromBgra(0xFFFFFFFF),
|
||||
ForegroundGrey = Color.FromBgra(0xFFA9A9B3),
|
||||
ForegroundDisabled = Color.FromBgra(0xFF787883),
|
||||
ForegroundViewport = Color.FromBgra(0xFFFFFFFF),
|
||||
BackgroundHighlighted = Color.FromBgra(0xFF54545C),
|
||||
BorderHighlighted = Color.FromBgra(0xFF6A6A75),
|
||||
BackgroundSelected = Color.FromBgra(0xFF007ACC),
|
||||
|
||||
@@ -0,0 +1,254 @@
|
||||
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Serialization;
|
||||
using FlaxEngine.Interop;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Serialization;
|
||||
|
||||
#nullable enable
|
||||
|
||||
namespace FlaxEngine.Json.JsonCustomSerializers
|
||||
{
|
||||
internal class ExtendedSerializationBinder : SerializationBinder, ISerializationBinder
|
||||
{
|
||||
private record struct TypeKey(string? assemblyName, string typeName);
|
||||
|
||||
private ConcurrentDictionary<TypeKey, Type> _typeCache;
|
||||
private Func<TypeKey, Type> _resolveType;
|
||||
|
||||
/// <summary>Clear the cache</summary>
|
||||
/// <remarks>Should be cleared on scripting domain reload to avoid out of date types participating in dynamic type resolution</remarks>
|
||||
public void ResetCache()
|
||||
{
|
||||
_typeCache.Clear();
|
||||
}
|
||||
|
||||
public override Type BindToType(string? assemblyName, string typeName)
|
||||
{
|
||||
return FindCachedType(new(assemblyName, typeName));
|
||||
}
|
||||
|
||||
public override void BindToName(Type serializedType, out string? assemblyName, out string? typeName)
|
||||
{
|
||||
assemblyName = serializedType.Assembly.FullName;
|
||||
typeName = serializedType.FullName;
|
||||
}
|
||||
|
||||
|
||||
public ExtendedSerializationBinder()
|
||||
{
|
||||
_resolveType = ResolveType;
|
||||
_typeCache = new();
|
||||
}
|
||||
|
||||
Type ResolveType(TypeKey key)
|
||||
{
|
||||
Type? type = null;
|
||||
if (key.assemblyName is null)
|
||||
{
|
||||
// No assembly name, attempt to find globally
|
||||
type = FindTypeGlobal(key.typeName);
|
||||
}
|
||||
|
||||
if (type is null && key.assemblyName is not null)
|
||||
{
|
||||
// Type not found yet, but we have assembly name
|
||||
var assembly = ResolveAssembly(new(key.assemblyName));
|
||||
|
||||
// We have assembly, attempt to load from assembly
|
||||
type = FindTypeInAssembly(key.typeName, assembly);
|
||||
}
|
||||
|
||||
//if (type is null)
|
||||
// type = _fallBack.BindToType(key.assemblyName, key.typeName); // Use fallback
|
||||
|
||||
if (type is null)
|
||||
throw MakeTypeResolutionException(key.assemblyName, key.typeName);
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
|
||||
Assembly ResolveAssembly(AssemblyName name)
|
||||
{
|
||||
Assembly? assembly = null;
|
||||
assembly = FindScriptingAssembly(name); // Attempt to find in scripting assemblies
|
||||
if (assembly is null)
|
||||
assembly = FindLoadAssembly(name); // Attempt to load
|
||||
if (assembly is null)
|
||||
assembly = FindDomainAssembly(name); // Attempt to find in the current domain
|
||||
if (assembly is null)
|
||||
throw MakeAsmResolutionException(name.FullName); // Assembly failed to resolve
|
||||
return assembly;
|
||||
}
|
||||
|
||||
/// <summary>Attempt to find the assembly among loaded scripting assemblies</summary>
|
||||
Assembly? FindScriptingAssembly(AssemblyName assemblyName)
|
||||
{
|
||||
return NativeInterop.ResolveScriptingAssemblyByName(assemblyName, allowPartial: true);
|
||||
}
|
||||
|
||||
/// <summary>Attempt to find the assembly in the current domain</summary>
|
||||
Assembly? FindDomainAssembly(AssemblyName assemblyName)
|
||||
{
|
||||
var assemblies = AppDomain.CurrentDomain.GetAssemblies();
|
||||
foreach (Assembly assembly in assemblies)
|
||||
{
|
||||
// Looking in domain may be necessary (in case of anon dynamic assembly for example)
|
||||
var curName = assembly.GetName();
|
||||
if (curName == assemblyName || curName.Name == assemblyName.Name)
|
||||
return assembly;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>Attempt to load the assembly</summary>
|
||||
Assembly? FindLoadAssembly(AssemblyName assemblyName)
|
||||
{
|
||||
Assembly? assembly = null;
|
||||
assembly = Assembly.Load(assemblyName);
|
||||
if (assembly is null && assemblyName.Name is not null)
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
assembly = Assembly.LoadWithPartialName(assemblyName.Name); // Copying behavior of DefaultSerializationBinder
|
||||
#pragma warning restore CS0618 // Type or member is obsolete
|
||||
return assembly;
|
||||
}
|
||||
|
||||
/// <summary>Attempt to find a type in a specified assembly</summary>
|
||||
Type? FindTypeInAssembly(string typeName, Assembly assembly)
|
||||
{
|
||||
var type = assembly.GetType(typeName); // Attempt to load directly
|
||||
if (type is null && typeName.IndexOf('`') >= 0) // Attempt failed, but name has generic variant tick, try resolving generic manually
|
||||
type = FindTypeGeneric(typeName, assembly);
|
||||
return type;
|
||||
}
|
||||
|
||||
/// <summary>Attempt to find unqualified type by only name</summary>
|
||||
Type? FindTypeGlobal(string typeName)
|
||||
{
|
||||
return Type.GetType(typeName);
|
||||
}
|
||||
|
||||
/// <summary>Get type from the cache</summary>
|
||||
private Type FindCachedType(TypeKey key)
|
||||
{
|
||||
return _typeCache.GetOrAdd(key, _resolveType);
|
||||
}
|
||||
|
||||
/*********************************************
|
||||
** Below code is adapted from Newtonsoft.Json
|
||||
*********************************************/
|
||||
|
||||
/// <summary>Attempt to recursively resolve a generic type</summary>
|
||||
private Type? FindTypeGeneric(string typeName, Assembly assembly)
|
||||
{
|
||||
Type? type = null;
|
||||
int openBracketIndex = typeName.IndexOf('[', StringComparison.Ordinal);
|
||||
if (openBracketIndex >= 0)
|
||||
{
|
||||
string genericTypeDefName = typeName.Substring(0, openBracketIndex); // Find the unspecialized type
|
||||
Type? genericTypeDef = assembly.GetType(genericTypeDefName);
|
||||
if (genericTypeDef != null)
|
||||
{
|
||||
List<Type> genericTypeArguments = new List<Type>(); // Recursively resolve the arguments
|
||||
int scope = 0;
|
||||
int typeArgStartIndex = 0;
|
||||
int endIndex = typeName.Length - 1;
|
||||
for (int i = openBracketIndex + 1; i < endIndex; ++i)
|
||||
{
|
||||
char current = typeName[i];
|
||||
switch (current)
|
||||
{
|
||||
case '[':
|
||||
if (scope == 0)
|
||||
{
|
||||
typeArgStartIndex = i + 1;
|
||||
}
|
||||
++scope;
|
||||
break;
|
||||
case ']':
|
||||
--scope;
|
||||
if (scope == 0)
|
||||
{
|
||||
// All arguments resolved, compose our type
|
||||
string typeArgAssemblyQualifiedName = typeName.Substring(typeArgStartIndex, i - typeArgStartIndex);
|
||||
|
||||
TypeKey typeNameKey = SplitFullyQualifiedTypeName(typeArgAssemblyQualifiedName);
|
||||
genericTypeArguments.Add(FindCachedType(typeNameKey));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
type = genericTypeDef.MakeGenericType(genericTypeArguments.ToArray());
|
||||
}
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
/// <summary>Split a fully qualified type name into assembly name, and type name</summary>
|
||||
private static TypeKey SplitFullyQualifiedTypeName(string fullyQualifiedTypeName)
|
||||
{
|
||||
int? assemblyDelimiterIndex = GetAssemblyDelimiterIndex(fullyQualifiedTypeName);
|
||||
ReadOnlySpan<char> typeName, assemblyName;
|
||||
if (assemblyDelimiterIndex != null)
|
||||
{
|
||||
typeName = fullyQualifiedTypeName.AsSpan().Slice(0, assemblyDelimiterIndex ?? 0);
|
||||
assemblyName = fullyQualifiedTypeName.AsSpan().Slice((assemblyDelimiterIndex ?? 0) + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
typeName = fullyQualifiedTypeName;
|
||||
assemblyName = null;
|
||||
}
|
||||
return new(new(assemblyName), new(typeName));
|
||||
}
|
||||
|
||||
/// <summary>Find the assembly name inside a fully qualified type name</summary>
|
||||
private static int? GetAssemblyDelimiterIndex(string fullyQualifiedTypeName)
|
||||
{
|
||||
// we need to get the first comma following all surrounded in brackets because of generic types
|
||||
// e.g. System.Collections.Generic.Dictionary`2[[System.String, mscorlib,Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
|
||||
int scope = 0;
|
||||
for (int i = 0; i < fullyQualifiedTypeName.Length; i++)
|
||||
{
|
||||
char current = fullyQualifiedTypeName[i];
|
||||
switch (current)
|
||||
{
|
||||
case '[':
|
||||
scope++;
|
||||
break;
|
||||
case ']':
|
||||
scope--;
|
||||
break;
|
||||
case ',':
|
||||
if (scope == 0)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
private static JsonSerializationException MakeAsmResolutionException(string asmName)
|
||||
{
|
||||
return new($"Could not load assembly '{asmName}'.");
|
||||
}
|
||||
|
||||
private static JsonSerializationException MakeTypeResolutionException(string? asmName, string typeName)
|
||||
{
|
||||
if (asmName is null)
|
||||
return new($"Could not find '{typeName}'");
|
||||
else
|
||||
return new($"Could not find '{typeName}' in assembly '{asmName}'.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,7 @@ namespace FlaxEngine.Json
|
||||
{
|
||||
internal class SerializerCache
|
||||
{
|
||||
public readonly JsonSerializerSettings JsonSettings;
|
||||
public Newtonsoft.Json.JsonSerializer JsonSerializer;
|
||||
public StringBuilder StringBuilder;
|
||||
public StringWriter StringWriter;
|
||||
@@ -28,17 +29,106 @@ namespace FlaxEngine.Json
|
||||
public StreamReader Reader;
|
||||
public bool IsWriting;
|
||||
public bool IsReading;
|
||||
#if FLAX_EDITOR
|
||||
public uint CacheVersion;
|
||||
#endif
|
||||
|
||||
public unsafe SerializerCache(JsonSerializerSettings settings)
|
||||
{
|
||||
JsonSerializer = Newtonsoft.Json.JsonSerializer.CreateDefault(settings);
|
||||
JsonSerializer.Formatting = Formatting.Indented;
|
||||
JsonSerializer.ReferenceLoopHandling = ReferenceLoopHandling.Serialize;
|
||||
JsonSettings = settings;
|
||||
StringBuilder = new StringBuilder(256);
|
||||
StringWriter = new StringWriter(StringBuilder, CultureInfo.InvariantCulture);
|
||||
SerializerWriter = new JsonSerializerInternalWriter(JsonSerializer);
|
||||
MemoryStream = new UnmanagedMemoryStream((byte*)0, 0);
|
||||
|
||||
#if FLAX_EDITOR
|
||||
lock (CurrentCacheSyncRoot)
|
||||
#endif
|
||||
{
|
||||
BuildSerializer();
|
||||
BuildRead();
|
||||
BuildWrite();
|
||||
#if FLAX_EDITOR
|
||||
CacheVersion = Json.JsonSerializer.CurrentCacheVersion;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
public void ReadBegin()
|
||||
{
|
||||
CheckCacheVersionRebuild();
|
||||
|
||||
// TODO: Reset reading state (eg if previous deserialization got exception)
|
||||
if (IsReading)
|
||||
BuildRead();
|
||||
|
||||
IsWriting = false;
|
||||
IsReading = true;
|
||||
}
|
||||
|
||||
public void ReadEnd()
|
||||
{
|
||||
IsReading = false;
|
||||
}
|
||||
|
||||
public void WriteBegin()
|
||||
{
|
||||
CheckCacheVersionRebuild();
|
||||
|
||||
// Reset writing state (eg if previous serialization got exception)
|
||||
if (IsWriting)
|
||||
BuildWrite();
|
||||
|
||||
StringBuilder.Clear();
|
||||
IsWriting = true;
|
||||
IsReading = false;
|
||||
}
|
||||
|
||||
public void WriteEnd()
|
||||
{
|
||||
IsWriting = false;
|
||||
}
|
||||
|
||||
/// <summary>Check that the cache is up to date, rebuild it if it isn't</summary>
|
||||
private void CheckCacheVersionRebuild()
|
||||
{
|
||||
#if FLAX_EDITOR
|
||||
var version = Json.JsonSerializer.CurrentCacheVersion;
|
||||
if (CacheVersion == version)
|
||||
return;
|
||||
|
||||
lock (CurrentCacheSyncRoot)
|
||||
{
|
||||
version = Json.JsonSerializer.CurrentCacheVersion;
|
||||
if (CacheVersion == version)
|
||||
return;
|
||||
|
||||
BuildSerializer();
|
||||
BuildRead();
|
||||
BuildWrite();
|
||||
|
||||
CacheVersion = version;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>Builds the serializer</summary>
|
||||
private void BuildSerializer()
|
||||
{
|
||||
JsonSerializer = Newtonsoft.Json.JsonSerializer.CreateDefault(Settings);
|
||||
JsonSerializer.Formatting = Formatting.Indented;
|
||||
JsonSerializer.ReferenceLoopHandling = ReferenceLoopHandling.Serialize;
|
||||
}
|
||||
|
||||
/// <summary>Builds the reader state</summary>
|
||||
private void BuildRead()
|
||||
{
|
||||
Reader = new StreamReader(MemoryStream, Encoding.UTF8, false);
|
||||
}
|
||||
|
||||
/// <summary>Builds the writer state</summary>
|
||||
private void BuildWrite()
|
||||
{
|
||||
SerializerWriter = new JsonSerializerInternalWriter(JsonSerializer);
|
||||
JsonWriter = new JsonTextWriter(StringWriter)
|
||||
{
|
||||
IndentChar = '\t',
|
||||
@@ -52,65 +142,30 @@ namespace FlaxEngine.Json
|
||||
DateFormatString = JsonSerializer.DateFormatString,
|
||||
};
|
||||
}
|
||||
|
||||
public void ReadBegin()
|
||||
{
|
||||
if (IsReading)
|
||||
{
|
||||
// TODO: Reset reading state (eg if previous deserialization got exception)
|
||||
}
|
||||
IsWriting = false;
|
||||
IsReading = true;
|
||||
}
|
||||
|
||||
public void ReadEnd()
|
||||
{
|
||||
IsReading = false;
|
||||
}
|
||||
|
||||
public void WriteBegin()
|
||||
{
|
||||
if (IsWriting)
|
||||
{
|
||||
// Reset writing state (eg if previous serialization got exception)
|
||||
SerializerWriter = new JsonSerializerInternalWriter(JsonSerializer);
|
||||
JsonWriter = new JsonTextWriter(StringWriter)
|
||||
{
|
||||
IndentChar = '\t',
|
||||
Indentation = 1,
|
||||
Formatting = JsonSerializer.Formatting,
|
||||
DateFormatHandling = JsonSerializer.DateFormatHandling,
|
||||
DateTimeZoneHandling = JsonSerializer.DateTimeZoneHandling,
|
||||
FloatFormatHandling = JsonSerializer.FloatFormatHandling,
|
||||
StringEscapeHandling = JsonSerializer.StringEscapeHandling,
|
||||
Culture = JsonSerializer.Culture,
|
||||
DateFormatString = JsonSerializer.DateFormatString,
|
||||
};
|
||||
}
|
||||
StringBuilder.Clear();
|
||||
IsWriting = true;
|
||||
IsReading = false;
|
||||
}
|
||||
|
||||
public void WriteEnd()
|
||||
{
|
||||
IsWriting = false;
|
||||
}
|
||||
}
|
||||
|
||||
internal static JsonSerializerSettings Settings = CreateDefaultSettings(false);
|
||||
internal static JsonSerializerSettings SettingsManagedOnly = CreateDefaultSettings(true);
|
||||
internal static ExtendedSerializationBinder SerializationBinder;
|
||||
internal static FlaxObjectConverter ObjectConverter;
|
||||
internal static ThreadLocal<SerializerCache> Current = new ThreadLocal<SerializerCache>();
|
||||
internal static ThreadLocal<SerializerCache> Cache = new ThreadLocal<SerializerCache>(() => new SerializerCache(Settings));
|
||||
internal static ThreadLocal<SerializerCache> CacheManagedOnly = new ThreadLocal<SerializerCache>(() => new SerializerCache(SettingsManagedOnly));
|
||||
internal static ThreadLocal<IntPtr> CachedGuidBuffer = new ThreadLocal<IntPtr>(() => Marshal.AllocHGlobal(32 * sizeof(char)), true);
|
||||
internal static string CachedGuidDigits = "0123456789abcdef";
|
||||
#if FLAX_EDITOR
|
||||
/// <summary>The version of the cache, used to check that a cache is not out of date</summary>
|
||||
internal static uint CurrentCacheVersion = 0;
|
||||
|
||||
/// <summary>Used to synchronize cache operations such as <see cref="SerializerCache"/> rebuild, and <see cref="ResetCache"/></summary>
|
||||
internal static readonly object CurrentCacheSyncRoot = new();
|
||||
#endif
|
||||
|
||||
internal static JsonSerializerSettings CreateDefaultSettings(bool isManagedOnly)
|
||||
{
|
||||
//Newtonsoft.Json.Utilities.MiscellaneousUtils.ValueEquals = ValueEquals;
|
||||
|
||||
if (SerializationBinder is null)
|
||||
SerializationBinder = new();
|
||||
var settings = new JsonSerializerSettings
|
||||
{
|
||||
ContractResolver = new ExtendedDefaultContractResolver(isManagedOnly),
|
||||
@@ -118,6 +173,7 @@ namespace FlaxEngine.Json
|
||||
TypeNameHandling = TypeNameHandling.Auto,
|
||||
NullValueHandling = NullValueHandling.Include,
|
||||
ObjectCreationHandling = ObjectCreationHandling.Auto,
|
||||
SerializationBinder = SerializationBinder,
|
||||
};
|
||||
if (ObjectConverter == null)
|
||||
ObjectConverter = new FlaxObjectConverter();
|
||||
@@ -134,6 +190,23 @@ namespace FlaxEngine.Json
|
||||
return settings;
|
||||
}
|
||||
|
||||
#if FLAX_EDITOR
|
||||
/// <summary>Resets the serialization cache.</summary>
|
||||
internal static void ResetCache()
|
||||
{
|
||||
lock (CurrentCacheSyncRoot)
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
CurrentCacheVersion++;
|
||||
}
|
||||
|
||||
Newtonsoft.Json.JsonSerializer.ClearCache();
|
||||
SerializationBinder.ResetCache();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
internal static void Dispose()
|
||||
{
|
||||
CachedGuidBuffer.Values.ForEach(Marshal.FreeHGlobal);
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "SerializationFwd.h"
|
||||
#include "ISerializeModifier.h"
|
||||
#include "Engine/Core/Collections/Array.h"
|
||||
#include "Engine/Core/Collections/Dictionary.h"
|
||||
#include "Engine/Scripting/ScriptingObject.h"
|
||||
|
||||
@@ -3,10 +3,11 @@
|
||||
#pragma once
|
||||
|
||||
#include "Engine/Core/ISerializable.h"
|
||||
#include "ISerializeModifier.h"
|
||||
#include "Json.h"
|
||||
#include "JsonWriter.h"
|
||||
|
||||
class ISerializeModifier;
|
||||
|
||||
// The floating-point values serialization epsilon for equality checks precision
|
||||
#define SERIALIZE_EPSILON 1e-7f
|
||||
#define SERIALIZE_EPSILON_DOUBLE 1e-17
|
||||
|
||||
@@ -36,7 +36,7 @@ DECLARE_SCRIPTING_TYPE_MINIMAL(TextureGroup);
|
||||
int32 MaxAnisotropy = 16;
|
||||
|
||||
/// <summary>
|
||||
/// The quality scale factor applied to textures in this group. Can be used to increase or decrease textures resolution. In range 0-1 where 0 means lowest quality, 1 means full quality.
|
||||
/// The quality scale factor applied to textures in this group. Can be used to increase or decrease textures resolution. In the range 0-1 where 0 means lowest quality, 1 means full quality.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="EditorOrder(20), Limit(0, 1)")
|
||||
float Quality = 1.0f;
|
||||
@@ -60,20 +60,20 @@ DECLARE_SCRIPTING_TYPE_MINIMAL(TextureGroup);
|
||||
int32 MipLevelsMin = 0;
|
||||
|
||||
/// <summary>
|
||||
/// The maximum amount of loaded mip levels for textures in this group. Defines the maximum amount of the mips that can be loaded. Overriden per-platform. Lower values reduce textures quality and improve performance.
|
||||
/// The maximum amount of loaded mip levels for textures in this group. Defines the maximum amount of mips that can be loaded. Overriden per-platform. Lower values reduce texture quality and improve performance.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="EditorOrder(40), Limit(1, 14)")
|
||||
int32 MipLevelsMax = 14;
|
||||
|
||||
/// <summary>
|
||||
/// The loaded mip levels bias for textures in this group. Can be used to increase or decrease quality of the streaming for textures in this group (eg. bump up the quality during cinematic sequence).
|
||||
/// The loaded mip levels bias for textures in this group. Can be used to increase or decrease the quality of streaming for textures in this group (eg. bump up the quality during cinematic sequence).
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="EditorOrder(50), Limit(-14, 14)")
|
||||
int32 MipLevelsBias = 0;
|
||||
|
||||
#if USE_EDITOR
|
||||
/// <summary>
|
||||
/// The per-platform maximum amount of mip levels for textures in this group. Can be used to strip textures quality when cooking game for a target platform.
|
||||
/// The per-platform maximum amount of mip levels for textures in this group. Can be used to strip textures quality when cooking the game for a target platform.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="EditorOrder(50)")
|
||||
Dictionary<PlatformType, int32> MipLevelsMaxPerPlatform;
|
||||
|
||||
@@ -519,6 +519,40 @@ void MaterialGenerator::ProcessGroupMaterial(Box* box, Node* node, Value& value)
|
||||
}
|
||||
break;
|
||||
}
|
||||
// Rectangle Mask
|
||||
case 40:
|
||||
{
|
||||
const auto uv = tryGetValue(node->GetBox(0), getUVs).AsFloat2();
|
||||
const auto rectangle = tryGetValue(node->GetBox(1), node->Values[0]).AsFloat2();
|
||||
auto d = writeLocal(ValueType::Float2, String::Format(TEXT("abs({0} * 2 - 1) - {1}"), uv.Value, rectangle.Value), node);
|
||||
auto d2 = writeLocal(ValueType::Float2, String::Format(TEXT("1 - {0} / fwidth({0})"), d.Value), node);
|
||||
value = writeLocal(ValueType::Float, String::Format(TEXT("saturate(min({0}.x, {0}.y))"), d2.Value), node);
|
||||
break;
|
||||
}
|
||||
// FWidth
|
||||
case 41:
|
||||
{
|
||||
const auto inValue = tryGetValue(node->GetBox(0), 0, Value::Zero);
|
||||
value = writeLocal(inValue.Type, String::Format(TEXT("fwidth({0})"), inValue.Value), node);
|
||||
break;
|
||||
}
|
||||
// AA Step
|
||||
case 42:
|
||||
{
|
||||
// Reference: https://www.ronja-tutorials.com/post/046-fwidth/#a-better-step
|
||||
|
||||
const auto compValue = tryGetValue(node->GetBox(0), getUVs).AsFloat();
|
||||
const auto gradient = tryGetValue(node->GetBox(1), node->Values[0]).AsFloat();
|
||||
|
||||
auto change = writeLocal(ValueType::Float, String::Format(TEXT("fwidth({0})"), gradient.Value), node);
|
||||
|
||||
// Base the range of the inverse lerp on the change over two pixels
|
||||
auto lowerEdge = writeLocal(ValueType::Float, String::Format(TEXT("{0} - {1}"), compValue.Value, change.Value), node);
|
||||
auto upperEdge = writeLocal(ValueType::Float, String::Format(TEXT("{0} + {1}"), compValue.Value, change.Value), node);
|
||||
|
||||
// Do the inverse interpolation and saturate it
|
||||
value = writeLocal(ValueType::Float, String::Format(TEXT("saturate((({0} - {1}) / ({2} - {1})))"), gradient.Value, lowerEdge.Value, upperEdge.Value), node);
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -228,25 +228,25 @@ public:
|
||||
|
||||
public: // Geometry
|
||||
|
||||
// Enable model normal vectors recalculating.
|
||||
// Enable model normal vectors re-calculating.
|
||||
API_FIELD(Attributes="EditorOrder(20), EditorDisplay(\"Geometry\"), VisibleIf(nameof(ShowGeometry))")
|
||||
bool CalculateNormals = false;
|
||||
// Specifies the maximum angle (in degrees) that may be between two face normals at the same vertex position that their are smoothed together. The default value is 175.
|
||||
// Specifies the maximum angle (in degrees) that may be between two face normals at the same vertex position before they are smoothed together. The default value is 175.
|
||||
API_FIELD(Attributes="EditorOrder(30), EditorDisplay(\"Geometry\"), VisibleIf(nameof(ShowSmoothingNormalsAngle)), Limit(0, 175, 0.1f)")
|
||||
float SmoothingNormalsAngle = 175.0f;
|
||||
// If checked, the imported normal vectors of the mesh will be flipped (scaled by -1).
|
||||
API_FIELD(Attributes="EditorOrder(35), EditorDisplay(\"Geometry\"), VisibleIf(nameof(ShowGeometry))")
|
||||
bool FlipNormals = false;
|
||||
// Enable model tangent vectors recalculating.
|
||||
// Enable model tangent vectors re-calculating.
|
||||
API_FIELD(Attributes="EditorOrder(40), EditorDisplay(\"Geometry\"), VisibleIf(nameof(ShowGeometry))")
|
||||
bool CalculateTangents = false;
|
||||
// Specifies the maximum angle (in degrees) that may be between two vertex tangents that their tangents and bi-tangents are smoothed. The default value is 45.
|
||||
// Specifies the maximum angle (in degrees) that may be between two vertex tangents before their tangents and bi-tangents are smoothed. The default value is 45.
|
||||
API_FIELD(Attributes="EditorOrder(45), EditorDisplay(\"Geometry\"), VisibleIf(nameof(ShowSmoothingTangentsAngle)), Limit(0, 45, 0.1f)")
|
||||
float SmoothingTangentsAngle = 45.0f;
|
||||
// Enable/disable meshes geometry optimization.
|
||||
API_FIELD(Attributes="EditorOrder(50), EditorDisplay(\"Geometry\"), VisibleIf(nameof(ShowGeometry))")
|
||||
bool OptimizeMeshes = true;
|
||||
// Enable/disable geometry merge for meshes with the same materials.
|
||||
// Enable/disable geometry merge for meshes with the same materials. Index buffer will be reordered to improve performance and other modifications will be applied. However, importing time will be increased.
|
||||
API_FIELD(Attributes="EditorOrder(60), EditorDisplay(\"Geometry\"), VisibleIf(nameof(ShowGeometry))")
|
||||
bool MergeMeshes = true;
|
||||
// Enable/disable importing meshes Level of Details.
|
||||
@@ -258,16 +258,16 @@ public:
|
||||
// Enable/disable importing blend shapes (morph targets).
|
||||
API_FIELD(Attributes="EditorOrder(85), EditorDisplay(\"Geometry\"), VisibleIf(nameof(ShowSkinnedModel))")
|
||||
bool ImportBlendShapes = false;
|
||||
// Enable skeleton bones offset matrices recalculating.
|
||||
// Enable skeleton bones offset matrices re-calculating.
|
||||
API_FIELD(Attributes="EditorOrder(86), EditorDisplay(\"Geometry\"), VisibleIf(nameof(ShowSkinnedModel))")
|
||||
bool CalculateBoneOffsetMatrices = false;
|
||||
// The lightmap UVs source.
|
||||
API_FIELD(Attributes="EditorOrder(90), EditorDisplay(\"Geometry\", \"Lightmap UVs Source\"), VisibleIf(nameof(ShowModel))")
|
||||
ModelLightmapUVsSource LightmapUVsSource = ModelLightmapUVsSource::Disable;
|
||||
// If specified, all meshes which name starts with this prefix will be imported as a separate collision data (excluded used for rendering).
|
||||
// If specified, all meshes that name starts with this prefix in the name will be imported as a separate collision data asset (excluded used for rendering).
|
||||
API_FIELD(Attributes="EditorOrder(100), EditorDisplay(\"Geometry\"), VisibleIf(nameof(ShowGeometry))")
|
||||
String CollisionMeshesPrefix = TEXT("");
|
||||
// The type of collision that should be generated if has collision prefix specified.
|
||||
// The type of collision that should be generated if the mesh has a collision prefix specified.
|
||||
API_FIELD(Attributes = "EditorOrder(105), EditorDisplay(\"Geometry\"), VisibleIf(nameof(ShowGeometry))")
|
||||
CollisionDataType CollisionType = CollisionDataType::TriangleMesh;
|
||||
|
||||
@@ -291,19 +291,19 @@ public:
|
||||
|
||||
public: // Animation
|
||||
|
||||
// Imported animation duration mode. Can use the original value or overriden by settings.
|
||||
// Imported animation duration mode. Can use the original value or be overriden by settings.
|
||||
API_FIELD(Attributes="EditorOrder(1000), EditorDisplay(\"Animation\"), VisibleIf(nameof(ShowAnimation))")
|
||||
AnimationDuration Duration = AnimationDuration::Imported;
|
||||
// Imported animation first/last frame index. Used only if Duration mode is set to Custom.
|
||||
API_FIELD(Attributes="EditorOrder(1010), EditorDisplay(\"Animation\"), VisibleIf(nameof(ShowFramesRange)), Limit(0)")
|
||||
Float2 FramesRange = Float2::Zero;
|
||||
// The imported animation default frame rate. Can specify the default frames per second amount for imported animation. If value is 0 then the original animation frame rate will be used.
|
||||
// The imported animation default frame rate. Can specify the default frames per second amount for imported animations. If the value is 0 then the original animation frame rate will be used.
|
||||
API_FIELD(Attributes="EditorOrder(1020), EditorDisplay(\"Animation\"), VisibleIf(nameof(ShowAnimation)), Limit(0, 1000, 0.01f)")
|
||||
float DefaultFrameRate = 0.0f;
|
||||
// The imported animation sampling rate. If value is 0 then the original animation speed will be used.
|
||||
API_FIELD(Attributes="EditorOrder(1030), EditorDisplay(\"Animation\"), VisibleIf(nameof(ShowAnimation)), Limit(0, 1000, 0.01f)")
|
||||
float SamplingRate = 0.0f;
|
||||
// The imported animation will have removed tracks with no keyframes or unspecified data.
|
||||
// The imported animation will have tracks with no keyframes or unspecified data removed.
|
||||
API_FIELD(Attributes="EditorOrder(1040), EditorDisplay(\"Animation\"), VisibleIf(nameof(ShowAnimation))")
|
||||
bool SkipEmptyCurves = true;
|
||||
// The imported animation channels will be optimized to remove redundant keyframes.
|
||||
@@ -348,7 +348,7 @@ public:
|
||||
// If checked, the importer will create the model's materials as instances of a base material.
|
||||
API_FIELD(Attributes = "EditorOrder(401), EditorDisplay(\"Materials\"), VisibleIf(nameof(ImportMaterials))")
|
||||
bool ImportMaterialsAsInstances = false;
|
||||
// The material to import the model's materials as an instance of.
|
||||
// The material used as the base material that will be instanced as the imported model's material.
|
||||
API_FIELD(Attributes = "EditorOrder(402), EditorDisplay(\"Materials\"), VisibleIf(nameof(ImportMaterialsAsInstances))")
|
||||
AssetReference<MaterialBase> InstanceToImportAs;
|
||||
// If checked, the importer will import texture files used by the model and any embedded texture resources.
|
||||
@@ -369,16 +369,16 @@ public:
|
||||
|
||||
public: // Splitting
|
||||
|
||||
// If checked, the imported mesh/animations are splitted into separate assets. Used if ObjectIndex is set to -1.
|
||||
// If checked, the imported mesh/animations are split into separate assets. Used if ObjectIndex is set to -1.
|
||||
API_FIELD(Attributes="EditorOrder(2000), EditorDisplay(\"Splitting\")")
|
||||
bool SplitObjects = false;
|
||||
// The zero-based index for the mesh/animation clip to import. If the source file has more than one mesh/animation it can be used to pick a desire object. Default -1 imports all objects.
|
||||
// The zero-based index for the mesh/animation clip to import. If the source file has more than one mesh/animation it can be used to pick a desired object. Default -1 imports all objects.
|
||||
API_FIELD(Attributes="EditorOrder(2010), EditorDisplay(\"Splitting\")")
|
||||
int32 ObjectIndex = -1;
|
||||
|
||||
public: // Other
|
||||
|
||||
// If specified, will be used as sub-directory name for automatically imported sub assets such as textures and materials. Set to whitespace (single space) to import to the same directory.
|
||||
// If specified, the specified folder will be used as sub-directory name for automatically imported sub assets such as textures and materials. Set to whitespace (single space) to import to the same directory.
|
||||
API_FIELD(Attributes="EditorOrder(3030), EditorDisplay(\"Other\")")
|
||||
String SubAssetFolder = TEXT("");
|
||||
|
||||
|
||||
@@ -57,11 +57,11 @@ API_CLASS(Namespace="FlaxEngine.Tools", Static) class FLAXENGINE_API TextureTool
|
||||
API_FIELD(Attributes="EditorOrder(70)")
|
||||
bool FlipY = false;
|
||||
|
||||
// Texture size scale. Default is 1.
|
||||
// Texture size scale. Allows increasing or decreasing the imported texture resolution. Default is 1.
|
||||
API_FIELD(Attributes="EditorOrder(80), Limit(0.0001f, 1000.0f, 0.01f)")
|
||||
float Scale = 1.0f;
|
||||
|
||||
// Maximum size of the texture (for both width and height). Higher resolution textures will be resized during importing process.
|
||||
// Maximum size of the texture (for both width and height). Higher resolution textures will be resized during importing process. Used to clip textures that are too big.
|
||||
API_FIELD(Attributes="HideInEditor")
|
||||
int32 MaxSize = 8192;
|
||||
|
||||
@@ -69,11 +69,11 @@ API_CLASS(Namespace="FlaxEngine.Tools", Static) class FLAXENGINE_API TextureTool
|
||||
API_FIELD(Attributes="EditorOrder(100)")
|
||||
bool Resize = false;
|
||||
|
||||
// The width of the imported texture. If Resize property is set to true then texture will be resized during the import to this value. Otherwise it will be ignored.
|
||||
// The width of the imported texture. If Resize property is set to true then texture will be resized during the import to this value during the import, otherwise it will be ignored.
|
||||
API_FIELD(Attributes="HideInEditor")
|
||||
int32 SizeX = 1024;
|
||||
|
||||
// The height of the imported texture. If Resize property is set to true then texture will be resized during the import to this value. Otherwise it will be ignored.
|
||||
// The height of the imported texture. If Resize property is set to true then texture will be resized during the import to this value during the import, otherwise it will be ignored.
|
||||
API_FIELD(Attributes="HideInEditor")
|
||||
int32 SizeY = 1024;
|
||||
|
||||
@@ -85,7 +85,7 @@ API_CLASS(Namespace="FlaxEngine.Tools", Static) class FLAXENGINE_API TextureTool
|
||||
API_FIELD(Attributes="EditorOrder(210), VisibleIf(\"PreserveAlphaCoverage\")")
|
||||
float PreserveAlphaCoverageReference = 0.5f;
|
||||
|
||||
// Texture group for streaming (negative if unused). See Streaming Settings.
|
||||
// The texture group for streaming (negative if unused). See Streaming Settings.
|
||||
API_FIELD(Attributes="EditorOrder(300), CustomEditorAlias(\"FlaxEditor.CustomEditors.Dedicated.TextureGroupEditor\")")
|
||||
int32 TextureGroup = -1;
|
||||
|
||||
|
||||
@@ -198,8 +198,12 @@ namespace FlaxEngine.GUI
|
||||
/// <inheritdoc />
|
||||
public override void Update(float deltaTime)
|
||||
{
|
||||
// UI navigation
|
||||
// Update navigation
|
||||
if (SkipEvents)
|
||||
{
|
||||
_navigationHeldTimeUp = _navigationHeldTimeDown = _navigationHeldTimeLeft = _navigationHeldTimeRight = 0;
|
||||
_navigationRateTimeUp = _navigationRateTimeDown = _navigationRateTimeLeft = _navigationRateTimeRight = 0;
|
||||
}
|
||||
{
|
||||
UpdateNavigation(deltaTime, _canvas.NavigateUp.Name, NavDirection.Up, ref _navigationHeldTimeUp, ref _navigationRateTimeUp);
|
||||
UpdateNavigation(deltaTime, _canvas.NavigateDown.Name, NavDirection.Down, ref _navigationHeldTimeDown, ref _navigationRateTimeDown);
|
||||
@@ -207,11 +211,6 @@ namespace FlaxEngine.GUI
|
||||
UpdateNavigation(deltaTime, _canvas.NavigateRight.Name, NavDirection.Right, ref _navigationHeldTimeRight, ref _navigationRateTimeRight);
|
||||
UpdateNavigation(deltaTime, _canvas.NavigateSubmit.Name, ref _navigationHeldTimeSubmit, ref _navigationRateTimeSubmit, SubmitFocused);
|
||||
}
|
||||
else
|
||||
{
|
||||
_navigationHeldTimeUp = _navigationHeldTimeDown = _navigationHeldTimeLeft = _navigationHeldTimeRight = 0;
|
||||
_navigationRateTimeUp = _navigationRateTimeDown = _navigationRateTimeLeft = _navigationRateTimeRight = 0;
|
||||
}
|
||||
|
||||
base.Update(deltaTime);
|
||||
}
|
||||
|
||||
@@ -22,12 +22,28 @@ namespace FlaxEngine.GUI
|
||||
/// Occurs when popup lost focus.
|
||||
/// </summary>
|
||||
public Action LostFocus;
|
||||
|
||||
/// <summary>
|
||||
/// The selected control. Used to scroll to the control on popup creation.
|
||||
/// </summary>
|
||||
public ContainerControl SelectedControl = null;
|
||||
|
||||
/// <summary>
|
||||
/// The main panel used to hold the items.
|
||||
/// </summary>
|
||||
public Panel MainPanel = null;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnEndContainsFocus()
|
||||
{
|
||||
base.OnEndContainsFocus();
|
||||
|
||||
|
||||
// Dont lose focus when using panel. Does prevent LostFocus even from being called if clicking inside of the panel.
|
||||
if (MainPanel != null && MainPanel.IsMouseOver && !MainPanel.ContainsFocus)
|
||||
{
|
||||
MainPanel.Focus();
|
||||
return;
|
||||
}
|
||||
// Call event after this 'focus contains flag' propagation ends to prevent focus issues
|
||||
if (LostFocus != null)
|
||||
Scripting.RunOnUpdate(LostFocus);
|
||||
@@ -125,6 +141,8 @@ namespace FlaxEngine.GUI
|
||||
public override void OnDestroy()
|
||||
{
|
||||
LostFocus = null;
|
||||
MainPanel = null;
|
||||
SelectedControl = null;
|
||||
|
||||
base.OnDestroy();
|
||||
}
|
||||
@@ -233,6 +251,18 @@ namespace FlaxEngine.GUI
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether to show all of the items.
|
||||
/// </summary>
|
||||
[EditorOrder(3), Tooltip("Whether to show all of the items in the drop down.")]
|
||||
public bool ShowAllItems { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the maximum number of items to show at once. Only used if ShowAllItems is false.
|
||||
/// </summary>
|
||||
[EditorOrder(4), VisibleIf(nameof(ShowAllItems), true), Limit(1), Tooltip("The number of items to show in the drop down.")]
|
||||
public int ShowMaxItemsCount { get; set; } = 5;
|
||||
|
||||
/// <summary>
|
||||
/// Event fired when selected index gets changed.
|
||||
/// </summary>
|
||||
@@ -411,13 +441,24 @@ namespace FlaxEngine.GUI
|
||||
|
||||
// TODO: support item templates
|
||||
|
||||
var container = new VerticalPanel
|
||||
var panel = new Panel
|
||||
{
|
||||
AnchorPreset = AnchorPresets.StretchAll,
|
||||
BackgroundColor = BackgroundColor,
|
||||
AutoSize = false,
|
||||
ScrollBars = ScrollBars.Vertical,
|
||||
AutoFocus = true,
|
||||
Parent = popup,
|
||||
};
|
||||
popup.MainPanel = panel;
|
||||
|
||||
var container = new VerticalPanel
|
||||
{
|
||||
AnchorPreset = AnchorPresets.StretchAll,
|
||||
BackgroundColor = Color.Transparent,
|
||||
IsScrollable = true,
|
||||
AutoSize = true,
|
||||
Parent = panel,
|
||||
};
|
||||
var border = new Border
|
||||
{
|
||||
BorderColor = BorderColorHighlighted,
|
||||
@@ -482,10 +523,20 @@ namespace FlaxEngine.GUI
|
||||
//AnchorPreset = AnchorPresets.VerticalStretchLeft,
|
||||
Parent = item,
|
||||
};
|
||||
popup.SelectedControl = item;
|
||||
}
|
||||
}
|
||||
|
||||
popup.Size = new Float2(itemsWidth, height);
|
||||
if (ShowAllItems || _items.Count < ShowMaxItemsCount)
|
||||
{
|
||||
popup.Size = new Float2(itemsWidth, height);
|
||||
panel.Size = popup.Size;
|
||||
}
|
||||
else
|
||||
{
|
||||
popup.Size = new Float2(itemsWidth, (itemsHeight + container.Spacing) * ShowMaxItemsCount);
|
||||
panel.Size = popup.Size;
|
||||
}
|
||||
|
||||
return popup;
|
||||
}
|
||||
@@ -527,7 +578,16 @@ namespace FlaxEngine.GUI
|
||||
/// </summary>
|
||||
public void ShowPopup()
|
||||
{
|
||||
var root = Root;
|
||||
// Find canvas scalar and set as root if it exists.
|
||||
ContainerControl c = Parent;
|
||||
while(c.Parent != Root && c.Parent != null)
|
||||
{
|
||||
c = c.Parent;
|
||||
if (c is CanvasScaler scalar)
|
||||
break;
|
||||
}
|
||||
var root = c is CanvasScaler ? c : Root;
|
||||
|
||||
if (_items.Count == 0 || root == null)
|
||||
return;
|
||||
|
||||
@@ -542,7 +602,7 @@ namespace FlaxEngine.GUI
|
||||
// Show dropdown popup
|
||||
var locationRootSpace = Location + new Float2(0, Height);
|
||||
var parent = Parent;
|
||||
while (parent != null && parent != Root)
|
||||
while (parent != null && parent != root)
|
||||
{
|
||||
locationRootSpace = parent.PointToParent(ref locationRootSpace);
|
||||
parent = parent.Parent;
|
||||
@@ -551,6 +611,8 @@ namespace FlaxEngine.GUI
|
||||
_popup.Parent = root;
|
||||
_popup.Focus();
|
||||
_popup.StartMouseCapture();
|
||||
if (_popup.SelectedControl != null && _popup.MainPanel != null)
|
||||
_popup.MainPanel.ScrollViewTo(_popup.SelectedControl, true);
|
||||
OnPopupShow();
|
||||
}
|
||||
|
||||
|
||||
@@ -104,6 +104,12 @@ namespace FlaxEngine.GUI
|
||||
[EditorOrder(110)]
|
||||
public Color ForegroundDisabled;
|
||||
|
||||
/// <summary>
|
||||
/// The foreground color in viewports (usually have a dark background)
|
||||
/// </summary>
|
||||
[EditorOrder(115)]
|
||||
public Color ForegroundViewport;
|
||||
|
||||
/// <summary>
|
||||
/// The background highlighted color.
|
||||
/// </summary>
|
||||
|
||||
@@ -8,7 +8,6 @@ using System.IO;
|
||||
using FlaxEditor.Scripting;
|
||||
using FlaxEditor.Utilities;
|
||||
#endif
|
||||
using FlaxEngine;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace FlaxEngine.Utilities
|
||||
@@ -18,6 +17,78 @@ namespace FlaxEngine.Utilities
|
||||
/// </summary>
|
||||
public static class VariantUtils
|
||||
{
|
||||
/// <summary>
|
||||
/// Casts the generic value to a given type. Matches native Variant casting logic that favors returning null/zero value rather than error.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The destination value type.</typeparam>
|
||||
/// <param name="value">The input value to cast.</param>
|
||||
/// <returns>The result value.</returns>
|
||||
internal static T Cast<T>(object value)
|
||||
{
|
||||
if (value == null)
|
||||
return default;
|
||||
var type = value.GetType();
|
||||
if (type != typeof(T))
|
||||
{
|
||||
if (typeof(T) == typeof(Vector2))
|
||||
{
|
||||
if (value is Float2 asFloat2)
|
||||
return (T)(object)new Vector2(asFloat2.X, asFloat2.Y);
|
||||
if (value is Float3 asFloat3)
|
||||
return (T)(object)new Vector2(asFloat3.X, asFloat3.Y);
|
||||
if (value is Float4 asFloat4)
|
||||
return (T)(object)new Vector2(asFloat4.X, asFloat4.Y);
|
||||
}
|
||||
else if (typeof(T) == typeof(Vector3))
|
||||
{
|
||||
if (value is Float2 asFloat2)
|
||||
return (T)(object)new Vector3(asFloat2.X, asFloat2.Y, 0);
|
||||
if (value is Float3 asFloat3)
|
||||
return (T)(object)new Vector3(asFloat3.X, asFloat3.Y, asFloat3.Z);
|
||||
if (value is Float4 asFloat4)
|
||||
return (T)(object)new Vector3(asFloat4.X, asFloat4.Y, asFloat4.Z);
|
||||
}
|
||||
else if (typeof(T) == typeof(Vector4))
|
||||
{
|
||||
if (value is Float2 asFloat2)
|
||||
return (T)(object)new Vector4(asFloat2.X, asFloat2.Y, 0, 0);
|
||||
if (value is Float3 asFloat3)
|
||||
return (T)(object)new Vector4(asFloat3.X, asFloat3.Y, asFloat3.Z, 0);
|
||||
if (value is Vector4 asFloat4)
|
||||
return (T)(object)new Vector4(asFloat4.X, asFloat4.Y, asFloat4.Z, asFloat4.W);
|
||||
}
|
||||
else if (typeof(T) == typeof(Float2))
|
||||
{
|
||||
if (value is Vector2 asVector2)
|
||||
return (T)(object)new Float2(asVector2.X, asVector2.Y);
|
||||
if (value is Vector3 asVector3)
|
||||
return (T)(object)new Float2(asVector3.X, asVector3.Y);
|
||||
if (value is Vector4 asVector4)
|
||||
return (T)(object)new Float2(asVector4.X, asVector4.Y);
|
||||
}
|
||||
else if (typeof(T) == typeof(Float3))
|
||||
{
|
||||
if (value is Vector2 asVector2)
|
||||
return (T)(object)new Float3(asVector2.X, asVector2.Y, 0);
|
||||
if (value is Vector3 asVector3)
|
||||
return (T)(object)new Float3(asVector3.X, asVector3.Y, asVector3.Z);
|
||||
if (value is Vector4 asFloat4)
|
||||
return (T)(object)new Float3(asFloat4.X, asFloat4.Y, asFloat4.Z);
|
||||
}
|
||||
else if (typeof(T) == typeof(Float4))
|
||||
{
|
||||
if (value is Vector2 asVector2)
|
||||
return (T)(object)new Float4(asVector2.X, asVector2.Y, 0, 0);
|
||||
if (value is Vector3 asVector3)
|
||||
return (T)(object)new Float4(asVector3.X, asVector3.Y, asVector3.Z, 0);
|
||||
if (value is Vector4 asVector4)
|
||||
return (T)(object)new Float4(asVector4.X, asVector4.Y, asVector4.Z, asVector4.W);
|
||||
}
|
||||
return (T)Convert.ChangeType(value, typeof(T));
|
||||
}
|
||||
return (T)value;
|
||||
}
|
||||
|
||||
internal enum VariantType
|
||||
{
|
||||
Null = 0,
|
||||
|
||||
@@ -685,7 +685,7 @@ void VisjectExecutor::ProcessGroupPacking(Box* box, Node* node, Value& value)
|
||||
case 36:
|
||||
{
|
||||
// Get value with structure data
|
||||
const Variant structureValue = eatBox(node, node->GetBox(0)->FirstConnection());
|
||||
Variant structureValue = eatBox(node, node->GetBox(0)->FirstConnection());
|
||||
if (!node->GetBox(0)->HasConnection())
|
||||
return;
|
||||
|
||||
@@ -741,7 +741,9 @@ void VisjectExecutor::ProcessGroupPacking(Box* box, Node* node, Value& value)
|
||||
return;
|
||||
}
|
||||
const ScriptingType& type = typeHandle.GetType();
|
||||
if (structureValue.Type.Type != VariantType::Structure || StringUtils::Compare(typeNameAnsi.Get(), structureValue.Type.TypeName) != 0)
|
||||
structureValue.InvertInline(); // Extract any Float3/Int32 into Structure type from inlined format
|
||||
const ScriptingTypeHandle structureValueTypeHandle = Scripting::FindScriptingType(structureValue.Type.GetTypeName());
|
||||
if (structureValue.Type.Type != VariantType::Structure || typeHandle != structureValueTypeHandle)
|
||||
{
|
||||
OnError(node, box, String::Format(TEXT("Cannot unpack value of type {0} to structure of type {1}"), structureValue.Type, typeName));
|
||||
return;
|
||||
|
||||
@@ -1793,6 +1793,18 @@ namespace Flax.Build.Bindings
|
||||
var apiTypeInfo = FindApiTypeInfo(buildData, typeInfo, caller);
|
||||
if (apiTypeInfo != null && apiTypeInfo.IsInterface)
|
||||
return true;
|
||||
|
||||
// Add includes to properly compile bindings (eg. SoftObjectReference<class Texture>)
|
||||
CppReferencesFiles.Add(apiTypeInfo?.File);
|
||||
if (typeInfo.GenericArgs != null)
|
||||
{
|
||||
for (int i = 0; i < typeInfo.GenericArgs.Count; i++)
|
||||
{
|
||||
var t = FindApiTypeInfo(buildData, typeInfo.GenericArgs[i], caller);
|
||||
CppReferencesFiles.Add(t?.File);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user