Merge remote-tracking branch 'origin/master' into dotnet7
This commit is contained in:
@@ -323,6 +323,7 @@
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=reverb/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=rigidbodies/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=rigidbody/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Scaler/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=scanline/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=scanlines/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Scarlett/@EntryIndexedValue">True</s:Boolean>
|
||||
|
||||
@@ -262,6 +262,13 @@ namespace FlaxEditor.Content.Import
|
||||
[EditorOrder(1050), DefaultValue(true)]
|
||||
public bool OptimizeKeyframes { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// If checked, the importer will import scale animation tracks (otherwise scale animation will be ignored).
|
||||
/// </summary>
|
||||
[EditorDisplay("Animation"), VisibleIf(nameof(ShowAnimation))]
|
||||
[EditorOrder(1055), DefaultValue(false)]
|
||||
public bool ImportScaleTracks { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// Enables root motion extraction support from this animation.
|
||||
/// </summary>
|
||||
@@ -390,6 +397,7 @@ namespace FlaxEditor.Content.Import
|
||||
public float SamplingRate;
|
||||
public byte SkipEmptyCurves;
|
||||
public byte OptimizeKeyframes;
|
||||
public byte ImportScaleTracks;
|
||||
public byte EnableRootMotion;
|
||||
public string RootNodeName;
|
||||
|
||||
@@ -599,6 +607,7 @@ namespace FlaxEditor.Content.Import
|
||||
SamplingRate = SamplingRate,
|
||||
SkipEmptyCurves = (byte)(SkipEmptyCurves ? 1 : 0),
|
||||
OptimizeKeyframes = (byte)(OptimizeKeyframes ? 1 : 0),
|
||||
ImportScaleTracks = (byte)(ImportScaleTracks ? 1 : 0),
|
||||
EnableRootMotion = (byte)(EnableRootMotion ? 1 : 0),
|
||||
RootNodeName = RootNodeName,
|
||||
GenerateLODs = (byte)(GenerateLODs ? 1 : 0),
|
||||
@@ -640,6 +649,7 @@ namespace FlaxEditor.Content.Import
|
||||
SamplingRate = options.SamplingRate;
|
||||
SkipEmptyCurves = options.SkipEmptyCurves != 0;
|
||||
OptimizeKeyframes = options.OptimizeKeyframes != 0;
|
||||
ImportScaleTracks = options.ImportScaleTracks != 0;
|
||||
EnableRootMotion = options.EnableRootMotion != 0;
|
||||
RootNodeName = options.RootNodeName;
|
||||
GenerateLODs = options.GenerateLODs != 0;
|
||||
|
||||
@@ -40,6 +40,11 @@ namespace FlaxEditor.CustomEditors
|
||||
[HideInEditor]
|
||||
public abstract class CustomEditor
|
||||
{
|
||||
/// <summary>
|
||||
/// True if Editor is during value setting (eg. by user or from copy/paste).
|
||||
/// </summary>
|
||||
public static bool IsSettingValue = false;
|
||||
|
||||
private LayoutElementsContainer _layout;
|
||||
private CustomEditorPresenter _presenter;
|
||||
private CustomEditor _parent;
|
||||
@@ -266,23 +271,30 @@ namespace FlaxEditor.CustomEditors
|
||||
// Check if need to update value
|
||||
if (_hasValueDirty)
|
||||
{
|
||||
// Cleanup (won't retry update in case of exception)
|
||||
object val = _valueToSet;
|
||||
_hasValueDirty = false;
|
||||
_valueToSet = null;
|
||||
|
||||
// Assign value
|
||||
SynchronizeValue(val);
|
||||
|
||||
// Propagate values up (eg. when member of structure gets modified, also structure should be updated as a part of the other object)
|
||||
var obj = _parent;
|
||||
while (obj._parent != null && !(obj._parent is SyncPointEditor))
|
||||
IsSettingValue = true;
|
||||
try
|
||||
{
|
||||
obj.Values.Set(obj._parent.Values, obj.Values);
|
||||
obj = obj._parent;
|
||||
}
|
||||
// Cleanup (won't retry update in case of exception)
|
||||
object val = _valueToSet;
|
||||
_hasValueDirty = false;
|
||||
_valueToSet = null;
|
||||
|
||||
OnUnDirty();
|
||||
// Assign value
|
||||
SynchronizeValue(val);
|
||||
|
||||
// Propagate values up (eg. when member of structure gets modified, also structure should be updated as a part of the other object)
|
||||
var obj = _parent;
|
||||
while (obj._parent != null && !(obj._parent is SyncPointEditor))
|
||||
{
|
||||
obj.Values.Set(obj._parent.Values, obj.Values);
|
||||
obj = obj._parent;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
OnUnDirty();
|
||||
IsSettingValue = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -645,7 +645,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
}
|
||||
cm.ItemClicked += controlType => SetType((ScriptType)controlType.Tag);
|
||||
cm.SortItems();
|
||||
cm.Show(button.Parent, button.BottomLeft);
|
||||
cm.Show(button.Parent, button.BottomLeft - new Float2((cm.Width - button.Width) / 2, 0));
|
||||
}
|
||||
|
||||
private void SetType(ref ScriptType controlType, UIControl uiControl)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
|
||||
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
|
||||
namespace FlaxEditor.CustomEditors.Editors
|
||||
{
|
||||
@@ -77,11 +78,36 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
/// <seealso cref="FlaxEditor.CustomEditors.Editors.Float3Editor" />
|
||||
public class ScaleEditor : Float3Editor
|
||||
{
|
||||
private Image _linkImage;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Initialize(LayoutElementsContainer layout)
|
||||
{
|
||||
base.Initialize(layout);
|
||||
|
||||
LinkValues = Editor.Instance.Windows.PropertiesWin.ScaleLinked;
|
||||
|
||||
_linkImage = new Image
|
||||
{
|
||||
Parent = LinkedLabel,
|
||||
Width = 18,
|
||||
Height = 18,
|
||||
Brush = LinkValues ? new SpriteBrush(Editor.Instance.Icons.Link32) : new SpriteBrush(),
|
||||
AnchorPreset = AnchorPresets.TopLeft,
|
||||
TooltipText = "Scale values are linked together.",
|
||||
};
|
||||
_linkImage.LocalX += 40;
|
||||
_linkImage.LocalY += 1;
|
||||
|
||||
LinkedLabel.SetupContextMenu += (label, menu, editor) =>
|
||||
{
|
||||
menu.AddSeparator();
|
||||
if (LinkValues)
|
||||
menu.AddButton("Unlink", ToggleLink).LinkTooltip("Unlinks scale components from uniform scaling");
|
||||
else
|
||||
menu.AddButton("Link", ToggleLink).LinkTooltip("Links scale components for uniform scaling");
|
||||
};
|
||||
|
||||
// Override colors
|
||||
var back = FlaxEngine.GUI.Style.Current.TextBoxBackground;
|
||||
var grayOutFactor = 0.6f;
|
||||
@@ -92,6 +118,16 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
ZElement.ValueBox.BorderColor = Color.Lerp(AxisColorZ, back, grayOutFactor);
|
||||
ZElement.ValueBox.BorderSelectedColor = AxisColorZ;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Toggles the linking functionality.
|
||||
/// </summary>
|
||||
public void ToggleLink()
|
||||
{
|
||||
LinkValues = !LinkValues;
|
||||
Editor.Instance.Windows.PropertiesWin.ScaleLinked = LinkValues;
|
||||
_linkImage.Brush = LinkValues ? new SpriteBrush(Editor.Instance.Icons.Link32) : new SpriteBrush();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,7 +72,7 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
var customType = TypeUtils.GetType(assetReference.TypeName);
|
||||
if (customType != ScriptType.Null)
|
||||
assetType = customType;
|
||||
else
|
||||
else if (!Content.Settings.GameSettings.OptionalPlatformSettings.Contains(assetReference.TypeName))
|
||||
Debug.LogWarning(string.Format("Unknown asset type '{0}' to use for asset picker filter.", assetReference.TypeName));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,6 +44,20 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
/// <inheritdoc />
|
||||
public override DisplayStyle Style => DisplayStyle.Inline;
|
||||
|
||||
/// <summary>
|
||||
/// If true, when one value is changed, the other 2 will change as well.
|
||||
/// </summary>
|
||||
public bool LinkValues = false;
|
||||
|
||||
private enum ValueChanged
|
||||
{
|
||||
X = 0,
|
||||
Y = 1,
|
||||
Z = 2
|
||||
}
|
||||
|
||||
private ValueChanged _valueChanged;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Initialize(LayoutElementsContainer layout)
|
||||
{
|
||||
@@ -63,28 +77,83 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
|
||||
XElement = grid.FloatValue();
|
||||
XElement.SetLimits(limit);
|
||||
XElement.ValueBox.ValueChanged += OnValueChanged;
|
||||
XElement.ValueBox.ValueChanged += OnXValueChanged;
|
||||
XElement.ValueBox.SlidingEnd += ClearToken;
|
||||
|
||||
YElement = grid.FloatValue();
|
||||
YElement.SetLimits(limit);
|
||||
YElement.ValueBox.ValueChanged += OnValueChanged;
|
||||
YElement.ValueBox.ValueChanged += OnYValueChanged;
|
||||
YElement.ValueBox.SlidingEnd += ClearToken;
|
||||
|
||||
ZElement = grid.FloatValue();
|
||||
ZElement.SetLimits(limit);
|
||||
ZElement.ValueBox.ValueChanged += OnValueChanged;
|
||||
ZElement.ValueBox.ValueChanged += OnZValueChanged;
|
||||
ZElement.ValueBox.SlidingEnd += ClearToken;
|
||||
}
|
||||
|
||||
private void OnXValueChanged()
|
||||
{
|
||||
if (IsSetBlocked)
|
||||
return;
|
||||
if (LinkValues)
|
||||
_valueChanged = ValueChanged.X;
|
||||
OnValueChanged();
|
||||
}
|
||||
|
||||
private void OnYValueChanged()
|
||||
{
|
||||
if (IsSetBlocked)
|
||||
return;
|
||||
if (LinkValues)
|
||||
_valueChanged = ValueChanged.Y;
|
||||
OnValueChanged();
|
||||
}
|
||||
|
||||
private void OnZValueChanged()
|
||||
{
|
||||
if (IsSetBlocked)
|
||||
return;
|
||||
if (LinkValues)
|
||||
_valueChanged = ValueChanged.Z;
|
||||
OnValueChanged();
|
||||
}
|
||||
|
||||
private void OnValueChanged()
|
||||
{
|
||||
if (IsSetBlocked)
|
||||
return;
|
||||
|
||||
var xValue = XElement.ValueBox.Value;
|
||||
var yValue = YElement.ValueBox.Value;
|
||||
var zValue = ZElement.ValueBox.Value;
|
||||
|
||||
if (LinkValues)
|
||||
{
|
||||
var valueChange = 0.0f;
|
||||
switch (_valueChanged)
|
||||
{
|
||||
case ValueChanged.X:
|
||||
valueChange = xValue - ((Float3)Values[0]).X;
|
||||
yValue += valueChange;
|
||||
zValue += valueChange;
|
||||
break;
|
||||
case ValueChanged.Y:
|
||||
valueChange = yValue - ((Float3)Values[0]).Y;
|
||||
xValue += valueChange;
|
||||
zValue += valueChange;
|
||||
break;
|
||||
case ValueChanged.Z:
|
||||
valueChange = zValue - ((Float3)Values[0]).Z;
|
||||
xValue += valueChange;
|
||||
yValue += valueChange;
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
var isSliding = XElement.IsSliding || YElement.IsSliding || ZElement.IsSliding;
|
||||
var token = isSliding ? this : null;
|
||||
var value = new Float3(XElement.ValueBox.Value, YElement.ValueBox.Value, ZElement.ValueBox.Value);
|
||||
var value = new Float3(xValue, yValue, zValue);
|
||||
object v = Values[0];
|
||||
if (v is Vector3)
|
||||
v = (Vector3)value;
|
||||
|
||||
@@ -198,7 +198,7 @@ namespace FlaxEditor.CustomEditors
|
||||
{
|
||||
for (int i = 0; i < Count; i++)
|
||||
{
|
||||
if (!Equals(this[i], _referenceValue))
|
||||
if (!ValueEquals(this[i], _referenceValue))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -228,20 +228,23 @@ namespace FlaxEditor.CustomEditors
|
||||
{
|
||||
for (int i = 0; i < Count; i++)
|
||||
{
|
||||
if (!Equals(this[i], _defaultValue))
|
||||
{
|
||||
// Special case for String (null string is kind of equal to empty string from the user perspective)
|
||||
if (this[i] == null && _defaultValue is string defaultValueStr && defaultValueStr.Length == 0)
|
||||
continue;
|
||||
|
||||
if (!ValueEquals(this[i], _defaultValue))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool ValueEquals(object objA, object objB)
|
||||
{
|
||||
// Special case for String (null string is kind of equal to empty string from the user perspective)
|
||||
if (objA == null && objB is string objBStr && objBStr.Length == 0)
|
||||
return true;
|
||||
|
||||
return Newtonsoft.Json.Utilities.MiscellaneousUtils.DefaultValueEquals(objA, objB);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ValueContainer"/> class.
|
||||
/// </summary>
|
||||
|
||||
@@ -181,6 +181,7 @@ namespace FlaxEditor.GUI.Input
|
||||
_cursorChanged = false;
|
||||
}
|
||||
SlidingEnd?.Invoke();
|
||||
Defocus();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -183,6 +183,7 @@ struct InternalModelOptions
|
||||
float SamplingRate;
|
||||
byte SkipEmptyCurves;
|
||||
byte OptimizeKeyframes;
|
||||
byte ImportScaleTracks;
|
||||
byte EnableRootMotion;
|
||||
MonoString* RootNodeName;
|
||||
|
||||
@@ -231,6 +232,7 @@ struct InternalModelOptions
|
||||
to->SamplingRate = from->SamplingRate;
|
||||
to->SkipEmptyCurves = from->SkipEmptyCurves;
|
||||
to->OptimizeKeyframes = from->OptimizeKeyframes;
|
||||
to->ImportScaleTracks = from->ImportScaleTracks;
|
||||
to->EnableRootMotion = from->EnableRootMotion;
|
||||
to->RootNodeName = MUtils::ToString(from->RootNodeName);
|
||||
to->GenerateLODs = from->GenerateLODs;
|
||||
@@ -272,6 +274,7 @@ struct InternalModelOptions
|
||||
to->SamplingRate = from->SamplingRate;
|
||||
to->SkipEmptyCurves = from->SkipEmptyCurves;
|
||||
to->OptimizeKeyframes = from->OptimizeKeyframes;
|
||||
to->ImportScaleTracks = from->ImportScaleTracks;
|
||||
to->EnableRootMotion = from->EnableRootMotion;
|
||||
to->RootNodeName = MUtils::ToString(from->RootNodeName);
|
||||
to->GenerateLODs = from->GenerateLODs;
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using FlaxEngine;
|
||||
@@ -365,7 +366,8 @@ namespace FlaxEditor.Scripting
|
||||
}
|
||||
}
|
||||
|
||||
Editor.LogWarning($"Failed to find type '{typeName}'.");
|
||||
if (!Content.Settings.GameSettings.OptionalPlatformSettings.Contains(typeName))
|
||||
Editor.LogWarning($"Failed to find type '{typeName}'.");
|
||||
return ScriptType.Null;
|
||||
}
|
||||
|
||||
|
||||
@@ -82,6 +82,16 @@ namespace FlaxEditor.States
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// True if play mode is starting.
|
||||
/// </summary>
|
||||
public bool IsPlayModeStarting;
|
||||
|
||||
/// <summary>
|
||||
/// True if play mode is ending.
|
||||
/// </summary>
|
||||
public bool IsPlayModeEnding;
|
||||
|
||||
internal PlayingState(Editor editor)
|
||||
: base(editor)
|
||||
{
|
||||
@@ -127,6 +137,7 @@ namespace FlaxEditor.States
|
||||
public override void OnEnter()
|
||||
{
|
||||
Profiler.BeginEvent("PlayingState.OnEnter");
|
||||
IsPlayModeStarting = true;
|
||||
Editor.OnPlayBeginning();
|
||||
|
||||
CacheSelection();
|
||||
@@ -150,6 +161,7 @@ namespace FlaxEditor.States
|
||||
RestoreSelection();
|
||||
|
||||
Editor.OnPlayBegin();
|
||||
IsPlayModeStarting = false;
|
||||
Profiler.EndEvent();
|
||||
}
|
||||
|
||||
@@ -171,6 +183,7 @@ namespace FlaxEditor.States
|
||||
public override void OnExit(State nextState)
|
||||
{
|
||||
Profiler.BeginEvent("PlayingState.OnExit");
|
||||
IsPlayModeEnding = true;
|
||||
Editor.OnPlayEnding();
|
||||
IsPaused = true;
|
||||
|
||||
@@ -194,6 +207,7 @@ namespace FlaxEditor.States
|
||||
RestoreSelection();
|
||||
|
||||
Editor.OnPlayEnd();
|
||||
IsPlayModeEnding = false;
|
||||
Profiler.EndEvent();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -114,10 +114,8 @@ namespace FlaxEditor.Windows.Assets
|
||||
public override DragDropEffect OnDragMove(ref Float2 location, DragData data)
|
||||
{
|
||||
var result = base.OnDragMove(ref location, data);
|
||||
if (result == DragDropEffect.None)
|
||||
{
|
||||
if (result == DragDropEffect.None && _dragHandlers != null)
|
||||
result = _dragHandlers.Effect;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -817,6 +817,7 @@ namespace FlaxEditor.Windows
|
||||
|
||||
// Selected UI controls outline
|
||||
bool drawAnySelectedControl = false;
|
||||
// TODO: optimize this (eg. cache list of selected UIControl's when selection gets changed)
|
||||
for (var i = 0; i < Editor.Instance.SceneEditing.Selection.Count; i++)
|
||||
{
|
||||
if (Editor.Instance.SceneEditing.Selection[i].EditableObject is UIControl controlActor && controlActor && controlActor.Control != null)
|
||||
@@ -827,7 +828,8 @@ namespace FlaxEditor.Windows
|
||||
Render2D.PushTransform(ref _viewport._cachedTransform);
|
||||
}
|
||||
var control = controlActor.Control;
|
||||
var bounds = Rectangle.FromPoints(control.PointToParent(_viewport, Float2.Zero), control.PointToParent(_viewport, control.Size));
|
||||
var bounds = control.EditorBounds;
|
||||
bounds = Rectangle.FromPoints(control.PointToParent(_viewport, bounds.Location), control.PointToParent(_viewport, bounds.Size));
|
||||
Render2D.DrawRectangle(bounds, Editor.Instance.Options.Options.Visual.SelectionOutlineColor0, Editor.Instance.Options.Options.Visual.UISelectionOutlineSize);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml;
|
||||
using FlaxEditor.CustomEditors;
|
||||
using FlaxEngine.GUI;
|
||||
|
||||
@@ -16,11 +17,19 @@ namespace FlaxEditor.Windows
|
||||
{
|
||||
private IEnumerable<object> undoRecordObjects;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool UseLayoutData => true;
|
||||
|
||||
/// <summary>
|
||||
/// The editor.
|
||||
/// </summary>
|
||||
public readonly CustomEditorPresenter Presenter;
|
||||
|
||||
/// <summary>
|
||||
/// Indication of if the scale is locked.
|
||||
/// </summary>
|
||||
public bool ScaleLinked = false;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="PropertiesWindow"/> class.
|
||||
/// </summary>
|
||||
@@ -52,5 +61,18 @@ namespace FlaxEditor.Windows
|
||||
var objects = Editor.SceneEditing.Selection.ConvertAll(x => x.EditableObject).Distinct();
|
||||
Presenter.Select(objects);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnLayoutSerialize(XmlWriter writer)
|
||||
{
|
||||
writer.WriteAttributeString("ScaleLinked", ScaleLinked.ToString());
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnLayoutDeserialize(XmlElement node)
|
||||
{
|
||||
if (bool.TryParse(node.GetAttribute("ScaleLinked"), out bool value1))
|
||||
ScaleLinked = value1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1070,7 +1070,8 @@ bool findAsset(const Guid& id, const String& directory, Array<String>& tmpCache,
|
||||
tmpCache.Clear();
|
||||
if (FileSystem::DirectoryGetFiles(tmpCache, directory, TEXT("*"), DirectorySearchOption::AllDirectories))
|
||||
{
|
||||
LOG(Error, "Cannot query files in folder '{0}'.", directory);
|
||||
if (FileSystem::DirectoryExists(directory))
|
||||
LOG(Error, "Cannot query files in folder '{0}'.", directory);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -163,7 +163,12 @@ bool CreateAssetContext::AllocateChunk(int32 index)
|
||||
void CreateAssetContext::AddMeta(JsonWriter& writer) const
|
||||
{
|
||||
writer.JKEY("ImportPath");
|
||||
if (AssetsImportingManager::UseImportPathRelative && !FileSystem::IsRelative(InputPath))
|
||||
if (AssetsImportingManager::UseImportPathRelative && !FileSystem::IsRelative(InputPath)
|
||||
#if PLATFORM_WINDOWS
|
||||
// Import path from other drive should be stored as absolute on Windows to prevent issues
|
||||
&& InputPath.Length() > 2 && Globals::ProjectFolder.Length() > 2 && InputPath[0] == Globals::ProjectFolder[0]
|
||||
#endif
|
||||
)
|
||||
{
|
||||
const String relativePath = FileSystem::ConvertAbsolutePathToRelative(Globals::ProjectFolder, InputPath);
|
||||
writer.String(relativePath);
|
||||
|
||||
@@ -15,6 +15,16 @@ namespace FlaxEditor.Content.Settings
|
||||
internal const string XboxOnePlatformSettingsTypename = "FlaxEditor.Content.Settings.XboxOnePlatformSettings";
|
||||
internal const string XboxScarlettPlatformSettingsTypename = "FlaxEditor.Content.Settings.XboxScarlettPlatformSettings";
|
||||
internal const string SwitchPlatformSettingsTypename = "FlaxEditor.Content.Settings.SwitchPlatformSettings";
|
||||
#if FLAX_EDITOR
|
||||
internal static string[] OptionalPlatformSettings =
|
||||
{
|
||||
PS4PlatformSettingsTypename,
|
||||
PS5PlatformSettingsTypename,
|
||||
XboxOnePlatformSettingsTypename,
|
||||
XboxScarlettPlatformSettingsTypename,
|
||||
SwitchPlatformSettingsTypename,
|
||||
};
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// The default application icon.
|
||||
|
||||
@@ -2,22 +2,18 @@
|
||||
|
||||
#include "ObjectsRemovalService.h"
|
||||
#include "Collections/Dictionary.h"
|
||||
#include "Engine/Engine/Time.h"
|
||||
#include "Engine/Engine/EngineService.h"
|
||||
#include "Engine/Threading/Threading.h"
|
||||
#include "Engine/Engine/Time.h"
|
||||
#include "Engine/Profiler/ProfilerCPU.h"
|
||||
#include "Engine/Scripting/ScriptingObject.h"
|
||||
#include "Log.h"
|
||||
|
||||
namespace ObjectsRemovalServiceImpl
|
||||
{
|
||||
bool IsReady = false;
|
||||
CriticalSection PoolLocker;
|
||||
CriticalSection NewItemsLocker;
|
||||
DateTime LastUpdate;
|
||||
float LastUpdateGameTime;
|
||||
Dictionary<Object*, float> Pool(8192);
|
||||
Dictionary<Object*, float> NewItemsPool(2048);
|
||||
}
|
||||
|
||||
using namespace ObjectsRemovalServiceImpl;
|
||||
@@ -39,41 +35,14 @@ ObjectsRemoval ObjectsRemovalInstance;
|
||||
|
||||
bool ObjectsRemovalService::IsInPool(Object* obj)
|
||||
{
|
||||
if (!IsReady)
|
||||
return false;
|
||||
|
||||
{
|
||||
ScopeLock lock(NewItemsLocker);
|
||||
if (NewItemsPool.ContainsKey(obj))
|
||||
return true;
|
||||
}
|
||||
|
||||
{
|
||||
ScopeLock lock(PoolLocker);
|
||||
if (Pool.ContainsKey(obj))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ObjectsRemovalService::HasNewItemsForFlush()
|
||||
{
|
||||
NewItemsLocker.Lock();
|
||||
const bool result = NewItemsPool.HasItems();
|
||||
NewItemsLocker.Unlock();
|
||||
PoolLocker.Lock();
|
||||
const bool result = Pool.ContainsKey(obj);
|
||||
PoolLocker.Unlock();
|
||||
return result;
|
||||
}
|
||||
|
||||
void ObjectsRemovalService::Dereference(Object* obj)
|
||||
{
|
||||
if (!IsReady)
|
||||
return;
|
||||
|
||||
NewItemsLocker.Lock();
|
||||
NewItemsPool.Remove(obj);
|
||||
NewItemsLocker.Unlock();
|
||||
|
||||
PoolLocker.Lock();
|
||||
Pool.Remove(obj);
|
||||
PoolLocker.Unlock();
|
||||
@@ -81,57 +50,37 @@ void ObjectsRemovalService::Dereference(Object* obj)
|
||||
|
||||
void ObjectsRemovalService::Add(Object* obj, float timeToLive, bool useGameTime)
|
||||
{
|
||||
ScopeLock lock(NewItemsLocker);
|
||||
|
||||
obj->Flags |= ObjectFlags::WasMarkedToDelete;
|
||||
if (useGameTime)
|
||||
obj->Flags |= ObjectFlags::UseGameTimeForDelete;
|
||||
else
|
||||
obj->Flags &= ~ObjectFlags::UseGameTimeForDelete;
|
||||
NewItemsPool[obj] = timeToLive;
|
||||
|
||||
PoolLocker.Lock();
|
||||
Pool[obj] = timeToLive;
|
||||
PoolLocker.Unlock();
|
||||
}
|
||||
|
||||
void ObjectsRemovalService::Flush(float dt, float gameDelta)
|
||||
{
|
||||
PROFILE_CPU();
|
||||
|
||||
// Add new items
|
||||
PoolLocker.Lock();
|
||||
|
||||
int32 itemsLeft;
|
||||
do
|
||||
{
|
||||
ScopeLock lock(NewItemsLocker);
|
||||
|
||||
for (auto i = NewItemsPool.Begin(); i.IsNotEnd(); ++i)
|
||||
{
|
||||
Pool[i->Key] = i->Value;
|
||||
}
|
||||
NewItemsPool.Clear();
|
||||
}
|
||||
|
||||
// Update timeouts and delete objects that timed out
|
||||
{
|
||||
ScopeLock lock(PoolLocker);
|
||||
|
||||
// Update timeouts and delete objects that timed out
|
||||
itemsLeft = Pool.Count();
|
||||
for (auto i = Pool.Begin(); i.IsNotEnd(); ++i)
|
||||
{
|
||||
auto obj = i->Key;
|
||||
Object* obj = i->Key;
|
||||
const float ttl = i->Value - ((obj->Flags & ObjectFlags::UseGameTimeForDelete) != ObjectFlags::None ? gameDelta : dt);
|
||||
if (ttl <= ZeroTolerance)
|
||||
if (ttl <= 0.0f)
|
||||
{
|
||||
Pool.Remove(i);
|
||||
|
||||
#if BUILD_DEBUG || BUILD_DEVELOPMENT
|
||||
if (NewItemsPool.ContainsKey(obj))
|
||||
{
|
||||
const auto asScriptingObj = dynamic_cast<ScriptingObject*>(obj);
|
||||
if (asScriptingObj)
|
||||
{
|
||||
LOG(Warning, "Object {0} was marked to delete after delete timeout", asScriptingObj->GetID());
|
||||
}
|
||||
}
|
||||
#endif
|
||||
NewItemsPool.Remove(obj);
|
||||
//ASSERT(!NewItemsPool.ContainsKey(obj));
|
||||
|
||||
obj->OnDeleteObject();
|
||||
itemsLeft--;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -139,38 +88,9 @@ void ObjectsRemovalService::Flush(float dt, float gameDelta)
|
||||
}
|
||||
}
|
||||
}
|
||||
while (itemsLeft != Pool.Count()); // Continue removing if any new item was added during removing (eg. sub-object delete with 0 timeout)
|
||||
|
||||
// Perform removing in loop
|
||||
// Note: objects during OnDeleteObject call can register new objects to remove with timeout=0, for example Actors do that to remove children and scripts
|
||||
while (HasNewItemsForFlush())
|
||||
{
|
||||
// Add new items
|
||||
{
|
||||
ScopeLock lock(NewItemsLocker);
|
||||
|
||||
for (auto i = NewItemsPool.Begin(); i.IsNotEnd(); ++i)
|
||||
{
|
||||
Pool[i->Key] = i->Value;
|
||||
}
|
||||
NewItemsPool.Clear();
|
||||
}
|
||||
|
||||
// Delete objects that timed out
|
||||
{
|
||||
ScopeLock lock(PoolLocker);
|
||||
|
||||
for (auto i = Pool.Begin(); i.IsNotEnd(); ++i)
|
||||
{
|
||||
if (i->Value <= ZeroTolerance)
|
||||
{
|
||||
auto obj = i->Key;
|
||||
Pool.Remove(i);
|
||||
ASSERT(!NewItemsPool.ContainsKey(obj));
|
||||
obj->OnDeleteObject();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
PoolLocker.Unlock();
|
||||
}
|
||||
|
||||
bool ObjectsRemoval::Init()
|
||||
@@ -204,14 +124,12 @@ void ObjectsRemoval::Dispose()
|
||||
ScopeLock lock(PoolLocker);
|
||||
for (auto i = Pool.Begin(); i.IsNotEnd(); ++i)
|
||||
{
|
||||
auto obj = i->Key;
|
||||
Object* obj = i->Key;
|
||||
Pool.Remove(i);
|
||||
obj->OnDeleteObject();
|
||||
}
|
||||
Pool.Clear();
|
||||
}
|
||||
|
||||
IsReady = false;
|
||||
}
|
||||
|
||||
Object::~Object()
|
||||
|
||||
@@ -17,12 +17,6 @@ public:
|
||||
/// <returns>True if object has been registered in the pool for the removing, otherwise false.</returns>
|
||||
static bool IsInPool(Object* obj);
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether any object has been registered to be removed from pool (requests are flushed on Flush call).
|
||||
/// </summary>
|
||||
/// <returns>True if any object has been registered to be removed, otherwise false.</returns>
|
||||
static bool HasNewItemsForFlush();
|
||||
|
||||
/// <summary>
|
||||
/// Removes the specified object from the dead pool (clears the reference to it).
|
||||
/// </summary>
|
||||
|
||||
@@ -10,7 +10,10 @@ namespace FlaxEngine
|
||||
/// </summary>
|
||||
public static class RandomUtil
|
||||
{
|
||||
private static readonly Random _random = new Random();
|
||||
/// <summary>
|
||||
/// Random numbers generator.
|
||||
/// </summary>
|
||||
public static readonly Random Random = new Random();
|
||||
|
||||
/// <summary>
|
||||
/// Generates a pseudo-random number from normalized range [0;1].
|
||||
@@ -19,7 +22,7 @@ namespace FlaxEngine
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static float Rand()
|
||||
{
|
||||
return _random.Next(0, int.MaxValue) / (float)int.MaxValue;
|
||||
return Random.Next(0, int.MaxValue) / (float)int.MaxValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,9 +57,9 @@
|
||||
#define GPU_USE_SHADERS_DEBUG_LAYER (BUILD_DEBUG)
|
||||
|
||||
// Maximum size of the texture that is supported by the engine (specific platforms can have lower limit)
|
||||
#define GPU_MAX_TEXTURE_SIZE 8192
|
||||
#define GPU_MAX_TEXTURE_MIP_LEVELS 14
|
||||
#define GPU_MAX_TEXTURE_ARRAY_SIZE 512
|
||||
#define GPU_MAX_TEXTURE_SIZE 16384
|
||||
#define GPU_MAX_TEXTURE_MIP_LEVELS 15
|
||||
#define GPU_MAX_TEXTURE_ARRAY_SIZE 1024
|
||||
|
||||
// Define default back buffer(s) format
|
||||
#if GPU_USE_BGRA_BACK_BUFFER
|
||||
|
||||
@@ -319,6 +319,15 @@ GPUDevice::~GPUDevice()
|
||||
|
||||
bool GPUDevice::Init()
|
||||
{
|
||||
// Clamp texture limits (eg. if driver reports higher value)
|
||||
Limits.MaximumTexture1DSize = Math::Min(Limits.MaximumTexture1DSize, GPU_MAX_TEXTURE_SIZE);
|
||||
Limits.MaximumTexture2DSize = Math::Min(Limits.MaximumTexture2DSize, GPU_MAX_TEXTURE_SIZE);
|
||||
Limits.MaximumTexture3DSize = Math::Min(Limits.MaximumTexture3DSize, GPU_MAX_TEXTURE_SIZE);
|
||||
Limits.MaximumTextureCubeSize = Math::Min(Limits.MaximumTextureCubeSize, GPU_MAX_TEXTURE_SIZE);
|
||||
Limits.MaximumTexture1DArraySize = Math::Min(Limits.MaximumTexture1DArraySize, GPU_MAX_TEXTURE_ARRAY_SIZE);
|
||||
Limits.MaximumTexture2DArraySize = Math::Min(Limits.MaximumTexture2DArraySize, GPU_MAX_TEXTURE_ARRAY_SIZE);
|
||||
Limits.MaximumMipLevelsCount = Math::Min(Limits.MaximumMipLevelsCount, GPU_MAX_TEXTURE_MIP_LEVELS);
|
||||
|
||||
_res->TasksManager.SetExecutor(CreateTasksExecutor());
|
||||
LOG(Info, "Total graphics memory: {0}", Utilities::BytesToText(TotalGraphicsMemory));
|
||||
return false;
|
||||
|
||||
@@ -21,7 +21,7 @@ namespace
|
||||
|
||||
void RenderTargetPool::Flush(bool force)
|
||||
{
|
||||
const uint64 framesOffset = 10;
|
||||
const uint64 framesOffset = 3 * 60;
|
||||
const uint64 maxReleaseFrame = Engine::FrameCount - framesOffset;
|
||||
force |= Engine::ShouldExit();
|
||||
|
||||
|
||||
@@ -443,6 +443,19 @@ const String& Actor::GetLayerName() const
|
||||
return Level::Layers[_layer];
|
||||
}
|
||||
|
||||
void Actor::SetLayerName(const StringView& value)
|
||||
{
|
||||
for (int32 i = 0; i < 32; i++)
|
||||
{
|
||||
if (Level::Layers[i] == value)
|
||||
{
|
||||
SetLayer(i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
LOG(Warning, "Unknown layer name '{0}'", value);
|
||||
}
|
||||
|
||||
bool Actor::HasTag() const
|
||||
{
|
||||
return Tags.Count() != 0;
|
||||
@@ -485,10 +498,21 @@ Script* Actor::GetScript(int32 index) const
|
||||
Script* Actor::GetScript(const MClass* type) const
|
||||
{
|
||||
CHECK_RETURN(type, nullptr);
|
||||
for (auto script : Scripts)
|
||||
if (type->IsInterface())
|
||||
{
|
||||
if (script->GetClass()->IsSubClassOf(type))
|
||||
return script;
|
||||
for (auto script : Scripts)
|
||||
{
|
||||
if (script->GetClass()->HasInterface(type))
|
||||
return script;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (auto script : Scripts)
|
||||
{
|
||||
if (script->GetClass()->IsSubClassOf(type))
|
||||
return script;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
@@ -496,9 +520,18 @@ Script* Actor::GetScript(const MClass* type) const
|
||||
Array<Script*> Actor::GetScripts(const MClass* type) const
|
||||
{
|
||||
Array<Script*> result;
|
||||
for (auto script : Scripts)
|
||||
if (script->GetClass()->IsSubClassOf(type))
|
||||
result.Add(script);
|
||||
if (type->IsInterface())
|
||||
{
|
||||
for (auto script : Scripts)
|
||||
if (script->GetClass()->HasInterface(type))
|
||||
result.Add(script);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (auto script : Scripts)
|
||||
if (script->GetClass()->IsSubClassOf(type))
|
||||
result.Add(script);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -222,7 +222,7 @@ namespace FlaxEngine
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of the script to search for. Includes any scripts derived from the type.</typeparam>
|
||||
/// <returns>The script or null if failed to find.</returns>
|
||||
public T GetScript<T>() where T : Script
|
||||
public T GetScript<T>() where T : class
|
||||
{
|
||||
return GetScript(typeof(T)) as T;
|
||||
}
|
||||
@@ -233,7 +233,7 @@ namespace FlaxEngine
|
||||
/// <typeparam name="T">Type of the script to search for. Includes any scripts derived from the type.</typeparam>
|
||||
/// <param name="script">The returned script, valid only if method returns true.</param>
|
||||
/// <returns>True if found a script of that type or false if failed to find.</returns>
|
||||
public bool TryGetScript<T>(out T script) where T : Script
|
||||
public bool TryGetScript<T>(out T script) where T : class
|
||||
{
|
||||
script = GetScript(typeof(T)) as T;
|
||||
return script != null;
|
||||
@@ -244,7 +244,7 @@ namespace FlaxEngine
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of the object.</typeparam>
|
||||
/// <returns>Script instance if found, null otherwise.</returns>
|
||||
public T FindScript<T>() where T : Script
|
||||
public T FindScript<T>() where T : class
|
||||
{
|
||||
return FindScript(typeof(T)) as T;
|
||||
}
|
||||
@@ -290,7 +290,7 @@ namespace FlaxEngine
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of the scripts to search for. Includes any scripts derived from the type.</typeparam>
|
||||
/// <returns>All scripts matching the specified type.</returns>
|
||||
public T[] GetScripts<T>() where T : Script
|
||||
public T[] GetScripts<T>() where T : class
|
||||
{
|
||||
var count = ScriptsCount;
|
||||
var length = 0;
|
||||
|
||||
@@ -105,7 +105,13 @@ public:
|
||||
/// <summary>
|
||||
/// Gets the name of the layer.
|
||||
/// </summary>
|
||||
API_PROPERTY() const String& GetLayerName() const;
|
||||
API_PROPERTY(Attributes="HideInEditor, NoSerialize, NoAnimate")
|
||||
const String& GetLayerName() const;
|
||||
|
||||
/// <summary>
|
||||
/// Sets the name of the layer.
|
||||
/// </summary>
|
||||
API_PROPERTY() void SetLayerName(const StringView& value);
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether this actor has any tag assigned.
|
||||
|
||||
@@ -1443,20 +1443,15 @@ void Level::ReloadScriptsAsync()
|
||||
|
||||
Actor* Level::FindActor(const Guid& id)
|
||||
{
|
||||
return Scripting::FindObject<Actor>(id);
|
||||
return Scripting::TryFindObject<Actor>(id);
|
||||
}
|
||||
|
||||
Actor* Level::FindActor(const StringView& name)
|
||||
{
|
||||
Actor* result = nullptr;
|
||||
|
||||
ScopeLock lock(ScenesLock);
|
||||
|
||||
for (int32 i = 0; result == nullptr && i < Scenes.Count(); i++)
|
||||
{
|
||||
result = Scenes[i]->FindActor(name);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -164,6 +164,7 @@ void ENetDriver::Disconnect(const NetworkConnection& connection)
|
||||
|
||||
bool ENetDriver::PopEvent(NetworkEvent* eventPtr)
|
||||
{
|
||||
ASSERT(_host);
|
||||
ENetEvent event;
|
||||
const int result = enet_host_service(_host, &event, 0);
|
||||
if (result < 0)
|
||||
|
||||
@@ -191,6 +191,7 @@ bool StartPeer()
|
||||
if (!NetworkManager::Peer)
|
||||
{
|
||||
LOG(Error, "Failed to create Network Peer at {0}:{1}", networkConfig.Address, networkConfig.Port);
|
||||
NetworkManager::State = NetworkConnectionState::Offline;
|
||||
return true;
|
||||
}
|
||||
NetworkManager::Frame = 0;
|
||||
@@ -243,7 +244,10 @@ bool NetworkManager::StartServer()
|
||||
LOG(Info, "Starting network manager as server");
|
||||
Mode = NetworkManagerMode::Server;
|
||||
if (StartPeer())
|
||||
{
|
||||
Mode = NetworkManagerMode::Offline;
|
||||
return true;
|
||||
}
|
||||
if (!Peer->Listen())
|
||||
{
|
||||
Stop();
|
||||
@@ -265,7 +269,10 @@ bool NetworkManager::StartClient()
|
||||
LOG(Info, "Starting network manager as client");
|
||||
Mode = NetworkManagerMode::Client;
|
||||
if (StartPeer())
|
||||
{
|
||||
Mode = NetworkManagerMode::Offline;
|
||||
return true;
|
||||
}
|
||||
if (!Peer->Connect())
|
||||
{
|
||||
Stop();
|
||||
@@ -286,9 +293,15 @@ bool NetworkManager::StartHost()
|
||||
LOG(Info, "Starting network manager as host");
|
||||
Mode = NetworkManagerMode::Host;
|
||||
if (StartPeer())
|
||||
{
|
||||
Mode = NetworkManagerMode::Offline;
|
||||
return true;
|
||||
}
|
||||
if (!Peer->Listen())
|
||||
{
|
||||
Mode = NetworkManagerMode::Offline;
|
||||
return true;
|
||||
}
|
||||
LocalClientId = ServerClientId;
|
||||
NextClientId = ServerClientId + 1;
|
||||
LocalClient = New<NetworkClient>(LocalClientId, NetworkConnection{ 0 });
|
||||
|
||||
@@ -140,6 +140,12 @@ public:
|
||||
return State == NetworkConnectionState::Connected;
|
||||
}
|
||||
|
||||
// Returns true if network is online or disconnected.
|
||||
API_PROPERTY() FORCE_INLINE static bool IsOffline()
|
||||
{
|
||||
return State == NetworkConnectionState::Offline || State == NetworkConnectionState::Disconnected;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the network client for a given connection. Returns null if failed to find it.
|
||||
/// </summary>
|
||||
|
||||
@@ -663,7 +663,7 @@ bool NetworkReplicator::InvokeSerializer(const ScriptingTypeHandle& typeHandle,
|
||||
|
||||
void NetworkReplicator::AddObject(ScriptingObject* obj, ScriptingObject* parent)
|
||||
{
|
||||
if (!obj || NetworkManager::State == NetworkConnectionState::Offline)
|
||||
if (!obj || NetworkManager::IsOffline())
|
||||
return;
|
||||
ScopeLock lock(ObjectsLock);
|
||||
if (Objects.Contains(obj))
|
||||
@@ -695,7 +695,7 @@ void NetworkReplicator::AddObject(ScriptingObject* obj, ScriptingObject* parent)
|
||||
|
||||
void NetworkReplicator::RemoveObject(ScriptingObject* obj)
|
||||
{
|
||||
if (!obj || NetworkManager::State == NetworkConnectionState::Offline)
|
||||
if (!obj || NetworkManager::IsOffline())
|
||||
return;
|
||||
ScopeLock lock(ObjectsLock);
|
||||
const auto it = Objects.Find(obj->GetID());
|
||||
@@ -715,7 +715,7 @@ void NetworkReplicator::SpawnObject(ScriptingObject* obj)
|
||||
|
||||
void NetworkReplicator::SpawnObject(ScriptingObject* obj, const DataContainer<uint32>& clientIds)
|
||||
{
|
||||
if (!obj || NetworkManager::State == NetworkConnectionState::Offline)
|
||||
if (!obj || NetworkManager::IsOffline())
|
||||
return;
|
||||
ScopeLock lock(ObjectsLock);
|
||||
const auto it = Objects.Find(obj->GetID());
|
||||
@@ -730,7 +730,7 @@ void NetworkReplicator::SpawnObject(ScriptingObject* obj, const DataContainer<ui
|
||||
|
||||
void NetworkReplicator::DespawnObject(ScriptingObject* obj)
|
||||
{
|
||||
if (!obj || NetworkManager::State == NetworkConnectionState::Offline)
|
||||
if (!obj || NetworkManager::IsOffline())
|
||||
return;
|
||||
ScopeLock lock(ObjectsLock);
|
||||
const auto it = Objects.Find(obj->GetID());
|
||||
@@ -887,7 +887,7 @@ NetworkStream* NetworkReplicator::BeginInvokeRPC()
|
||||
void NetworkReplicator::EndInvokeRPC(ScriptingObject* obj, const ScriptingTypeHandle& type, const StringAnsiView& name, NetworkStream* argsStream)
|
||||
{
|
||||
const NetworkRpcInfo* info = NetworkRpcInfo::RPCsTable.TryGet(NetworkRpcName(type, name));
|
||||
if (!info || !obj)
|
||||
if (!info || !obj || NetworkManager::IsOffline())
|
||||
return;
|
||||
ObjectsLock.Lock();
|
||||
auto& rpc = RpcQueue.AddOne();
|
||||
@@ -1023,15 +1023,6 @@ void NetworkInternal::NetworkReplicatorUpdate()
|
||||
NewClients.Clear();
|
||||
}
|
||||
|
||||
// Collect clients for replication (from server)
|
||||
BuildCachedTargets(NetworkManager::Clients);
|
||||
if (!isClient && CachedTargets.Count() == 0)
|
||||
{
|
||||
// Early exit if server has nobody to send data to
|
||||
Scripting::ObjectsLookupIdMapping.Set(nullptr);
|
||||
return;
|
||||
}
|
||||
|
||||
// Despawn
|
||||
if (DespawnQueue.Count() != 0)
|
||||
{
|
||||
@@ -1474,6 +1465,10 @@ void NetworkInternal::OnNetworkMessageObjectSpawn(NetworkEvent& event, NetworkCl
|
||||
if (!obj->IsRegistered())
|
||||
obj->RegisterObject();
|
||||
const NetworkReplicatedObject* parent = ResolveObject(msgDataItem.ParentId);
|
||||
if (!parent && msgDataItem.ParentId.IsValid())
|
||||
{
|
||||
NETWORK_REPLICATOR_LOG(Error, "[NetworkReplicator] Failed to find object {} as parent to spawned object", msgDataItem.ParentId.ToString());
|
||||
}
|
||||
|
||||
// Add object to the list
|
||||
NetworkReplicatedObject item;
|
||||
|
||||
@@ -407,6 +407,23 @@ namespace FlaxEngine.Networking
|
||||
return new Quaternion(ReadSingle(), ReadSingle(), ReadSingle(), ReadSingle());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes data of type <see cref="Ray"/> into the message.
|
||||
/// </summary>
|
||||
public void WriteRay(Ray value)
|
||||
{
|
||||
WriteVector3(value.Position);
|
||||
WriteVector3(value.Direction);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads and returns data of type <see cref="Ray"/> from the message.
|
||||
/// </summary>
|
||||
public Ray ReadRay()
|
||||
{
|
||||
return new Ray(ReadVector3(), ReadVector3());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes data of type <see cref="Boolean"/> into the message.
|
||||
/// </summary>
|
||||
|
||||
@@ -39,6 +39,9 @@ void ParticleSystem::Init(ParticleEmitter* emitter, float duration, float fps)
|
||||
track.AsEmitter.Index = 0;
|
||||
track.AsEmitter.StartFrame = 0;
|
||||
track.AsEmitter.DurationFrames = DurationFrames;
|
||||
#if !BUILD_RELEASE
|
||||
_debugName = StringUtils::GetFileNameWithoutExtension(emitter->GetPath());
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -155,6 +158,9 @@ ParticleEffect* ParticleSystem::Spawn(Actor* parent, const Transform& transform,
|
||||
auto effect = New<ParticleEffect>();
|
||||
effect->SetTransform(transform);
|
||||
effect->ParticleSystem = this;
|
||||
#if !BUILD_RELEASE
|
||||
effect->SetName(_debugName); // Give usable name in development builds
|
||||
#endif
|
||||
|
||||
Level::SpawnActor(effect, parent);
|
||||
|
||||
@@ -452,6 +458,9 @@ Asset::LoadResult ParticleSystem::load()
|
||||
return LoadResult::InvalidData;
|
||||
}
|
||||
|
||||
#if !BUILD_RELEASE
|
||||
_debugName = StringUtils::GetFileNameWithoutExtension(GetPath());
|
||||
#endif
|
||||
return LoadResult::Ok;
|
||||
}
|
||||
|
||||
@@ -463,6 +472,9 @@ void ParticleSystem::unload(bool isReloading)
|
||||
Emitters.Resize(0);
|
||||
EmittersParametersOverrides.SetCapacity(0);
|
||||
Tracks.Resize(0);
|
||||
#if !BUILD_RELEASE
|
||||
_debugName.Clear();
|
||||
#endif
|
||||
}
|
||||
|
||||
AssetChunksFlag ParticleSystem::getChunksToPreload() const
|
||||
|
||||
@@ -98,6 +98,11 @@ public:
|
||||
|
||||
typedef Pair<int32, Guid> EmitterParameterOverrideKey;
|
||||
|
||||
private:
|
||||
#if !BUILD_RELEASE
|
||||
String _debugName;
|
||||
#endif
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
/// The asset data version number. Used to sync the data with the instances state. Incremented each time asset gets loaded.
|
||||
|
||||
@@ -150,7 +150,6 @@ CharacterController::CollisionFlags CharacterController::SimpleMove(const Vector
|
||||
Vector3 displacement = speed;
|
||||
displacement += GetPhysicsScene()->GetGravity() * deltaTime;
|
||||
displacement *= deltaTime;
|
||||
|
||||
return Move(displacement);
|
||||
}
|
||||
|
||||
@@ -162,7 +161,8 @@ CharacterController::CollisionFlags CharacterController::Move(const Vector3& dis
|
||||
const float deltaTime = Time::GetCurrentSafe()->DeltaTime.GetTotalSeconds();
|
||||
result = (CollisionFlags)PhysicsBackend::MoveController(_controller, _shape, displacement, _minMoveDistance, deltaTime);
|
||||
_lastFlags = result;
|
||||
SetPosition(PhysicsBackend::GetControllerPosition(_controller));
|
||||
Vector3 position = PhysicsBackend::GetControllerPosition(_controller) - _center;
|
||||
SetPosition(position);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -178,10 +178,11 @@ void CharacterController::DrawPhysicsDebug(RenderView& view)
|
||||
const float minSize = 0.001f;
|
||||
const float radius = Math::Max(Math::Abs(_radius) * scaling, minSize);
|
||||
const float height = Math::Max(Math::Abs(_height) * scaling, minSize);
|
||||
const Vector3 position = _transform.LocalToWorld(_center);
|
||||
if (view.Mode == ViewMode::PhysicsColliders)
|
||||
DEBUG_DRAW_TUBE(_transform.LocalToWorld(_center), Quaternion::Euler(90, 0, 0), radius, height, Color::LightYellow, 0, true);
|
||||
DEBUG_DRAW_TUBE(position, Quaternion::Euler(90, 0, 0), radius, height, Color::LightYellow, 0, true);
|
||||
else
|
||||
DEBUG_DRAW_WIRE_TUBE(_transform.LocalToWorld(_center), Quaternion::Euler(90, 0, 0), radius, height, Color::GreenYellow * 0.8f, 0, true);
|
||||
DEBUG_DRAW_WIRE_TUBE(position, Quaternion::Euler(90, 0, 0), radius, height, Color::GreenYellow * 0.8f, 0, true);
|
||||
}
|
||||
|
||||
void CharacterController::OnDebugDrawSelected()
|
||||
@@ -190,7 +191,8 @@ void CharacterController::OnDebugDrawSelected()
|
||||
const float minSize = 0.001f;
|
||||
const float radius = Math::Max(Math::Abs(_radius) * scaling, minSize);
|
||||
const float height = Math::Max(Math::Abs(_height) * scaling, minSize);
|
||||
DEBUG_DRAW_WIRE_TUBE(_transform.LocalToWorld(_center), Quaternion::Euler(90, 0, 0), radius, height, Color::GreenYellow, 0, false);
|
||||
const Vector3 position = _transform.LocalToWorld(_center);
|
||||
DEBUG_DRAW_WIRE_TUBE(position, Quaternion::Euler(90, 0, 0), radius, height, Color::GreenYellow, 0, false);
|
||||
|
||||
// Base
|
||||
Collider::OnDebugDrawSelected();
|
||||
@@ -204,7 +206,8 @@ void CharacterController::CreateController()
|
||||
ASSERT(_controller == nullptr && _shape == nullptr);
|
||||
_cachedScale = GetScale();
|
||||
const float scaling = _cachedScale.GetAbsolute().MaxValue();
|
||||
_controller = PhysicsBackend::CreateController(GetPhysicsScene()->GetPhysicsScene(), this, this, _contactOffset, _transform.Translation, _slopeLimit, (int32)_nonWalkableMode, Material.Get(), Math::Abs(_radius) * scaling, Math::Abs(_height) * scaling, _stepOffset, _shape);
|
||||
const Vector3 position = _transform.LocalToWorld(_center);
|
||||
_controller = PhysicsBackend::CreateController(GetPhysicsScene()->GetPhysicsScene(), this, this, _contactOffset, position, _slopeLimit, (int32)_nonWalkableMode, Material.Get(), Math::Abs(_radius) * scaling, Math::Abs(_height) * scaling, _stepOffset, _shape);
|
||||
|
||||
// Setup
|
||||
PhysicsBackend::SetControllerUpDirection(_shape, _upDirection);
|
||||
@@ -280,6 +283,7 @@ void CharacterController::OnActiveTransformChanged()
|
||||
_isUpdatingTransform = true;
|
||||
Transform transform;
|
||||
PhysicsBackend::GetRigidActorPose(PhysicsBackend::GetShapeActor(_shape), transform.Translation, transform.Orientation);
|
||||
transform.Translation -= _center;
|
||||
transform.Orientation = _transform.Orientation;
|
||||
transform.Scale = _transform.Scale;
|
||||
SetTransform(transform);
|
||||
@@ -360,9 +364,10 @@ void CharacterController::OnTransformChanged()
|
||||
Actor::OnTransformChanged();
|
||||
|
||||
// Update physics
|
||||
const Vector3 position = _transform.LocalToWorld(_center);
|
||||
if (!_isUpdatingTransform && _controller)
|
||||
{
|
||||
PhysicsBackend::SetControllerPosition(_controller, _transform.Translation);
|
||||
PhysicsBackend::SetControllerPosition(_controller, position);
|
||||
const Float3 scale = GetScale();
|
||||
if (!Float3::NearEqual(_cachedScale, scale))
|
||||
UpdateGeometry();
|
||||
@@ -370,7 +375,7 @@ void CharacterController::OnTransformChanged()
|
||||
}
|
||||
else if (!_controller)
|
||||
{
|
||||
_box = BoundingBox(_transform.Translation);
|
||||
_box = BoundingBox(position);
|
||||
BoundingSphere::FromBox(_box, _sphere);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -127,7 +127,7 @@ public:
|
||||
/// <summary>
|
||||
/// Gets the character up vector.
|
||||
/// </summary>
|
||||
API_PROPERTY(Attributes="EditorOrder(240), DefaultValue(true), EditorDisplay(\"Character Controller\")")
|
||||
API_PROPERTY(Attributes="EditorOrder(240), DefaultValue(typeof(Vector3), \"0,1,0\"), EditorDisplay(\"Character Controller\")")
|
||||
Vector3 GetUpDirection() const;
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -1143,6 +1143,7 @@ void GlobalSurfaceAtlasPass::RenderDebug(RenderContext& renderContext, GPUContex
|
||||
EyeAdaptationPass::Instance()->Render(renderContext, tempBuffer);
|
||||
PostProcessingPass::Instance()->Render(renderContext, tempBuffer, output, colorGradingLUT);
|
||||
RenderTargetPool::Release(colorGradingLUT);
|
||||
RenderTargetPool::Release(tempBuffer);
|
||||
context->ResetRenderTarget();
|
||||
|
||||
// Rebind resources
|
||||
|
||||
@@ -681,6 +681,16 @@ void* BinaryModule::FindMethod(const ScriptingTypeHandle& typeHandle, const Scri
|
||||
|
||||
void BinaryModule::Destroy(bool isReloading)
|
||||
{
|
||||
// Destroy any default script instances
|
||||
for (const auto& type : Types)
|
||||
{
|
||||
if (type.Type == ScriptingTypes::Script && type.Script.DefaultInstance)
|
||||
{
|
||||
Delete(type.Script.DefaultInstance);
|
||||
type.Script.DefaultInstance = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// Unregister
|
||||
GetModules().RemoveKeepOrder(this);
|
||||
}
|
||||
@@ -1443,6 +1453,10 @@ void NativeBinaryModule::Destroy(bool isReloading)
|
||||
{
|
||||
ManagedBinaryModule::Destroy(isReloading);
|
||||
|
||||
// Skip native code unloading from core libs
|
||||
if (this == GetBinaryModuleCorlib() || this == GetBinaryModuleFlaxEngine())
|
||||
return;
|
||||
|
||||
// Release native library
|
||||
const auto library = Library;
|
||||
if (library)
|
||||
|
||||
@@ -127,6 +127,15 @@ bool MClass::IsSubClassOf(const MonoClass* monoClass) const
|
||||
}
|
||||
#endif
|
||||
|
||||
bool MClass::HasInterface(const MClass* klass) const
|
||||
{
|
||||
#if USE_MONO
|
||||
return klass && mono_class_is_assignable_from(klass->GetNative(), _monoClass) != 0;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool MClass::IsInstanceOfType(MObject* object) const
|
||||
{
|
||||
if (object == nullptr)
|
||||
|
||||
@@ -154,6 +154,13 @@ public:
|
||||
bool IsSubClassOf(const MonoClass* monoClass) const;
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Checks if this class implements the specified interface (including any base types).
|
||||
/// </summary>
|
||||
/// <param name="klass">The interface class.</param>
|
||||
/// <returns>True if this class implements the specified interface.</returns>
|
||||
bool HasInterface(const MClass* klass) const;
|
||||
|
||||
/// <summary>
|
||||
/// Checks is the provided object instance of this class' type.
|
||||
/// </summary>
|
||||
|
||||
@@ -530,13 +530,6 @@ void Scripting::Release()
|
||||
for (int32 i = modules.Count() - 1; i >= 0; i--)
|
||||
{
|
||||
auto module = modules[i];
|
||||
if (module == GetBinaryModuleCorlib() || module == GetBinaryModuleFlaxEngine())
|
||||
{
|
||||
// Just C# assembly unload for in-build modules
|
||||
((ManagedBinaryModule*)module)->Assembly->Unload();
|
||||
continue;
|
||||
}
|
||||
|
||||
module->Destroy(false);
|
||||
}
|
||||
_nonNativeModules.ClearDelete();
|
||||
|
||||
@@ -819,7 +819,8 @@ bool ModelTool::ImportDataAssimp(const char* path, ImportedModelData& data, Opti
|
||||
|
||||
ImportCurve(aAnim->mPositionKeys, aAnim->mNumPositionKeys, anim.Position);
|
||||
ImportCurve(aAnim->mRotationKeys, aAnim->mNumRotationKeys, anim.Rotation);
|
||||
ImportCurve(aAnim->mScalingKeys, aAnim->mNumScalingKeys, anim.Scale);
|
||||
if (options.ImportScaleTracks)
|
||||
ImportCurve(aAnim->mScalingKeys, aAnim->mNumScalingKeys, anim.Scale);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1051,9 +1051,9 @@ bool ImportAnimation(int32 index, ImportedModelData& data, OpenFbxImporterData&
|
||||
|
||||
const ofbx::AnimationCurveNode* translationNode = layer->getCurveNode(*aNode.FbxObj, "Lcl Translation");
|
||||
const ofbx::AnimationCurveNode* rotationNode = layer->getCurveNode(*aNode.FbxObj, "Lcl Rotation");
|
||||
const ofbx::AnimationCurveNode* scalingNode = nullptr; //layer->getCurveNode(*aNode.FbxObj, "Lcl Scaling");
|
||||
const ofbx::AnimationCurveNode* scalingNode = layer->getCurveNode(*aNode.FbxObj, "Lcl Scaling");
|
||||
|
||||
if (translationNode || rotationNode || scalingNode)
|
||||
if (translationNode || rotationNode || (scalingNode && importerData.Options.ImportScaleTracks))
|
||||
animatedNodes.Add(nodeIndex);
|
||||
}
|
||||
if (animatedNodes.IsEmpty())
|
||||
@@ -1069,13 +1069,14 @@ bool ImportAnimation(int32 index, ImportedModelData& data, OpenFbxImporterData&
|
||||
|
||||
const ofbx::AnimationCurveNode* translationNode = layer->getCurveNode(*aNode.FbxObj, "Lcl Translation");
|
||||
const ofbx::AnimationCurveNode* rotationNode = layer->getCurveNode(*aNode.FbxObj, "Lcl Rotation");
|
||||
//const ofbx::AnimationCurveNode* scalingNode = layer->getCurveNode(*aNode.FbxObj, "Lcl Scaling");
|
||||
const ofbx::AnimationCurveNode* scalingNode = layer->getCurveNode(*aNode.FbxObj, "Lcl Scaling");
|
||||
|
||||
anim.NodeName = aNode.Name;
|
||||
|
||||
ImportCurve(translationNode, anim.Position, info, ExtractKeyframePosition);
|
||||
ImportCurve(rotationNode, anim.Rotation, info, ExtractKeyframeRotation);
|
||||
//ImportCurve(scalingNode, anim.Scale, info, ExtractKeyframeScale);
|
||||
if (importerData.Options.ImportScaleTracks)
|
||||
ImportCurve(scalingNode, anim.Scale, info, ExtractKeyframeScale);
|
||||
}
|
||||
|
||||
if (importerData.ConvertRH)
|
||||
|
||||
@@ -53,6 +53,7 @@ void ModelTool::Options::Serialize(SerializeStream& stream, const void* otherObj
|
||||
SERIALIZE(SamplingRate);
|
||||
SERIALIZE(SkipEmptyCurves);
|
||||
SERIALIZE(OptimizeKeyframes);
|
||||
SERIALIZE(ImportScaleTracks);
|
||||
SERIALIZE(EnableRootMotion);
|
||||
SERIALIZE(RootNodeName);
|
||||
SERIALIZE(GenerateLODs);
|
||||
@@ -93,6 +94,7 @@ void ModelTool::Options::Deserialize(DeserializeStream& stream, ISerializeModifi
|
||||
DESERIALIZE(SamplingRate);
|
||||
DESERIALIZE(SkipEmptyCurves);
|
||||
DESERIALIZE(OptimizeKeyframes);
|
||||
DESERIALIZE(ImportScaleTracks);
|
||||
DESERIALIZE(EnableRootMotion);
|
||||
DESERIALIZE(RootNodeName);
|
||||
DESERIALIZE(GenerateLODs);
|
||||
|
||||
@@ -235,6 +235,7 @@ public:
|
||||
float SamplingRate = 0.0f;
|
||||
bool SkipEmptyCurves = true;
|
||||
bool OptimizeKeyframes = true;
|
||||
bool ImportScaleTracks = false;
|
||||
bool EnableRootMotion = false;
|
||||
String RootNodeName;
|
||||
|
||||
|
||||
541
Source/Engine/UI/GUI/CanvasScaler.cs
Normal file
541
Source/Engine/UI/GUI/CanvasScaler.cs
Normal file
@@ -0,0 +1,541 @@
|
||||
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace FlaxEngine.GUI
|
||||
{
|
||||
/// <summary>
|
||||
/// UI canvas scaling component for user interface that targets multiple different game resolutions (eg. mobile screens).
|
||||
/// </summary>
|
||||
public class CanvasScaler : ContainerControl
|
||||
{
|
||||
/// <summary>
|
||||
/// Canvas scaling modes.
|
||||
/// </summary>
|
||||
public enum ScalingMode
|
||||
{
|
||||
/// <summary>
|
||||
/// Applies constant scale to the whole UI in pixels.
|
||||
/// </summary>
|
||||
ConstantPixelSize,
|
||||
|
||||
/// <summary>
|
||||
/// Applies constant scale to the whole UI in physical units (depends on the screen DPI). Ensures the UI will have specific real-world size no matter the screen resolution.
|
||||
/// </summary>
|
||||
ConstantPhysicalSize,
|
||||
|
||||
/// <summary>
|
||||
/// Applies min/max scaling to the UI depending on the screen resolution. Ensures the UI size won't go below min or above max resolution to maintain it's readability.
|
||||
/// </summary>
|
||||
ScaleWithResolution,
|
||||
|
||||
/// <summary>
|
||||
/// Applies scaling curve to the UI depending on the screen DPI.
|
||||
/// </summary>
|
||||
ScaleWithDpi,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Physical unit types for canvas scaling.
|
||||
/// </summary>
|
||||
public enum PhysicalUnitMode
|
||||
{
|
||||
/// <summary>
|
||||
/// Centimeters (0.01 meter).
|
||||
/// </summary>
|
||||
Centimeters,
|
||||
|
||||
/// <summary>
|
||||
/// Millimeters (0.1 centimeter, 0.001 meter).
|
||||
/// </summary>
|
||||
Millimeters,
|
||||
|
||||
/// <summary>
|
||||
/// Inches (2.54 centimeters).
|
||||
/// </summary>
|
||||
Inches,
|
||||
|
||||
/// <summary>
|
||||
/// Points (1/72 inch, 1/112 of pica).
|
||||
/// </summary>
|
||||
Points,
|
||||
|
||||
/// <summary>
|
||||
/// Pica (1/6 inch).
|
||||
/// </summary>
|
||||
Picas,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resolution scaling modes.
|
||||
/// </summary>
|
||||
public enum ResolutionScalingMode
|
||||
{
|
||||
/// <summary>
|
||||
/// Uses the shortest side of the screen to scale the canvas for min/max rule.
|
||||
/// </summary>
|
||||
ShortestSide,
|
||||
|
||||
/// <summary>
|
||||
/// Uses the longest side of the screen to scale the canvas for min/max rule.
|
||||
/// </summary>
|
||||
LongestSide,
|
||||
|
||||
/// <summary>
|
||||
/// Uses the horizontal (X, width) side of the screen to scale the canvas for min/max rule.
|
||||
/// </summary>
|
||||
Horizontal,
|
||||
|
||||
/// <summary>
|
||||
/// Uses the vertical (Y, height) side of the screen to scale the canvas for min/max rule.
|
||||
/// </summary>
|
||||
Vertical,
|
||||
}
|
||||
|
||||
private ScalingMode _scalingMode = ScalingMode.ConstantPixelSize;
|
||||
private PhysicalUnitMode _physicalUnit = PhysicalUnitMode.Points;
|
||||
private ResolutionScalingMode _resolutionMode = ResolutionScalingMode.ShortestSide;
|
||||
private float _scale = 1.0f;
|
||||
private float _scaleFactor = 1.0f;
|
||||
private float _physicalUnitSize = 1.0f;
|
||||
private Float2 _resolutionMin = new Float2(1, 1);
|
||||
private Float2 _resolutionMax = new Float2(10000, 10000);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current UI scale. Computed based on the setup when performing layout.
|
||||
/// </summary>
|
||||
public float CurrentScale => _scale;
|
||||
|
||||
/// <summary>
|
||||
/// The UI Canvas scaling mode.
|
||||
/// </summary>
|
||||
[EditorOrder(0), EditorDisplay("Canvas Scaler"), ExpandGroups, DefaultValue(ScalingMode.ConstantPixelSize)]
|
||||
public ScalingMode Scaling
|
||||
{
|
||||
get => _scalingMode;
|
||||
set
|
||||
{
|
||||
if (_scalingMode == value)
|
||||
return;
|
||||
_scalingMode = value;
|
||||
PerformLayout();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The UI Canvas scale. Applied in all scaling modes for custom UI sizing.
|
||||
/// </summary>
|
||||
[EditorOrder(10), EditorDisplay("Canvas Scaler"), DefaultValue(1.0f), Limit(0.001f, 1000.0f, 0.01f)]
|
||||
public float ScaleFactor
|
||||
{
|
||||
get => _scaleFactor;
|
||||
set
|
||||
{
|
||||
if (Mathf.NearEqual(_scaleFactor, value))
|
||||
return;
|
||||
_scaleFactor = value;
|
||||
PerformLayout();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The UI Canvas physical unit to use for scaling via PhysicalUnitSize. Used only in ConstantPhysicalSize mode.
|
||||
/// </summary>
|
||||
#if FLAX_EDITOR
|
||||
[EditorOrder(100), EditorDisplay("Canvas Scaler"), DefaultValue(PhysicalUnitMode.Points), VisibleIf(nameof(IsConstantPhysicalSize))]
|
||||
#endif
|
||||
public PhysicalUnitMode PhysicalUnit
|
||||
{
|
||||
get => _physicalUnit;
|
||||
set
|
||||
{
|
||||
if (_physicalUnit == value)
|
||||
return;
|
||||
_physicalUnit = value;
|
||||
#if FLAX_EDITOR
|
||||
if (FlaxEditor.CustomEditors.CustomEditor.IsSettingValue)
|
||||
{
|
||||
// Set auto-default physical unit value for easier tweaking in Editor
|
||||
_physicalUnitSize = GetUnitDpi(_physicalUnit) / Platform.Dpi;
|
||||
}
|
||||
#endif
|
||||
PerformLayout();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The UI Canvas physical unit value. Used only in ConstantPhysicalSize mode.
|
||||
/// </summary>
|
||||
#if FLAX_EDITOR
|
||||
[EditorOrder(110), EditorDisplay("Canvas Scaler"), DefaultValue(1.0f), Limit(0.000001f, 1000000.0f, 0.0f), VisibleIf(nameof(IsConstantPhysicalSize))]
|
||||
#endif
|
||||
public float PhysicalUnitSize
|
||||
{
|
||||
get => _physicalUnitSize;
|
||||
set
|
||||
{
|
||||
if (Mathf.NearEqual(_physicalUnitSize, value))
|
||||
return;
|
||||
_physicalUnitSize = value;
|
||||
PerformLayout();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The UI Canvas resolution scaling mode. Controls min/max resolutions usage in relation to the current screen resolution to compute the UI scale. Used only in ScaleWithResolution mode.
|
||||
/// </summary>
|
||||
#if FLAX_EDITOR
|
||||
[EditorOrder(120), EditorDisplay("Canvas Scaler"), VisibleIf(nameof(IsScaleWithResolution))]
|
||||
#endif
|
||||
public ResolutionScalingMode ResolutionMode
|
||||
{
|
||||
get => _resolutionMode;
|
||||
set
|
||||
{
|
||||
if (_resolutionMode == value)
|
||||
return;
|
||||
_resolutionMode = value;
|
||||
PerformLayout();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The UI Canvas minimum resolution. If the screen has lower size, then the interface will be scaled accordingly. Used only in ScaleWithResolution mode.
|
||||
/// </summary>
|
||||
#if FLAX_EDITOR
|
||||
[EditorOrder(120), EditorDisplay("Canvas Scaler"), VisibleIf(nameof(IsScaleWithResolution))]
|
||||
#endif
|
||||
public Float2 ResolutionMin
|
||||
{
|
||||
get => _resolutionMin;
|
||||
set
|
||||
{
|
||||
value = Float2.Max(value, Float2.One);
|
||||
if (Float2.NearEqual(ref _resolutionMin, ref value))
|
||||
return;
|
||||
_resolutionMin = value;
|
||||
PerformLayout();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The UI Canvas maximum resolution. If the screen has higher size, then the interface will be scaled accordingly. Used only in ScaleWithResolution mode.
|
||||
/// </summary>
|
||||
#if FLAX_EDITOR
|
||||
[EditorOrder(130), EditorDisplay("Canvas Scaler"), VisibleIf(nameof(IsScaleWithResolution))]
|
||||
#endif
|
||||
public Float2 ResolutionMax
|
||||
{
|
||||
get => _resolutionMax;
|
||||
set
|
||||
{
|
||||
value = Float2.Max(value, Float2.One);
|
||||
if (Float2.NearEqual(ref _resolutionMax, ref value))
|
||||
return;
|
||||
_resolutionMax = value;
|
||||
PerformLayout();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The UI Canvas scaling curve based on screen resolution - shortest/longest/vertical/horizontal (key is resolution, value is scale factor). Clear keyframes to skip using it and follow min/max rules only. Used only in ScaleWithResolution mode.
|
||||
/// </summary>
|
||||
#if FLAX_EDITOR
|
||||
[EditorOrder(140), EditorDisplay("Canvas Scaler"), VisibleIf(nameof(IsScaleWithResolution))]
|
||||
#endif
|
||||
public LinearCurve<float> ResolutionCurve = new LinearCurve<float>(new[]
|
||||
{
|
||||
new LinearCurve<float>.Keyframe(480, 0.444f), // 480p
|
||||
new LinearCurve<float>.Keyframe(720, 0.666f), // 720p
|
||||
new LinearCurve<float>.Keyframe(1080, 1.0f), // 1080p
|
||||
new LinearCurve<float>.Keyframe(8640, 8.0f), // 8640p
|
||||
});
|
||||
|
||||
/// <summary>
|
||||
/// The UI Canvas scaling curve based on screen DPI (key is DPI, value is scale factor). Used only in ScaleWithDpi mode.
|
||||
/// </summary>
|
||||
#if FLAX_EDITOR
|
||||
[EditorOrder(150), EditorDisplay("Canvas Scaler"), VisibleIf(nameof(IsScaleWithDpi))]
|
||||
#endif
|
||||
public LinearCurve<float> DpiCurve = new LinearCurve<float>(new[]
|
||||
{
|
||||
new LinearCurve<float>.Keyframe(1.0f, 1.0f),
|
||||
new LinearCurve<float>.Keyframe(96.0f, 1.0f),
|
||||
new LinearCurve<float>.Keyframe(200.0f, 2.0f),
|
||||
new LinearCurve<float>.Keyframe(400.0f, 4.0f),
|
||||
});
|
||||
|
||||
#if FLAX_EDITOR
|
||||
private bool IsConstantPhysicalSize => _scalingMode == ScalingMode.ConstantPhysicalSize;
|
||||
private bool IsScaleWithResolution => _scalingMode == ScalingMode.ScaleWithResolution;
|
||||
private bool IsScaleWithDpi => _scalingMode == ScalingMode.ScaleWithDpi;
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CanvasScaler"/> class.
|
||||
/// </summary>
|
||||
public CanvasScaler()
|
||||
{
|
||||
// Fill the canvas by default
|
||||
Offsets = Margin.Zero;
|
||||
AnchorPreset = AnchorPresets.StretchAll;
|
||||
AutoFocus = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the scaler for the current setup.
|
||||
/// </summary>
|
||||
public void UpdateScale()
|
||||
{
|
||||
float scale = 1.0f;
|
||||
if (Parent != null)
|
||||
{
|
||||
UICanvas canvas = (Root as CanvasRootControl)?.Canvas;
|
||||
float dpi = Platform.Dpi;
|
||||
switch (canvas?.RenderMode ?? CanvasRenderMode.ScreenSpace)
|
||||
{
|
||||
case CanvasRenderMode.WorldSpace:
|
||||
case CanvasRenderMode.WorldSpaceFaceCamera:
|
||||
scale = 1.0f;
|
||||
break;
|
||||
default:
|
||||
switch (_scalingMode)
|
||||
{
|
||||
case ScalingMode.ConstantPixelSize:
|
||||
scale = 1.0f;
|
||||
break;
|
||||
case ScalingMode.ConstantPhysicalSize:
|
||||
{
|
||||
float targetDpi = GetUnitDpi(_physicalUnit);
|
||||
scale = dpi / targetDpi * _physicalUnitSize;
|
||||
break;
|
||||
}
|
||||
case ScalingMode.ScaleWithResolution:
|
||||
{
|
||||
Float2 resolution = Float2.Max(Size, Float2.One);
|
||||
int axis = 0;
|
||||
switch (_resolutionMode)
|
||||
{
|
||||
case ResolutionScalingMode.ShortestSide:
|
||||
axis = resolution.X > resolution.Y ? 1 : 0;
|
||||
break;
|
||||
case ResolutionScalingMode.LongestSide:
|
||||
axis = resolution.X > resolution.Y ? 0 : 1;
|
||||
break;
|
||||
case ResolutionScalingMode.Horizontal:
|
||||
axis = 0;
|
||||
break;
|
||||
case ResolutionScalingMode.Vertical:
|
||||
axis = 1;
|
||||
break;
|
||||
}
|
||||
float min = _resolutionMin[axis], max = _resolutionMax[axis], value = resolution[axis];
|
||||
if (value < min)
|
||||
scale = min / value;
|
||||
else if (value > max)
|
||||
scale = max / value;
|
||||
if (ResolutionCurve != null && ResolutionCurve.Keyframes?.Length != 0)
|
||||
{
|
||||
ResolutionCurve.Evaluate(out var curveScale, value, false);
|
||||
scale *= curveScale;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ScalingMode.ScaleWithDpi:
|
||||
DpiCurve?.Evaluate(out scale, dpi, false);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
_scale = Mathf.Max(scale * _scaleFactor, 0.01f);
|
||||
}
|
||||
|
||||
private float GetUnitDpi(PhysicalUnitMode unit)
|
||||
{
|
||||
float dpi = 1.0f;
|
||||
switch (unit)
|
||||
{
|
||||
case PhysicalUnitMode.Centimeters:
|
||||
dpi = 2.54f;
|
||||
break;
|
||||
case PhysicalUnitMode.Millimeters:
|
||||
dpi = 25.4f;
|
||||
break;
|
||||
case PhysicalUnitMode.Inches:
|
||||
dpi = 1;
|
||||
break;
|
||||
case PhysicalUnitMode.Points:
|
||||
dpi = 72;
|
||||
break;
|
||||
case PhysicalUnitMode.Picas:
|
||||
dpi = 6;
|
||||
break;
|
||||
}
|
||||
return dpi;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void PerformLayoutBeforeChildren()
|
||||
{
|
||||
// Update current scaling before performing layout
|
||||
UpdateScale();
|
||||
|
||||
base.PerformLayoutBeforeChildren();
|
||||
}
|
||||
|
||||
#region UI Scale
|
||||
|
||||
#if FLAX_EDITOR
|
||||
/// <inheritdoc />
|
||||
public override Rectangle EditorBounds => new Rectangle(Float2.Zero, Size / _scale);
|
||||
#endif
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Draw()
|
||||
{
|
||||
DrawSelf();
|
||||
|
||||
// Draw children with scale
|
||||
var scaling = new Float3(_scale, _scale, 1);
|
||||
Matrix3x3.Scaling(ref scaling, out Matrix3x3 scale);
|
||||
Render2D.PushTransform(scale);
|
||||
if (ClipChildren)
|
||||
{
|
||||
GetDesireClientArea(out var clientArea);
|
||||
Render2D.PushClip(ref clientArea);
|
||||
DrawChildren();
|
||||
Render2D.PopClip();
|
||||
}
|
||||
else
|
||||
{
|
||||
DrawChildren();
|
||||
}
|
||||
Render2D.PopTransform();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void GetDesireClientArea(out Rectangle rect)
|
||||
{
|
||||
// Scale the area for the client controls
|
||||
rect = new Rectangle(Float2.Zero, Size / _scale);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool IntersectsContent(ref Float2 locationParent, out Float2 location)
|
||||
{
|
||||
// Skip local PointFromParent but use base code
|
||||
location = base.PointFromParent(ref locationParent);
|
||||
return ContainsPoint(ref location);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Float2 PointToParent(ref Float2 location)
|
||||
{
|
||||
var result = base.PointToParent(ref location);
|
||||
result *= _scaleFactor;
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Float2 PointFromParent(ref Float2 location)
|
||||
{
|
||||
var result = base.PointFromParent(ref location);
|
||||
result /= _scaleFactor;
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DragDropEffect OnDragEnter(ref Float2 location, DragData data)
|
||||
{
|
||||
location /= _scale;
|
||||
return base.OnDragEnter(ref location, data);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DragDropEffect OnDragMove(ref Float2 location, DragData data)
|
||||
{
|
||||
location /= _scale;
|
||||
return base.OnDragMove(ref location, data);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DragDropEffect OnDragDrop(ref Float2 location, DragData data)
|
||||
{
|
||||
location /= _scale;
|
||||
return base.OnDragDrop(ref location, data);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnMouseEnter(Float2 location)
|
||||
{
|
||||
location /= _scale;
|
||||
base.OnMouseEnter(location);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnMouseMove(Float2 location)
|
||||
{
|
||||
location /= _scale;
|
||||
base.OnMouseMove(location);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool OnMouseDown(Float2 location, MouseButton button)
|
||||
{
|
||||
location /= _scale;
|
||||
return base.OnMouseDown(location, button);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool OnMouseUp(Float2 location, MouseButton button)
|
||||
{
|
||||
location /= _scale;
|
||||
return base.OnMouseUp(location, button);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool OnMouseDoubleClick(Float2 location, MouseButton button)
|
||||
{
|
||||
location /= _scale;
|
||||
return base.OnMouseDoubleClick(location, button);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool OnMouseWheel(Float2 location, float delta)
|
||||
{
|
||||
location /= _scale;
|
||||
return base.OnMouseWheel(location, delta);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnTouchEnter(Float2 location, int pointerId)
|
||||
{
|
||||
location /= _scale;
|
||||
base.OnTouchEnter(location, pointerId);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnTouchMove(Float2 location, int pointerId)
|
||||
{
|
||||
location /= _scale;
|
||||
base.OnTouchMove(location, pointerId);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool OnTouchDown(Float2 location, int pointerId)
|
||||
{
|
||||
location /= _scale;
|
||||
return base.OnTouchDown(location, pointerId);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool OnTouchUp(Float2 location, int pointerId)
|
||||
{
|
||||
location /= _scale;
|
||||
return base.OnTouchUp(location, pointerId);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1259,6 +1259,13 @@ namespace FlaxEngine.GUI
|
||||
return PointFromParent(ref location);
|
||||
}
|
||||
|
||||
#if FLAX_EDITOR
|
||||
/// <summary>
|
||||
/// Bounds rectangle for editor UI.
|
||||
/// </summary>
|
||||
public virtual Rectangle EditorBounds => new Rectangle(Float2.Zero, _bounds.Size);
|
||||
#endif
|
||||
|
||||
#endregion
|
||||
|
||||
#region Control Action
|
||||
|
||||
@@ -11,9 +11,15 @@ namespace FlaxEngine.GUI
|
||||
/// <summary>
|
||||
/// Gets or sets the blur strength. Defines how blurry the background is. Larger numbers increase blur, resulting in a larger runtime cost on the GPU.
|
||||
/// </summary>
|
||||
[EditorOrder(0), Limit(0, 100, 0.0f), Tooltip("Blur strength defines how blurry the background is. Larger numbers increase blur, resulting in a larger runtime cost on the GPU.")]
|
||||
[EditorOrder(0), Limit(0, 100, 0.0f)]
|
||||
public float BlurStrength { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// If checked, the blur strength will be scaled with the control size, which makes it resolution-independent.
|
||||
/// </summary>
|
||||
[EditorOrder(10)]
|
||||
public bool BlurScaleWithSize { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="BlurPanel"/> class.
|
||||
/// </summary>
|
||||
@@ -27,10 +33,13 @@ namespace FlaxEngine.GUI
|
||||
{
|
||||
base.Draw();
|
||||
|
||||
float strength = BlurStrength;
|
||||
var size = Size;
|
||||
var strength = BlurStrength;
|
||||
if (BlurScaleWithSize)
|
||||
strength *= size.MinValue / 1000.0f;
|
||||
if (strength > Mathf.Epsilon)
|
||||
{
|
||||
Render2D.DrawBlur(new Rectangle(Float2.Zero, Size), strength);
|
||||
Render2D.DrawBlur(new Rectangle(Float2.Zero, size), strength);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -303,6 +303,11 @@ namespace Flax.Build.Bindings
|
||||
case "char": return "mono_get_char_class()";
|
||||
case "IntPtr": return "mono_get_intptr_class()";
|
||||
case "UIntPtr": return "mono_get_uintptr_class()";
|
||||
|
||||
// Vector2/3/4 have custom type in C# (due to lack of typename using in older C#)
|
||||
case "Vector2": return "Scripting::FindClassNative(\"FlaxEngine.Vector2\")";
|
||||
case "Vector3": return "Scripting::FindClassNative(\"FlaxEngine.Vector3\")";
|
||||
case "Vector4": return "Scripting::FindClassNative(\"FlaxEngine.Vector4\")";
|
||||
}
|
||||
|
||||
// Find API type
|
||||
|
||||
@@ -75,6 +75,7 @@ namespace Flax.Build.Plugins
|
||||
{ "FlaxEngine.Float3", new InBuildSerializer("WriteFloat3", "ReadFloat3") },
|
||||
{ "FlaxEngine.Float4", new InBuildSerializer("WriteFloat4", "ReadFloat4") },
|
||||
{ "FlaxEngine.Quaternion", new InBuildSerializer("WriteQuaternion", "ReadQuaternion") },
|
||||
{ "FlaxEngine.Ray", new InBuildSerializer("WriteRay", "ReadRay") },
|
||||
};
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -471,7 +472,7 @@ namespace Flax.Build.Plugins
|
||||
if (string.Equals(binaryModule.Key, "FlaxEngine", StringComparison.Ordinal))
|
||||
return;
|
||||
|
||||
// Skip assemblies not using netowrking
|
||||
// Skip assemblies not using networking
|
||||
if (!binaryModule.Any(module => module.Tags.ContainsKey(Network)))
|
||||
return;
|
||||
|
||||
@@ -1244,15 +1245,16 @@ namespace Flax.Build.Plugins
|
||||
var methodRPC = new MethodRPC();
|
||||
methodRPC.Type = type;
|
||||
methodRPC.Method = method;
|
||||
methodRPC.IsServer = (bool)attribute.GetFieldValue("Server", false);
|
||||
methodRPC.IsClient = (bool)attribute.GetFieldValue("Client", false);
|
||||
methodRPC.Channel = (int)attribute.GetFieldValue("Channel", 4); // int as NetworkChannelType (default is ReliableOrdered=4)
|
||||
methodRPC.Channel = 4; // int as NetworkChannelType (default is ReliableOrdered=4)
|
||||
if (attribute.HasConstructorArguments && attribute.ConstructorArguments.Count >= 3)
|
||||
{
|
||||
methodRPC.IsServer = (bool)attribute.ConstructorArguments[0].Value;
|
||||
methodRPC.IsClient = (bool)attribute.ConstructorArguments[1].Value;
|
||||
methodRPC.Channel = (int)attribute.ConstructorArguments[2].Value;
|
||||
}
|
||||
methodRPC.IsServer = (bool)attribute.GetFieldValue("Server", methodRPC.IsServer);
|
||||
methodRPC.IsClient = (bool)attribute.GetFieldValue("Client", methodRPC.IsClient);
|
||||
methodRPC.Channel = (int)attribute.GetFieldValue("Channel", methodRPC.Channel);
|
||||
if (methodRPC.IsServer && methodRPC.IsClient)
|
||||
{
|
||||
MonoCecil.CompilationError($"Network RPC {method.Name} in {type.FullName} cannot be both Server and Client.", method);
|
||||
@@ -1358,7 +1360,9 @@ namespace Flax.Build.Plugins
|
||||
var jumpIfBodyStart = il.Create(OpCodes.Nop); // if block body
|
||||
var jumpIf2Start = il.Create(OpCodes.Nop); // 2nd part of the if
|
||||
var jumpBodyStart = il.Create(OpCodes.Nop); // original method body start
|
||||
var jumpBodyEnd = il.Body.Instructions.First(x => x.OpCode == OpCodes.Ret);
|
||||
var jumpBodyEnd = il.Body.Instructions.Last(x => x.OpCode == OpCodes.Ret && x.Previous != null);
|
||||
if (jumpBodyEnd == null)
|
||||
throw new Exception("Missing IL Return op code in method " + method.Name);
|
||||
il.InsertBefore(ilStart, il.Create(OpCodes.Ldloc, varsStart + 0));
|
||||
il.InsertBefore(ilStart, il.Create(OpCodes.Brfalse_S, jumpIf2Start));
|
||||
il.InsertBefore(ilStart, il.Create(OpCodes.Ldloc, varsStart + 2));
|
||||
@@ -1408,7 +1412,13 @@ namespace Flax.Build.Plugins
|
||||
il.InsertBefore(ilStart, il.Create(OpCodes.Nop));
|
||||
il.InsertBefore(ilStart, il.Create(OpCodes.Ldloc, varsStart + 2));
|
||||
il.InsertBefore(ilStart, il.Create(OpCodes.Ldc_I4_2));
|
||||
il.InsertBefore(ilStart, il.Create(OpCodes.Beq_S, jumpBodyEnd));
|
||||
var tmp = il.Create(OpCodes.Nop);
|
||||
il.InsertBefore(ilStart, il.Create(OpCodes.Beq_S, tmp));
|
||||
il.InsertBefore(ilStart, il.Create(OpCodes.Br, jumpBodyStart));
|
||||
il.InsertBefore(ilStart, tmp);
|
||||
//il.InsertBefore(ilStart, il.Create(OpCodes.Ret));
|
||||
il.InsertBefore(ilStart, il.Create(OpCodes.Br, jumpBodyEnd));
|
||||
|
||||
}
|
||||
|
||||
// if (client && networkMode == NetworkManagerMode.Server) return;
|
||||
@@ -1417,7 +1427,12 @@ namespace Flax.Build.Plugins
|
||||
il.InsertBefore(ilStart, il.Create(OpCodes.Nop));
|
||||
il.InsertBefore(ilStart, il.Create(OpCodes.Ldloc, varsStart + 2));
|
||||
il.InsertBefore(ilStart, il.Create(OpCodes.Ldc_I4_1));
|
||||
il.InsertBefore(ilStart, il.Create(OpCodes.Beq_S, jumpBodyEnd));
|
||||
var tmp = il.Create(OpCodes.Nop);
|
||||
il.InsertBefore(ilStart, il.Create(OpCodes.Beq_S, tmp));
|
||||
il.InsertBefore(ilStart, il.Create(OpCodes.Br, jumpBodyStart));
|
||||
il.InsertBefore(ilStart, tmp);
|
||||
//il.InsertBefore(ilStart, il.Create(OpCodes.Ret));
|
||||
il.InsertBefore(ilStart, il.Create(OpCodes.Br, jumpBodyEnd));
|
||||
}
|
||||
|
||||
// Continue to original method body
|
||||
|
||||
@@ -54,6 +54,7 @@ namespace Flax.Deploy
|
||||
DeployFile(src, dst, "Flax.Build.xml");
|
||||
DeployFile(src, dst, "Ionic.Zip.Reduced.dll");
|
||||
DeployFile(src, dst, "Newtonsoft.Json.dll");
|
||||
DeployFile(src, dst, "Mono.Cecil.dll");
|
||||
}
|
||||
|
||||
// Deploy content
|
||||
|
||||
Reference in New Issue
Block a user