Merge branch 'master' of github.com:nothingTVatYT/FlaxEngine
This commit is contained in:
@@ -486,7 +486,7 @@ namespace FlaxEditor.Content
|
||||
else
|
||||
Render2D.FillRectangle(rectangle, Color.Black);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Draws the item thumbnail.
|
||||
/// </summary>
|
||||
@@ -684,7 +684,7 @@ namespace FlaxEditor.Content
|
||||
var thumbnailSize = size.X;
|
||||
thumbnailRect = new Rectangle(0, 0, thumbnailSize, thumbnailSize);
|
||||
nameAlignment = TextAlignment.Center;
|
||||
|
||||
|
||||
if (this is ContentFolder)
|
||||
{
|
||||
// Small shadow
|
||||
@@ -692,7 +692,7 @@ namespace FlaxEditor.Content
|
||||
var color = Color.Black.AlphaMultiplied(0.2f);
|
||||
Render2D.FillRectangle(shadowRect, color);
|
||||
Render2D.FillRectangle(clientRect, style.Background.RGBMultiplied(1.25f));
|
||||
|
||||
|
||||
if (isSelected)
|
||||
Render2D.FillRectangle(clientRect, Parent.ContainsFocus ? style.BackgroundSelected : style.LightBackground);
|
||||
else if (IsMouseOver)
|
||||
@@ -706,14 +706,14 @@ namespace FlaxEditor.Content
|
||||
var shadowRect = new Rectangle(2, 2, clientRect.Width + 1, clientRect.Height + 1);
|
||||
var color = Color.Black.AlphaMultiplied(0.2f);
|
||||
Render2D.FillRectangle(shadowRect, color);
|
||||
|
||||
|
||||
Render2D.FillRectangle(clientRect, style.Background.RGBMultiplied(1.25f));
|
||||
Render2D.FillRectangle(TextRectangle, style.LightBackground);
|
||||
|
||||
|
||||
var accentHeight = 2 * view.ViewScale;
|
||||
var barRect = new Rectangle(0, thumbnailRect.Height - accentHeight, clientRect.Width, accentHeight);
|
||||
Render2D.FillRectangle(barRect, Color.DimGray);
|
||||
|
||||
|
||||
DrawThumbnail(ref thumbnailRect, false);
|
||||
if (isSelected)
|
||||
{
|
||||
@@ -733,18 +733,18 @@ namespace FlaxEditor.Content
|
||||
var thumbnailSize = size.Y - 2 * DefaultMarginSize;
|
||||
thumbnailRect = new Rectangle(DefaultMarginSize, DefaultMarginSize, thumbnailSize, thumbnailSize);
|
||||
nameAlignment = TextAlignment.Near;
|
||||
|
||||
|
||||
if (isSelected)
|
||||
Render2D.FillRectangle(clientRect, Parent.ContainsFocus ? style.BackgroundSelected : style.LightBackground);
|
||||
else if (IsMouseOver)
|
||||
Render2D.FillRectangle(clientRect, style.BackgroundHighlighted);
|
||||
|
||||
|
||||
DrawThumbnail(ref thumbnailRect);
|
||||
break;
|
||||
}
|
||||
default: throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
|
||||
|
||||
// Draw short name
|
||||
Render2D.PushClip(ref textRect);
|
||||
Render2D.DrawText(style.FontMedium, ShowFileExtension || view.ShowFileExtensions ? FileName : ShortName, textRect, style.Foreground, nameAlignment, TextAlignment.Center, TextWrapping.WrapWords, 1f, 0.95f);
|
||||
|
||||
@@ -20,7 +20,7 @@ public class MissingScriptEditor : GenericEditor
|
||||
}
|
||||
|
||||
dropPanel.HeaderTextColor = Color.OrangeRed;
|
||||
|
||||
|
||||
base.Initialize(layout);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -348,7 +348,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
|
||||
if (!CanEditTangent())
|
||||
return;
|
||||
|
||||
|
||||
var index = _lastPointSelected.Index;
|
||||
var currentTangentInPosition = _selectedSpline.GetSplineLocalTangent(index, true).Translation;
|
||||
var currentTangentOutPosition = _selectedSpline.GetSplineLocalTangent(index, false).Translation;
|
||||
|
||||
@@ -88,7 +88,7 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
LinkValues = Editor.Instance.Windows.PropertiesWin.ScaleLinked;
|
||||
|
||||
// Add button with the link icon
|
||||
|
||||
|
||||
_linkButton = new Button
|
||||
{
|
||||
BackgroundBrush = new SpriteBrush(Editor.Instance.Icons.Link32),
|
||||
|
||||
@@ -160,7 +160,6 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
var option = _options[comboBox.SelectedIndex];
|
||||
if (option.Type != null)
|
||||
value = option.Creator(option.Type);
|
||||
|
||||
}
|
||||
SetValue(value);
|
||||
RebuildLayoutOnRefresh();
|
||||
|
||||
@@ -634,7 +634,7 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
var textSize = FlaxEngine.GUI.Style.Current.FontMedium.MeasureText(buttonText);
|
||||
if (textSize.Y > button.Width)
|
||||
button.Width = textSize.Y + 2;
|
||||
|
||||
|
||||
button.SetAnchorPreset(AnchorPresets.MiddleRight, false, true);
|
||||
button.Clicked += ShowPicker;
|
||||
}
|
||||
|
||||
@@ -175,7 +175,7 @@ namespace FlaxEditor.CustomEditors.GUI
|
||||
{
|
||||
// Clear flag
|
||||
_mouseOverSplitter = false;
|
||||
|
||||
|
||||
if (_cursorChanged)
|
||||
{
|
||||
Cursor = CursorType.Default;
|
||||
|
||||
@@ -106,7 +106,7 @@ namespace FlaxEditor.Options
|
||||
[DefaultValue(60.0f), Limit(0, 666)]
|
||||
[EditorDisplay("General", "Editor FPS"), EditorOrder(110), Tooltip("Limit for the editor draw/update frames per second rate (FPS). Use higher values if you need more responsive interface or lower values to use less device power. Value 0 disables any limits.")]
|
||||
public float EditorFPS { get; set; } = 60.0f;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets The FPS of the editor when the editor window is not focused. Usually set to lower then the editor FPS.
|
||||
/// </summary>
|
||||
@@ -203,7 +203,7 @@ namespace FlaxEditor.Options
|
||||
[DefaultValue(5), Limit(1)]
|
||||
[EditorDisplay("Auto Save", "Auto Save Frequency"), EditorOrder(801), Tooltip("The interval between auto saves (in minutes)")]
|
||||
public int AutoSaveFrequency { get; set; } = 5;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating the time before the auto save that the popup shows (in seconds).
|
||||
/// </summary>
|
||||
|
||||
@@ -166,7 +166,19 @@ namespace FlaxEditor.Options
|
||||
/// Gets or sets the output log text font.
|
||||
/// </summary>
|
||||
[EditorDisplay("Output Log", "Text Font"), EditorOrder(320), Tooltip("The output log text font.")]
|
||||
public FontReference OutputLogTextFont { get; set; } = new FontReference(FlaxEngine.Content.LoadAsyncInternal<FontAsset>(EditorAssets.InconsolataRegularFont), 10);
|
||||
public FontReference OutputLogTextFont
|
||||
{
|
||||
get => _outputLogFont;
|
||||
set
|
||||
{
|
||||
if (value == null)
|
||||
_outputLogFont = new FontReference(FlaxEngine.Content.LoadAsyncInternal<FontAsset>(EditorAssets.InconsolataRegularFont), 10);
|
||||
else if (!value.Font)
|
||||
_outputLogFont.Font = FlaxEngine.Content.LoadAsyncInternal<FontAsset>(EditorAssets.InconsolataRegularFont);
|
||||
else
|
||||
_outputLogFont = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the output log text color.
|
||||
@@ -225,29 +237,82 @@ namespace FlaxEditor.Options
|
||||
public int NumberOfGameClientsToLaunch = 1;
|
||||
|
||||
private static FontAsset DefaultFont => FlaxEngine.Content.LoadAsyncInternal<FontAsset>(EditorAssets.PrimaryFont);
|
||||
private FontReference _titleFont = new FontReference(DefaultFont, 18);
|
||||
private FontReference _largeFont = new FontReference(DefaultFont, 14);
|
||||
private FontReference _mediumFont = new FontReference(DefaultFont, 9);
|
||||
private FontReference _smallFont = new FontReference(DefaultFont, 9);
|
||||
private FontReference _outputLogFont = new FontReference(FlaxEngine.Content.LoadAsyncInternal<FontAsset>(EditorAssets.InconsolataRegularFont), 10);
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the title font for editor UI.
|
||||
/// </summary>
|
||||
[EditorDisplay("Fonts"), EditorOrder(600), Tooltip("The title font for editor UI.")]
|
||||
public FontReference TitleFont { get; set; } = new FontReference(DefaultFont, 18);
|
||||
public FontReference TitleFont
|
||||
{
|
||||
get => _titleFont;
|
||||
set
|
||||
{
|
||||
if (value == null)
|
||||
_titleFont = new FontReference(DefaultFont, 18);
|
||||
else if (!value.Font)
|
||||
_titleFont.Font = DefaultFont;
|
||||
else
|
||||
_titleFont = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the large font for editor UI.
|
||||
/// </summary>
|
||||
[EditorDisplay("Fonts"), EditorOrder(610), Tooltip("The large font for editor UI.")]
|
||||
public FontReference LargeFont { get; set; } = new FontReference(DefaultFont, 14);
|
||||
public FontReference LargeFont
|
||||
{
|
||||
get => _largeFont;
|
||||
set
|
||||
{
|
||||
if (value == null)
|
||||
_largeFont = new FontReference(DefaultFont, 14);
|
||||
else if (!value.Font)
|
||||
_largeFont.Font = DefaultFont;
|
||||
else
|
||||
_largeFont = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the medium font for editor UI.
|
||||
/// </summary>
|
||||
[EditorDisplay("Fonts"), EditorOrder(620), Tooltip("The medium font for editor UI.")]
|
||||
public FontReference MediumFont { get; set; } = new FontReference(DefaultFont, 9);
|
||||
public FontReference MediumFont
|
||||
{
|
||||
get => _mediumFont;
|
||||
set
|
||||
{
|
||||
if (value == null)
|
||||
_mediumFont = new FontReference(DefaultFont, 9);
|
||||
else if (!value.Font)
|
||||
_mediumFont.Font = DefaultFont;
|
||||
else
|
||||
_mediumFont = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the small font for editor UI.
|
||||
/// </summary>
|
||||
[EditorDisplay("Fonts"), EditorOrder(630), Tooltip("The small font for editor UI.")]
|
||||
public FontReference SmallFont { get; set; } = new FontReference(DefaultFont, 9);
|
||||
public FontReference SmallFont
|
||||
{
|
||||
get => _smallFont;
|
||||
set
|
||||
{
|
||||
if (value == null)
|
||||
_smallFont = new FontReference(DefaultFont, 9);
|
||||
else if (!value.Font)
|
||||
_smallFont.Font = DefaultFont;
|
||||
else
|
||||
_smallFont = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1393,5 +1393,32 @@ namespace FlaxEditor.Scripting
|
||||
return _custom.GetMembers(name, MemberTypes.Method, bindingAttr).FirstOrDefault();
|
||||
return ScriptMemberInfo.Null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Basic check to see if a type could be casted to another type
|
||||
/// </summary>
|
||||
/// <param name="from">Source type</param>
|
||||
/// <param name="to">Target type</param>
|
||||
/// <returns>True if the type can be casted</returns>
|
||||
public static bool CanCast(ScriptType from, ScriptType to)
|
||||
{
|
||||
if (from == to)
|
||||
return true;
|
||||
if (from == Null || to == Null)
|
||||
return false;
|
||||
return (from.Type != typeof(void) && from.Type != typeof(FlaxEngine.Object)) &&
|
||||
(to.Type != typeof(void) && to.Type != typeof(FlaxEngine.Object)) &&
|
||||
from.IsAssignableFrom(to);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Basic check to see if this type could be casted to another type
|
||||
/// </summary>
|
||||
/// <param name="to">Target type</param>
|
||||
/// <returns>True if the type can be casted</returns>
|
||||
public bool CanCastTo(ScriptType to)
|
||||
{
|
||||
return CanCast(this, to);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -744,6 +744,16 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
|
||||
base.OnDestroy();
|
||||
}
|
||||
|
||||
internal static bool IsInputCompatible(NodeArchetype nodeArch, ScriptType outputType, ConnectionsHint hint)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
internal static bool IsOutputCompatible(NodeArchetype nodeArch, ScriptType inputType, ConnectionsHint hint)
|
||||
{
|
||||
return inputType.IsVoid;
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class InvokeMethodNode : SurfaceNode
|
||||
@@ -1151,6 +1161,54 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
|
||||
base.OnDestroy();
|
||||
}
|
||||
|
||||
internal static bool IsInputCompatible(NodeArchetype nodeArch, ScriptType outputType, ConnectionsHint hint)
|
||||
{
|
||||
if (nodeArch.Tag is not ScriptMemberInfo memberInfo)
|
||||
return false;
|
||||
|
||||
if (!memberInfo.IsStatic)
|
||||
{
|
||||
if (VisjectSurface.FullCastCheck(memberInfo.DeclaringType, outputType, hint))
|
||||
return true;
|
||||
}
|
||||
|
||||
var parameters = memberInfo.GetParameters();
|
||||
bool isPure = (parameters.Length == 0 && !memberInfo.ValueType.IsVoid);
|
||||
if (outputType.IsVoid)
|
||||
return !isPure;
|
||||
|
||||
foreach (var param in parameters)
|
||||
{
|
||||
if (param.IsOut)
|
||||
continue;
|
||||
if (VisjectSurface.FullCastCheck(param.Type, outputType, hint))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
internal static bool IsOutputCompatible(NodeArchetype nodeArch, ScriptType inputType, ConnectionsHint hint)
|
||||
{
|
||||
if (nodeArch.Tag is not ScriptMemberInfo memberInfo)
|
||||
return false;
|
||||
if (VisjectSurface.FullCastCheck(memberInfo.ValueType, inputType, hint))
|
||||
return true;
|
||||
|
||||
var parameters = memberInfo.GetParameters();
|
||||
bool isPure = (parameters.Length == 0 && !memberInfo.ValueType.IsVoid);
|
||||
if (inputType.IsVoid)
|
||||
return !isPure;
|
||||
|
||||
foreach (var param in memberInfo.GetParameters())
|
||||
{
|
||||
if (!param.IsOut)
|
||||
continue;
|
||||
if (VisjectSurface.FullCastCheck(param.Type, inputType, hint))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class ReturnNode : SurfaceNode
|
||||
@@ -1777,6 +1835,16 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
|
||||
base.OnDestroy();
|
||||
}
|
||||
|
||||
internal static bool IsInputCompatible(NodeArchetype nodeArch, ScriptType outputType, ConnectionsHint hint)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
internal static bool IsOutputCompatible(NodeArchetype nodeArch, ScriptType inputType, ConnectionsHint hint)
|
||||
{
|
||||
return inputType.IsVoid;
|
||||
}
|
||||
}
|
||||
|
||||
private abstract class FieldNodeBase : SurfaceNode
|
||||
@@ -1913,6 +1981,64 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
Title = "Get " + SurfaceUtils.GetMethodDisplayName((string)Values[1]);
|
||||
UpdateSignature();
|
||||
}
|
||||
|
||||
internal static bool IsInputCompatible(NodeArchetype nodeArch, ScriptType outputType, ConnectionsHint hint)
|
||||
{
|
||||
var scriptType = TypeUtils.GetType((string)nodeArch.DefaultValues[0]);
|
||||
if (scriptType == ScriptType.Null)
|
||||
return false;
|
||||
|
||||
var members = scriptType.GetMembers((string)nodeArch.DefaultValues[1], MemberTypes.Field, BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly);
|
||||
foreach (var member in members)
|
||||
{
|
||||
if (!SurfaceUtils.IsValidVisualScriptField(member))
|
||||
continue;
|
||||
|
||||
if (member)
|
||||
{
|
||||
if (!member.IsStatic && VisjectSurface.FullCastCheck(scriptType, outputType, hint))
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
var isStatic = (bool)nodeArch.DefaultValues[3];
|
||||
if (!isStatic && VisjectSurface.FullCastCheck(scriptType, outputType, hint))
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
internal static bool IsOutputCompatible(NodeArchetype nodeArch, ScriptType inputType, ConnectionsHint hint)
|
||||
{
|
||||
var scriptType = TypeUtils.GetType((string)nodeArch.DefaultValues[0]);
|
||||
if (scriptType == ScriptType.Null)
|
||||
return false;
|
||||
|
||||
var members = scriptType.GetMembers((string)nodeArch.DefaultValues[1], MemberTypes.Field, BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly);
|
||||
foreach (var member in members)
|
||||
{
|
||||
if (!SurfaceUtils.IsValidVisualScriptField(member))
|
||||
continue;
|
||||
|
||||
if (member)
|
||||
{
|
||||
if (VisjectSurface.FullCastCheck(member.ValueType, inputType, hint))
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
var typeName = (string)nodeArch.DefaultValues[2];
|
||||
if (VisjectSurface.FullCastCheck(TypeUtils.GetType(typeName), inputType, hint))
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class SetFieldNode : FieldNodeBase
|
||||
@@ -1966,6 +2092,48 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
Title = "Set " + SurfaceUtils.GetMethodDisplayName((string)Values[1]);
|
||||
UpdateSignature();
|
||||
}
|
||||
|
||||
internal static bool IsInputCompatible(NodeArchetype nodeArch, ScriptType outputType, ConnectionsHint hint)
|
||||
{
|
||||
if (outputType.IsVoid)
|
||||
return true;
|
||||
|
||||
var scriptType = TypeUtils.GetType((string)nodeArch.DefaultValues[0]);
|
||||
if (scriptType == ScriptType.Null)
|
||||
return false;
|
||||
|
||||
var members = scriptType.GetMembers((string)nodeArch.DefaultValues[1], MemberTypes.Field, BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly);
|
||||
foreach (var member in members)
|
||||
{
|
||||
if (!SurfaceUtils.IsValidVisualScriptField(member))
|
||||
continue;
|
||||
|
||||
if (member)
|
||||
{
|
||||
if (VisjectSurface.FullCastCheck(member.ValueType, outputType, hint))
|
||||
return true;
|
||||
if (!member.IsStatic && VisjectSurface.FullCastCheck(scriptType, outputType, hint))
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
var typeName = (string)nodeArch.DefaultValues[2];
|
||||
if (VisjectSurface.FullCastCheck(TypeUtils.GetType(typeName), outputType, hint))
|
||||
return true;
|
||||
var isStatic = (bool)nodeArch.DefaultValues[3];
|
||||
if (!isStatic && VisjectSurface.FullCastCheck(scriptType, outputType, hint))
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
internal static bool IsOutputCompatible(NodeArchetype nodeArch, ScriptType inputType, ConnectionsHint hint)
|
||||
{
|
||||
return inputType.IsVoid;
|
||||
}
|
||||
}
|
||||
|
||||
private abstract class EventBaseNode : SurfaceNode, IFunctionsDependantNode
|
||||
@@ -2184,6 +2352,43 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
|
||||
base.OnDestroy();
|
||||
}
|
||||
|
||||
internal static bool IsInputCompatible(NodeArchetype nodeArch, ScriptType outputType, ConnectionsHint hint)
|
||||
{
|
||||
// Event based nodes always have a pulse input, so it's always compatible with void
|
||||
if (outputType.IsVoid)
|
||||
return true;
|
||||
|
||||
var eventName = (string)nodeArch.DefaultValues[1];
|
||||
var eventType = TypeUtils.GetType((string)nodeArch.DefaultValues[0]);
|
||||
var member = eventType.GetMember(eventName, MemberTypes.Event, BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance);
|
||||
if (member && SurfaceUtils.IsValidVisualScriptEvent(member))
|
||||
{
|
||||
if (!member.IsStatic)
|
||||
{
|
||||
if (VisjectSurface.FullCastCheck(eventType, outputType, hint))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
internal static bool IsOutputCompatible(NodeArchetype nodeArch, ScriptType inputType, ConnectionsHint hint)
|
||||
{
|
||||
// Event based nodes always have a pulse output, so it's always compatible with void
|
||||
if (inputType.IsVoid)
|
||||
return true;
|
||||
|
||||
var eventName = (string)nodeArch.DefaultValues[1];
|
||||
var eventType = TypeUtils.GetType((string)nodeArch.DefaultValues[0]);
|
||||
var member = eventType.GetMember(eventName, MemberTypes.Event, BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance);
|
||||
if (member && SurfaceUtils.IsValidVisualScriptEvent(member))
|
||||
{
|
||||
if (VisjectSurface.FullCastCheck(member.ValueType, inputType, hint))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class BindEventNode : EventBaseNode
|
||||
@@ -2265,6 +2470,8 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
Title = string.Empty,
|
||||
Description = "Overrides the base class method with custom implementation",
|
||||
Flags = NodeFlags.VisualScriptGraph | NodeFlags.NoSpawnViaGUI | NodeFlags.NoSpawnViaPaste,
|
||||
IsInputCompatible = MethodOverrideNode.IsInputCompatible,
|
||||
IsOutputCompatible = MethodOverrideNode.IsOutputCompatible,
|
||||
Size = new Float2(240, 60),
|
||||
DefaultValues = new object[]
|
||||
{
|
||||
@@ -2277,6 +2484,8 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
{
|
||||
TypeID = 4,
|
||||
Create = (id, context, arch, groupArch) => new InvokeMethodNode(id, context, arch, groupArch),
|
||||
IsInputCompatible = InvokeMethodNode.IsInputCompatible,
|
||||
IsOutputCompatible = InvokeMethodNode.IsOutputCompatible,
|
||||
Title = string.Empty,
|
||||
Flags = NodeFlags.VisualScriptGraph | NodeFlags.NoSpawnViaGUI,
|
||||
Size = new Float2(240, 60),
|
||||
@@ -2317,6 +2526,8 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
{
|
||||
TypeID = 6,
|
||||
Create = (id, context, arch, groupArch) => new VisualScriptFunctionNode(id, context, arch, groupArch),
|
||||
IsInputCompatible = VisualScriptFunctionNode.IsInputCompatible,
|
||||
IsOutputCompatible = VisualScriptFunctionNode.IsOutputCompatible,
|
||||
Title = "New Function",
|
||||
Description = "Adds a new function to the script",
|
||||
Flags = NodeFlags.VisualScriptGraph,
|
||||
@@ -2330,6 +2541,8 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
{
|
||||
TypeID = 7,
|
||||
Create = (id, context, arch, groupArch) => new GetFieldNode(id, context, arch, groupArch),
|
||||
IsInputCompatible = GetFieldNode.IsInputCompatible,
|
||||
IsOutputCompatible = GetFieldNode.IsOutputCompatible,
|
||||
Title = string.Empty,
|
||||
Flags = NodeFlags.VisualScriptGraph | NodeFlags.NoSpawnViaGUI,
|
||||
Size = new Float2(240, 60),
|
||||
@@ -2345,6 +2558,8 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
{
|
||||
TypeID = 8,
|
||||
Create = (id, context, arch, groupArch) => new SetFieldNode(id, context, arch, groupArch),
|
||||
IsInputCompatible = SetFieldNode.IsInputCompatible,
|
||||
IsOutputCompatible = SetFieldNode.IsOutputCompatible,
|
||||
Title = string.Empty,
|
||||
Flags = NodeFlags.VisualScriptGraph | NodeFlags.NoSpawnViaGUI,
|
||||
Size = new Float2(240, 60),
|
||||
@@ -2361,6 +2576,8 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
{
|
||||
TypeID = 9,
|
||||
Create = (id, context, arch, groupArch) => new BindEventNode(id, context, arch, groupArch),
|
||||
IsInputCompatible = EventBaseNode.IsInputCompatible,
|
||||
IsOutputCompatible = EventBaseNode.IsOutputCompatible,
|
||||
Title = string.Empty,
|
||||
Flags = NodeFlags.VisualScriptGraph | NodeFlags.NoSpawnViaGUI,
|
||||
Size = new Float2(260, 60),
|
||||
@@ -2383,6 +2600,8 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
{
|
||||
TypeID = 10,
|
||||
Create = (id, context, arch, groupArch) => new UnbindEventNode(id, context, arch, groupArch),
|
||||
IsInputCompatible = EventBaseNode.IsInputCompatible,
|
||||
IsOutputCompatible = EventBaseNode.IsOutputCompatible,
|
||||
Title = string.Empty,
|
||||
Flags = NodeFlags.VisualScriptGraph | NodeFlags.NoSpawnViaGUI,
|
||||
Size = new Float2(260, 60),
|
||||
|
||||
@@ -216,6 +216,35 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
: base(id, context, nodeArch, groupArch, false)
|
||||
{
|
||||
}
|
||||
|
||||
internal static bool IsInputCompatible(NodeArchetype nodeArch, ScriptType outputType, ConnectionsHint hint)
|
||||
{
|
||||
var typeName = (string)nodeArch.DefaultValues[0];
|
||||
var type = TypeUtils.GetType(typeName);
|
||||
if (type)
|
||||
{
|
||||
var fields = type.GetMembers(BindingFlags.Public | BindingFlags.Instance).Where(x => x.IsField).ToArray();
|
||||
var fieldsLength = fields.Length;
|
||||
for (var i = 0; i < fieldsLength; i++)
|
||||
{
|
||||
if (VisjectSurface.FullCastCheck(fields[i].ValueType, outputType, hint))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
internal static bool IsOutputCompatible(NodeArchetype nodeArch, ScriptType inputType, ConnectionsHint hint)
|
||||
{
|
||||
var typeName = (string)nodeArch.DefaultValues[0];
|
||||
var type = TypeUtils.GetType(typeName);
|
||||
if (type)
|
||||
{
|
||||
if (VisjectSurface.FullCastCheck(type, inputType, hint))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class UnpackStructureNode : StructureNode
|
||||
@@ -225,6 +254,35 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
: base(id, context, nodeArch, groupArch, true)
|
||||
{
|
||||
}
|
||||
|
||||
internal static bool IsInputCompatible(NodeArchetype nodeArch, ScriptType outputType, ConnectionsHint hint)
|
||||
{
|
||||
var typeName = (string)nodeArch.DefaultValues[0];
|
||||
var type = TypeUtils.GetType(typeName);
|
||||
if (type)
|
||||
{
|
||||
if (VisjectSurface.FullCastCheck(type, outputType, hint))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
internal static bool IsOutputCompatible(NodeArchetype nodeArch, ScriptType inputType, ConnectionsHint hint)
|
||||
{
|
||||
var typeName = (string)nodeArch.DefaultValues[0];
|
||||
var type = TypeUtils.GetType(typeName);
|
||||
if (type)
|
||||
{
|
||||
var fields = type.GetMembers(BindingFlags.Public | BindingFlags.Instance).Where(x => x.IsField).ToArray();
|
||||
var fieldsLength = fields.Length;
|
||||
for (var i = 0; i < fieldsLength; i++)
|
||||
{
|
||||
if (VisjectSurface.FullCastCheck(fields[i].ValueType, inputType, hint))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -351,6 +409,8 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
TypeID = 26,
|
||||
Title = "Pack Structure",
|
||||
Create = (id, context, arch, groupArch) => new PackStructureNode(id, context, arch, groupArch),
|
||||
IsInputCompatible = PackStructureNode.IsInputCompatible,
|
||||
IsOutputCompatible = PackStructureNode.IsOutputCompatible,
|
||||
Description = "Makes the structure data to from the components.",
|
||||
Flags = NodeFlags.VisualScriptGraph | NodeFlags.AnimGraph | NodeFlags.NoSpawnViaGUI,
|
||||
Size = new Float2(180, 20),
|
||||
@@ -461,6 +521,8 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
TypeID = 36,
|
||||
Title = "Unpack Structure",
|
||||
Create = (id, context, arch, groupArch) => new UnpackStructureNode(id, context, arch, groupArch),
|
||||
IsInputCompatible = UnpackStructureNode.IsInputCompatible,
|
||||
IsOutputCompatible = UnpackStructureNode.IsOutputCompatible,
|
||||
Description = "Breaks the structure data to allow extracting components from it.",
|
||||
Flags = NodeFlags.VisualScriptGraph | NodeFlags.AnimGraph | NodeFlags.NoSpawnViaGUI,
|
||||
Size = new Float2(180, 20),
|
||||
|
||||
@@ -5,7 +5,6 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using FlaxEditor.GUI.ContextMenu;
|
||||
using FlaxEditor.GUI.Input;
|
||||
using FlaxEditor.Scripting;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
using FlaxEngine.Utilities;
|
||||
@@ -40,6 +39,8 @@ namespace FlaxEditor.Surface.ContextMenu
|
||||
public delegate List<SurfaceParameter> ParameterGetterDelegate();
|
||||
|
||||
private readonly List<VisjectCMGroup> _groups = new List<VisjectCMGroup>(16);
|
||||
private CheckBox _contextSensitiveToggle;
|
||||
private bool _contextSensitiveSearchEnabled = true;
|
||||
private readonly TextBox _searchBox;
|
||||
private bool _waitingForInput;
|
||||
private VisjectCMGroup _surfaceParametersGroup;
|
||||
@@ -127,7 +128,7 @@ namespace FlaxEditor.Surface.ContextMenu
|
||||
_parameterSetNodeArchetype = info.ParameterSetNodeArchetype ?? Archetypes.Parameters.Nodes[3];
|
||||
|
||||
// Context menu dimensions
|
||||
Size = new Float2(320, 248);
|
||||
Size = new Float2(300, 400);
|
||||
|
||||
var headerPanel = new Panel(ScrollBars.None)
|
||||
{
|
||||
@@ -139,17 +140,41 @@ namespace FlaxEditor.Surface.ContextMenu
|
||||
};
|
||||
|
||||
// Title bar
|
||||
var titleFontReference = new FontReference(Style.Current.FontLarge.Asset, 10);
|
||||
var titleLabel = new Label
|
||||
{
|
||||
Width = Width - 8,
|
||||
Width = Width * 0.5f - 8f,
|
||||
Height = 20,
|
||||
X = 4,
|
||||
Parent = headerPanel,
|
||||
Text = "Select Node",
|
||||
HorizontalAlignment = TextAlignment.Center,
|
||||
Font = new FontReference(Style.Current.FontLarge.Asset, 10),
|
||||
HorizontalAlignment = TextAlignment.Near,
|
||||
Font = titleFontReference,
|
||||
};
|
||||
|
||||
// Context sensitive toggle
|
||||
var contextSensitiveLabel = new Label
|
||||
{
|
||||
Width = Width * 0.5f - 28,
|
||||
Height = 20,
|
||||
X = Width * 0.5f,
|
||||
Parent = headerPanel,
|
||||
Text = "Context Sensitive",
|
||||
TooltipText = "Should the nodes be filtered to only show those that can be connected in the current context?",
|
||||
HorizontalAlignment = TextAlignment.Far,
|
||||
Font = titleFontReference,
|
||||
};
|
||||
|
||||
_contextSensitiveToggle = new CheckBox
|
||||
{
|
||||
Width = 20,
|
||||
Height = 20,
|
||||
X = Width - 24,
|
||||
Parent = headerPanel,
|
||||
Checked = _contextSensitiveSearchEnabled,
|
||||
};
|
||||
_contextSensitiveToggle.StateChanged += OnContextSensitiveToggleStateChanged;
|
||||
|
||||
// Search box
|
||||
_searchBox = new SearchBox(false, 2, 22)
|
||||
{
|
||||
@@ -288,6 +313,10 @@ namespace FlaxEditor.Surface.ContextMenu
|
||||
OnSearchFilterChanged();
|
||||
}
|
||||
}
|
||||
else if (_contextSensitiveSearchEnabled)
|
||||
{
|
||||
group.EvaluateVisibilityWithBox(_selectedBox);
|
||||
}
|
||||
|
||||
Profiler.EndEvent();
|
||||
}
|
||||
@@ -321,6 +350,8 @@ namespace FlaxEditor.Surface.ContextMenu
|
||||
Parent = group
|
||||
};
|
||||
}
|
||||
if (_contextSensitiveSearchEnabled)
|
||||
group.EvaluateVisibilityWithBox(_selectedBox);
|
||||
group.SortChildren();
|
||||
group.Parent = _groupsPanel;
|
||||
_groups.Add(group);
|
||||
@@ -418,8 +449,26 @@ namespace FlaxEditor.Surface.ContextMenu
|
||||
return;
|
||||
|
||||
Profiler.BeginEvent("VisjectCM.OnSearchFilterChanged");
|
||||
|
||||
if (string.IsNullOrEmpty(_searchBox.Text))
|
||||
UpdateFilters();
|
||||
_searchBox.Focus();
|
||||
Profiler.EndEvent();
|
||||
}
|
||||
|
||||
private void OnContextSensitiveToggleStateChanged(CheckBox checkBox)
|
||||
{
|
||||
// Skip events during setup or init stuff
|
||||
if (IsLayoutLocked)
|
||||
return;
|
||||
|
||||
Profiler.BeginEvent("VisjectCM.OnContextSensitiveToggleStateChanged");
|
||||
_contextSensitiveSearchEnabled = checkBox.Checked;
|
||||
UpdateFilters();
|
||||
Profiler.EndEvent();
|
||||
}
|
||||
|
||||
private void UpdateFilters()
|
||||
{
|
||||
if (string.IsNullOrEmpty(_searchBox.Text) && _selectedBox == null)
|
||||
{
|
||||
ResetView();
|
||||
Profiler.EndEvent();
|
||||
@@ -430,7 +479,7 @@ namespace FlaxEditor.Surface.ContextMenu
|
||||
LockChildrenRecursive();
|
||||
for (int i = 0; i < _groups.Count; i++)
|
||||
{
|
||||
_groups[i].UpdateFilter(_searchBox.Text);
|
||||
_groups[i].UpdateFilter(_searchBox.Text, _contextSensitiveSearchEnabled ? _selectedBox : null);
|
||||
_groups[i].UpdateItemSort(_selectedBox);
|
||||
}
|
||||
SortGroups();
|
||||
@@ -443,9 +492,6 @@ namespace FlaxEditor.Surface.ContextMenu
|
||||
PerformLayout();
|
||||
if (SelectedItem != null)
|
||||
_panel1.ScrollViewTo(SelectedItem);
|
||||
_searchBox.Focus();
|
||||
Profiler.EndEvent();
|
||||
|
||||
Profiler.EndEvent();
|
||||
}
|
||||
|
||||
@@ -503,7 +549,11 @@ namespace FlaxEditor.Surface.ContextMenu
|
||||
_searchBox.Clear();
|
||||
SelectedItem = null;
|
||||
for (int i = 0; i < _groups.Count; i++)
|
||||
{
|
||||
_groups[i].ResetView();
|
||||
if (_contextSensitiveSearchEnabled)
|
||||
_groups[i].EvaluateVisibilityWithBox(_selectedBox);
|
||||
}
|
||||
UnlockChildrenRecursive();
|
||||
|
||||
SortGroups();
|
||||
@@ -759,5 +809,12 @@ namespace FlaxEditor.Surface.ContextMenu
|
||||
{
|
||||
return GetPreviousSiblings(item).OfType<T>();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnDestroy()
|
||||
{
|
||||
_contextSensitiveToggle.StateChanged -= OnContextSensitiveToggleStateChanged;
|
||||
base.OnDestroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,7 +66,7 @@ namespace FlaxEditor.Surface.ContextMenu
|
||||
{
|
||||
if (_children[i] is VisjectCMItem item)
|
||||
{
|
||||
item.UpdateFilter(null);
|
||||
item.UpdateFilter(null, null);
|
||||
item.UpdateScore(null);
|
||||
}
|
||||
}
|
||||
@@ -84,23 +84,42 @@ namespace FlaxEditor.Surface.ContextMenu
|
||||
/// Updates the filter.
|
||||
/// </summary>
|
||||
/// <param name="filterText">The filter text.</param>
|
||||
public void UpdateFilter(string filterText)
|
||||
/// <param name="selectedBox">The optionally selected box to show hints for it.</param>
|
||||
public void UpdateFilter(string filterText, Box selectedBox)
|
||||
{
|
||||
Profiler.BeginEvent("VisjectCMGroup.UpdateFilter");
|
||||
|
||||
// Update items
|
||||
bool isAnyVisible = false;
|
||||
bool groupHeaderMatches = QueryFilterHelper.Match(filterText, HeaderText);
|
||||
for (int i = 0; i < _children.Count; i++)
|
||||
{
|
||||
if (_children[i] is VisjectCMItem item)
|
||||
{
|
||||
item.UpdateFilter(filterText);
|
||||
item.UpdateFilter(filterText, selectedBox, groupHeaderMatches);
|
||||
isAnyVisible |= item.Visible;
|
||||
}
|
||||
}
|
||||
|
||||
// Update header title
|
||||
if (QueryFilterHelper.Match(filterText, HeaderText))
|
||||
// Update itself
|
||||
if (isAnyVisible)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(filterText))
|
||||
Open(false);
|
||||
Visible = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Hide group if none of the items matched the filter
|
||||
Visible = false;
|
||||
}
|
||||
|
||||
Profiler.EndEvent();
|
||||
}
|
||||
|
||||
internal void EvaluateVisibilityWithBox(Box selectedBox)
|
||||
{
|
||||
if (selectedBox == null)
|
||||
{
|
||||
for (int i = 0; i < _children.Count; i++)
|
||||
{
|
||||
@@ -109,14 +128,25 @@ namespace FlaxEditor.Surface.ContextMenu
|
||||
item.Visible = true;
|
||||
}
|
||||
}
|
||||
isAnyVisible = true;
|
||||
Visible = true;
|
||||
return;
|
||||
}
|
||||
|
||||
Profiler.BeginEvent("VisjectCMGroup.EvaluateVisibilityWithBox");
|
||||
|
||||
bool isAnyVisible = false;
|
||||
for (int i = 0; i < _children.Count; i++)
|
||||
{
|
||||
if (_children[i] is VisjectCMItem item)
|
||||
{
|
||||
item.Visible = item.CanConnectTo(selectedBox);
|
||||
isAnyVisible |= item.Visible;
|
||||
}
|
||||
}
|
||||
|
||||
// Update itself
|
||||
if (isAnyVisible)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(filterText))
|
||||
Open(false);
|
||||
Visible = true;
|
||||
}
|
||||
else
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using FlaxEditor.Scripting;
|
||||
using FlaxEditor.Surface.Elements;
|
||||
using FlaxEditor.Utilities;
|
||||
using FlaxEngine;
|
||||
@@ -56,7 +57,7 @@ namespace FlaxEditor.Surface.ContextMenu
|
||||
/// <param name="groupArchetype">The group archetype.</param>
|
||||
/// <param name="archetype">The archetype.</param>
|
||||
public VisjectCMItem(VisjectCMGroup group, GroupArchetype groupArchetype, NodeArchetype archetype)
|
||||
: base(0, 0, 120, 12)
|
||||
: base(0, 0, 120, 14)
|
||||
{
|
||||
Group = group;
|
||||
_groupArchetype = groupArchetype;
|
||||
@@ -77,7 +78,7 @@ namespace FlaxEditor.Surface.ContextMenu
|
||||
if (!Visible)
|
||||
return;
|
||||
|
||||
if (selectedBox != null && CanConnectTo(selectedBox, NodeArchetype))
|
||||
if (selectedBox != null && CanConnectTo(selectedBox))
|
||||
SortScore += 1;
|
||||
if (Data != null)
|
||||
SortScore += 1;
|
||||
@@ -92,35 +93,93 @@ namespace FlaxEditor.Surface.ContextMenu
|
||||
textRect = new Rectangle(22, 0, Width - 24, Height);
|
||||
}
|
||||
|
||||
private bool CanConnectTo(Box startBox, NodeArchetype nodeArchetype)
|
||||
/// <summary>
|
||||
/// Checks if this context menu item can be connected to a given box, before a node is actually spawned.
|
||||
/// </summary>
|
||||
/// <param name="startBox">The connected box</param>
|
||||
/// <returns>True if the connected box is compatible with this item</returns>
|
||||
public bool CanConnectTo(Box startBox)
|
||||
{
|
||||
// Is compatible if box is null for reset reasons
|
||||
if (startBox == null)
|
||||
return false;
|
||||
if (!startBox.IsOutput)
|
||||
return false; // For now, I'm only handing the output box case
|
||||
return true;
|
||||
|
||||
if (nodeArchetype.Elements != null)
|
||||
if (_archetype == null)
|
||||
return false;
|
||||
|
||||
bool isCompatible = false;
|
||||
if (startBox.IsOutput && _archetype.IsInputCompatible != null)
|
||||
{
|
||||
for (int i = 0; i < nodeArchetype.Elements.Length; i++)
|
||||
{
|
||||
if (nodeArchetype.Elements[i].Type == NodeElementType.Input &&
|
||||
startBox.CanUseType(nodeArchetype.Elements[i].ConnectionsType))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
isCompatible |= _archetype.IsInputCompatible.Invoke(_archetype, startBox.CurrentType, _archetype.ConnectionsHints);
|
||||
}
|
||||
return false;
|
||||
else if (!startBox.IsOutput && _archetype.IsOutputCompatible != null)
|
||||
{
|
||||
isCompatible |= _archetype.IsOutputCompatible.Invoke(_archetype, startBox.CurrentType, startBox.ParentNode.Archetype.ConnectionsHints);
|
||||
}
|
||||
else if (_archetype.Elements != null)
|
||||
{
|
||||
// Check compatibility based on the defined elements in the archetype. This handles all the default groups and items
|
||||
isCompatible = CheckElementsCompatibility(startBox);
|
||||
}
|
||||
|
||||
return isCompatible;
|
||||
}
|
||||
|
||||
private bool CheckElementsCompatibility(Box startBox)
|
||||
{
|
||||
bool isCompatible = false;
|
||||
foreach (NodeElementArchetype element in _archetype.Elements)
|
||||
{
|
||||
// Ignore all elements that aren't inputs or outputs (e.g. input fields)
|
||||
if (element.Type != NodeElementType.Output && element.Type != NodeElementType.Input)
|
||||
continue;
|
||||
|
||||
// Ignore elements with the same direction as the box
|
||||
if ((startBox.IsOutput && element.Type == NodeElementType.Output) || (!startBox.IsOutput && element.Type == NodeElementType.Input))
|
||||
continue;
|
||||
|
||||
ScriptType fromType;
|
||||
ScriptType toType;
|
||||
ConnectionsHint hint;
|
||||
if (startBox.IsOutput)
|
||||
{
|
||||
fromType = element.ConnectionsType;
|
||||
toType = startBox.CurrentType;
|
||||
hint = _archetype.ConnectionsHints;
|
||||
}
|
||||
else
|
||||
{
|
||||
fromType = startBox.CurrentType;
|
||||
toType = element.ConnectionsType;
|
||||
hint = startBox.ParentNode.Archetype.ConnectionsHints;
|
||||
}
|
||||
|
||||
isCompatible |= VisjectSurface.FullCastCheck(fromType, toType, hint);
|
||||
}
|
||||
|
||||
return isCompatible;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the filter.
|
||||
/// </summary>
|
||||
/// <param name="filterText">The filter text.</param>
|
||||
public void UpdateFilter(string filterText)
|
||||
/// <param name="selectedBox">The optionally selected box to show hints for it.</param>
|
||||
/// <param name="groupHeaderMatches">True if item's group header got a filter match and item should stay visible.</param>
|
||||
public void UpdateFilter(string filterText, Box selectedBox, bool groupHeaderMatches = false)
|
||||
{
|
||||
if (selectedBox != null)
|
||||
{
|
||||
Visible = CanConnectTo(selectedBox);
|
||||
if (!Visible)
|
||||
{
|
||||
_highlights?.Clear();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
_isStartsWithMatch = _isFullMatch = false;
|
||||
if (filterText == null)
|
||||
if (string.IsNullOrEmpty(filterText))
|
||||
{
|
||||
// Clear filter
|
||||
_highlights?.Clear();
|
||||
@@ -184,7 +243,7 @@ namespace FlaxEditor.Surface.ContextMenu
|
||||
|
||||
Data = data;
|
||||
}
|
||||
else
|
||||
else if (!groupHeaderMatches)
|
||||
{
|
||||
// Hide
|
||||
_highlights?.Clear();
|
||||
|
||||
@@ -91,7 +91,7 @@ namespace FlaxEditor.Surface.Elements
|
||||
_currentType = value;
|
||||
|
||||
// Check if will need to update box connections due to type change
|
||||
if ((Surface == null || Surface._isUpdatingBoxTypes == 0) && HasAnyConnection && !CanCast(prev, _currentType))
|
||||
if ((Surface == null || Surface._isUpdatingBoxTypes == 0) && HasAnyConnection && !prev.CanCastTo(_currentType))
|
||||
{
|
||||
// Remove all invalid connections and update those which still can be valid
|
||||
var connections = Connections.ToArray();
|
||||
@@ -231,58 +231,8 @@ namespace FlaxEditor.Surface.Elements
|
||||
}
|
||||
|
||||
// Check using connection hints
|
||||
var connectionsHints = ParentNode.Archetype.ConnectionsHints;
|
||||
if (Archetype.ConnectionsType == ScriptType.Null && connectionsHints != ConnectionsHint.None)
|
||||
{
|
||||
if ((connectionsHints & ConnectionsHint.Anything) == ConnectionsHint.Anything)
|
||||
return true;
|
||||
if ((connectionsHints & ConnectionsHint.Value) == ConnectionsHint.Value && type.Type != typeof(void))
|
||||
return true;
|
||||
if ((connectionsHints & ConnectionsHint.Enum) == ConnectionsHint.Enum && type.IsEnum)
|
||||
return true;
|
||||
if ((connectionsHints & ConnectionsHint.Array) == ConnectionsHint.Array && type.IsArray)
|
||||
return true;
|
||||
if ((connectionsHints & ConnectionsHint.Dictionary) == ConnectionsHint.Dictionary && type.IsDictionary)
|
||||
return true;
|
||||
if ((connectionsHints & ConnectionsHint.Vector) == ConnectionsHint.Vector)
|
||||
{
|
||||
var t = type.Type;
|
||||
if (t == typeof(Vector2) ||
|
||||
t == typeof(Vector3) ||
|
||||
t == typeof(Vector4) ||
|
||||
t == typeof(Float2) ||
|
||||
t == typeof(Float3) ||
|
||||
t == typeof(Float4) ||
|
||||
t == typeof(Double2) ||
|
||||
t == typeof(Double3) ||
|
||||
t == typeof(Double4) ||
|
||||
t == typeof(Int2) ||
|
||||
t == typeof(Int3) ||
|
||||
t == typeof(Int4) ||
|
||||
t == typeof(Color))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if ((connectionsHints & ConnectionsHint.Scalar) == ConnectionsHint.Scalar)
|
||||
{
|
||||
var t = type.Type;
|
||||
if (t == typeof(bool) ||
|
||||
t == typeof(char) ||
|
||||
t == typeof(byte) ||
|
||||
t == typeof(short) ||
|
||||
t == typeof(ushort) ||
|
||||
t == typeof(int) ||
|
||||
t == typeof(uint) ||
|
||||
t == typeof(long) ||
|
||||
t == typeof(ulong) ||
|
||||
t == typeof(float) ||
|
||||
t == typeof(double))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (VisjectSurface.IsTypeCompatible(Archetype.ConnectionsType, type, ParentNode.Archetype.ConnectionsHints))
|
||||
return true;
|
||||
|
||||
// Check independent and if there is box with bigger potential because it may block current one from changing type
|
||||
var parentArch = ParentNode.Archetype;
|
||||
@@ -296,7 +246,7 @@ namespace FlaxEditor.Surface.Elements
|
||||
var b = ParentNode.GetBox(boxes[i]);
|
||||
|
||||
// Check if its the same and tested type matches the default value type
|
||||
if (b == this && CanCast(parentArch.DefaultType, type))
|
||||
if (b == this && parentArch.DefaultType.CanCastTo(type))
|
||||
{
|
||||
// Can
|
||||
return true;
|
||||
@@ -718,17 +668,6 @@ namespace FlaxEditor.Surface.Elements
|
||||
}
|
||||
}
|
||||
|
||||
private static bool CanCast(ScriptType oB, ScriptType iB)
|
||||
{
|
||||
if (oB == iB)
|
||||
return true;
|
||||
if (oB == ScriptType.Null || iB == ScriptType.Null)
|
||||
return false;
|
||||
return (oB.Type != typeof(void) && oB.Type != typeof(FlaxEngine.Object)) &&
|
||||
(iB.Type != typeof(void) && iB.Type != typeof(FlaxEngine.Object)) &&
|
||||
oB.IsAssignableFrom(iB);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool AreConnected(IConnectionInstigator other)
|
||||
{
|
||||
@@ -787,7 +726,7 @@ namespace FlaxEditor.Surface.Elements
|
||||
{
|
||||
if (!iB.CanUseType(oB.CurrentType))
|
||||
{
|
||||
if (!CanCast(oB.CurrentType, iB.CurrentType))
|
||||
if (!oB.CurrentType.CanCastTo(iB.CurrentType))
|
||||
{
|
||||
// Cannot
|
||||
return false;
|
||||
@@ -798,7 +737,7 @@ namespace FlaxEditor.Surface.Elements
|
||||
{
|
||||
if (!oB.CanUseType(iB.CurrentType))
|
||||
{
|
||||
if (!CanCast(oB.CurrentType, iB.CurrentType))
|
||||
if (!oB.CurrentType.CanCastTo(iB.CurrentType))
|
||||
{
|
||||
// Cannot
|
||||
return false;
|
||||
@@ -871,7 +810,7 @@ namespace FlaxEditor.Surface.Elements
|
||||
bool useCaster = false;
|
||||
if (!iB.CanUseType(oB.CurrentType))
|
||||
{
|
||||
if (CanCast(oB.CurrentType, iB.CurrentType))
|
||||
if (oB.CurrentType.CanCastTo(iB.CurrentType))
|
||||
useCaster = true;
|
||||
else
|
||||
return;
|
||||
|
||||
@@ -35,7 +35,7 @@ namespace FlaxEditor.Surface.Elements
|
||||
{
|
||||
// Calculate control points
|
||||
CalculateBezierControlPoints(start, end, out var control1, out var control2);
|
||||
|
||||
|
||||
// Draw line
|
||||
Render2D.DrawBezier(start, control1, control2, end, color, thickness);
|
||||
|
||||
@@ -54,16 +54,16 @@ namespace FlaxEditor.Surface.Elements
|
||||
const float maxControlLength = 150f;
|
||||
var dst = (end - start).Length;
|
||||
var yDst = Mathf.Abs(start.Y - end.Y);
|
||||
|
||||
|
||||
// Calculate control points
|
||||
var minControlDst = dst * 0.5f;
|
||||
var maxControlDst = Mathf.Max(Mathf.Min(maxControlLength, dst), minControlLength);
|
||||
var controlDst = Mathf.Lerp(minControlDst, maxControlDst, Mathf.Clamp(yDst / minControlLength, 0f, 1f));
|
||||
|
||||
|
||||
control1 = new Float2(start.X + controlDst, start.Y);
|
||||
control2 = new Float2(end.X - controlDst, end.Y);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a point intersects a connection
|
||||
/// </summary>
|
||||
@@ -86,11 +86,13 @@ namespace FlaxEditor.Surface.Elements
|
||||
public static bool IntersectsConnection(ref Float2 start, ref Float2 end, ref Float2 point, float distance)
|
||||
{
|
||||
// Pretty much a point in rectangle check
|
||||
if ((point.X - start.X) * (end.X - point.X) < 0) return false;
|
||||
if ((point.X - start.X) * (end.X - point.X) < 0)
|
||||
return false;
|
||||
|
||||
float offset = Mathf.Sign(end.Y - start.Y) * distance;
|
||||
if ((point.Y - (start.Y - offset)) * ((end.Y + offset) - point.Y) < 0) return false;
|
||||
|
||||
if ((point.Y - (start.Y - offset)) * ((end.Y + offset) - point.Y) < 0)
|
||||
return false;
|
||||
|
||||
float squaredDistance = distance;
|
||||
CalculateBezierControlPoints(start, end, out var control1, out var control2);
|
||||
|
||||
|
||||
@@ -89,6 +89,11 @@ namespace FlaxEditor.Surface
|
||||
/// <returns>The created node object.</returns>
|
||||
public delegate SurfaceNode CreateCustomNodeFunc(uint id, VisjectSurfaceContext context, NodeArchetype nodeArch, GroupArchetype groupArch);
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the given type is compatible with the given node archetype. Used for custom nodes
|
||||
/// </summary>
|
||||
public delegate bool IsCompatible(NodeArchetype nodeArch, ScriptType portType, ConnectionsHint hint);
|
||||
|
||||
/// <summary>
|
||||
/// Unique node type ID within a single group.
|
||||
/// </summary>
|
||||
@@ -99,6 +104,16 @@ namespace FlaxEditor.Surface
|
||||
/// </summary>
|
||||
public CreateCustomNodeFunc Create;
|
||||
|
||||
/// <summary>
|
||||
/// Function for asynchronously loaded nodes to check if input ports are compatible, for filtering.
|
||||
/// </summary>
|
||||
public IsCompatible IsInputCompatible;
|
||||
|
||||
/// <summary>
|
||||
/// Function for asynchronously loaded nodes to check if output ports are compatible, for filtering.
|
||||
/// </summary>
|
||||
public IsCompatible IsOutputCompatible;
|
||||
|
||||
/// <summary>
|
||||
/// Default initial size of the node.
|
||||
/// </summary>
|
||||
@@ -184,6 +199,8 @@ namespace FlaxEditor.Surface
|
||||
{
|
||||
TypeID = TypeID,
|
||||
Create = Create,
|
||||
IsInputCompatible = IsInputCompatible,
|
||||
IsOutputCompatible = IsOutputCompatible,
|
||||
Size = Size,
|
||||
Flags = Flags,
|
||||
Title = Title,
|
||||
|
||||
@@ -406,8 +406,8 @@ namespace FlaxEditor.Surface
|
||||
|
||||
internal static bool IsValidVisualScriptType(ScriptType scriptType)
|
||||
{
|
||||
if (!scriptType.IsPublic ||
|
||||
scriptType.HasAttribute(typeof(HideInEditorAttribute), true) ||
|
||||
if (!scriptType.IsPublic ||
|
||||
scriptType.HasAttribute(typeof(HideInEditorAttribute), true) ||
|
||||
scriptType.HasAttribute(typeof(System.Runtime.CompilerServices.CompilerGeneratedAttribute), false))
|
||||
return false;
|
||||
if (scriptType.IsGenericType)
|
||||
|
||||
@@ -136,6 +136,88 @@ namespace FlaxEditor.Surface
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a type is compatible with another type and can be casted by using a connection hint
|
||||
/// </summary>
|
||||
/// <param name="from">Source type</param>
|
||||
/// <param name="to">Type to check compatibility with</param>
|
||||
/// <param name="hint">Hint to check if casting is possible</param>
|
||||
/// <returns>True if the source type is compatible with the target type</returns>
|
||||
public static bool IsTypeCompatible(ScriptType from, ScriptType to, ConnectionsHint hint)
|
||||
{
|
||||
if (from == ScriptType.Null && hint != ConnectionsHint.None)
|
||||
{
|
||||
if ((hint & ConnectionsHint.Anything) == ConnectionsHint.Anything)
|
||||
return true;
|
||||
if ((hint & ConnectionsHint.Value) == ConnectionsHint.Value && to.Type != typeof(void))
|
||||
return true;
|
||||
if ((hint & ConnectionsHint.Enum) == ConnectionsHint.Enum && to.IsEnum)
|
||||
return true;
|
||||
if ((hint & ConnectionsHint.Array) == ConnectionsHint.Array && to.IsArray)
|
||||
return true;
|
||||
if ((hint & ConnectionsHint.Dictionary) == ConnectionsHint.Dictionary && to.IsDictionary)
|
||||
return true;
|
||||
if ((hint & ConnectionsHint.Vector) == ConnectionsHint.Vector)
|
||||
{
|
||||
var t = to.Type;
|
||||
if (t == typeof(Vector2) ||
|
||||
t == typeof(Vector3) ||
|
||||
t == typeof(Vector4) ||
|
||||
t == typeof(Float2) ||
|
||||
t == typeof(Float3) ||
|
||||
t == typeof(Float4) ||
|
||||
t == typeof(Double2) ||
|
||||
t == typeof(Double3) ||
|
||||
t == typeof(Double4) ||
|
||||
t == typeof(Int2) ||
|
||||
t == typeof(Int3) ||
|
||||
t == typeof(Int4) ||
|
||||
t == typeof(Color))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if ((hint & ConnectionsHint.Scalar) == ConnectionsHint.Scalar)
|
||||
{
|
||||
var t = to.Type;
|
||||
if (t == typeof(bool) ||
|
||||
t == typeof(char) ||
|
||||
t == typeof(byte) ||
|
||||
t == typeof(short) ||
|
||||
t == typeof(ushort) ||
|
||||
t == typeof(int) ||
|
||||
t == typeof(uint) ||
|
||||
t == typeof(long) ||
|
||||
t == typeof(ulong) ||
|
||||
t == typeof(float) ||
|
||||
t == typeof(double))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a type is compatible with another type and can be casted by using a connection hint
|
||||
/// </summary>
|
||||
/// <param name="from">Source type</param>
|
||||
/// <param name="to">Target type</param>
|
||||
/// <param name="hint">Connection hint</param>
|
||||
/// <returns>True if any method of casting or compatibility check succeeds</returns>
|
||||
public static bool FullCastCheck(ScriptType from, ScriptType to, ConnectionsHint hint)
|
||||
{
|
||||
// Yes, from and to are switched on purpose
|
||||
if (CanUseDirectCastStatic(to, from, false))
|
||||
return true;
|
||||
if (IsTypeCompatible(from, to, hint))
|
||||
return true;
|
||||
// Same here
|
||||
return to.CanCastTo(from);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if can use direct conversion from one type to another.
|
||||
/// </summary>
|
||||
|
||||
@@ -463,6 +463,18 @@ namespace FlaxEditor.Windows
|
||||
Cursor = CursorType.Default;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnMouseLeave()
|
||||
{
|
||||
base.OnMouseLeave();
|
||||
|
||||
// Remove focus from game window when mouse moves out and the cursor is hidden during game
|
||||
if ((IsFocused || ContainsFocus) && Parent != null && Editor.IsPlayMode && !Screen.CursorVisible)
|
||||
{
|
||||
Parent.Focus();
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnShowContextMenu(ContextMenu menu)
|
||||
{
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "FlaxEngine.Gen.h"
|
||||
#include "Cache/AssetsCache.h"
|
||||
#include "Engine/Core/Log.h"
|
||||
#include "Engine/Core/Config/Settings.h"
|
||||
#include "Engine/Serialization/JsonTools.h"
|
||||
#include "Engine/Serialization/JsonWriters.h"
|
||||
#include "Engine/Content/Factories/JsonAssetFactory.h"
|
||||
@@ -126,8 +127,7 @@ void FindIds(ISerializable::DeserializeStream& node, Array<Guid>& output)
|
||||
}
|
||||
else if (node.IsString())
|
||||
{
|
||||
const auto length = node.GetStringLength();
|
||||
if (length == 32)
|
||||
if (node.GetStringLength() == 32)
|
||||
{
|
||||
// Try parse as Guid in format `N` (32 hex chars)
|
||||
Guid id;
|
||||
@@ -362,6 +362,7 @@ void JsonAsset::unload(bool isReloading)
|
||||
#endif
|
||||
Scripting::ScriptsUnload.Unbind<JsonAsset, &JsonAsset::DeleteInstance>(this);
|
||||
DeleteInstance();
|
||||
_isAfterReload |= isReloading;
|
||||
|
||||
JsonAssetBase::unload(isReloading);
|
||||
}
|
||||
@@ -408,6 +409,13 @@ bool JsonAsset::CreateInstance()
|
||||
}
|
||||
}
|
||||
|
||||
// Special case for Settings assets to flush them after edited and saved in Editor
|
||||
if (typeHandle && typeHandle.IsSubclassOf(SettingsBase::TypeInitializer) && _isAfterReload)
|
||||
{
|
||||
_isAfterReload = false;
|
||||
((SettingsBase*)Instance)->Apply();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -118,6 +118,7 @@ API_CLASS(NoSpawn) class FLAXENGINE_API JsonAsset : public JsonAssetBase
|
||||
DECLARE_ASSET_HEADER(JsonAsset);
|
||||
private:
|
||||
ScriptingType::Dtor _dtor;
|
||||
bool _isAfterReload = false;
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
|
||||
@@ -1283,8 +1283,8 @@ void ParticlesSystem::Job(int32 index)
|
||||
updateBounds = true;
|
||||
}
|
||||
// TODO: if using fixed timestep quantize the dt and accumulate remaining part for the next update?
|
||||
if (dt <= 1.0f / 240.0f)
|
||||
return;
|
||||
//if (dt <= 1.0f / 240.0f)
|
||||
// return;
|
||||
dt *= effect->SimulationSpeed;
|
||||
instance.Time += dt;
|
||||
const float fps = particleSystem->FramesPerSecond;
|
||||
|
||||
@@ -256,7 +256,7 @@ BoundingBox JsonTools::GetBoundingBox(const Value& value)
|
||||
|
||||
Guid JsonTools::GetGuid(const Value& value)
|
||||
{
|
||||
if (value.IsNull())
|
||||
if (!value.IsString())
|
||||
return Guid::Empty;
|
||||
CHECK_RETURN(value.GetStringLength() == 32, Guid::Empty);
|
||||
|
||||
|
||||
@@ -524,7 +524,7 @@ bool ImportMaterialTexture(ImportedModelData& result, AssimpImporterData& data,
|
||||
{
|
||||
const aiTexture* aTex = data.Scene->GetEmbeddedTexture(aFilename.C_Str());
|
||||
const StringView texIndexName(filename.Get() + (ARRAY_COUNT(AI_EMBEDDED_TEXNAME_PREFIX) - 1));
|
||||
int32 texIndex;
|
||||
uint32 texIndex;
|
||||
if (!aTex && !StringUtils::Parse(texIndexName.Get(), texIndexName.Length(), &texIndex) && texIndex >= 0 && texIndex < data.Scene->mNumTextures)
|
||||
aTex = data.Scene->mTextures[texIndex];
|
||||
if (aTex && aTex->mHeight == 0 && aTex->mWidth > 0)
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
#include "Engine/Tools/TextureTool/TextureTool.h"
|
||||
#include "Engine/ContentImporters/AssetsImportingManager.h"
|
||||
#include "Engine/ContentImporters/CreateMaterial.h"
|
||||
#include "Engine/ContentImporters/CreateMaterialInstance.h"
|
||||
#include "Engine/ContentImporters/CreateCollisionData.h"
|
||||
#include "Engine/Serialization/Serialization.h"
|
||||
#include "Editor/Utilities/EditorUtilities.h"
|
||||
@@ -387,6 +388,8 @@ void ModelTool::Options::Serialize(SerializeStream& stream, const void* otherObj
|
||||
SERIALIZE(SloppyOptimization);
|
||||
SERIALIZE(LODTargetError);
|
||||
SERIALIZE(ImportMaterials);
|
||||
SERIALIZE(ImportMaterialsAsInstances);
|
||||
SERIALIZE(InstanceToImportAs);
|
||||
SERIALIZE(ImportTextures);
|
||||
SERIALIZE(RestoreMaterialsOnReimport);
|
||||
SERIALIZE(GenerateSDF);
|
||||
@@ -430,6 +433,8 @@ void ModelTool::Options::Deserialize(DeserializeStream& stream, ISerializeModifi
|
||||
DESERIALIZE(SloppyOptimization);
|
||||
DESERIALIZE(LODTargetError);
|
||||
DESERIALIZE(ImportMaterials);
|
||||
DESERIALIZE(ImportMaterialsAsInstances);
|
||||
DESERIALIZE(InstanceToImportAs);
|
||||
DESERIALIZE(ImportTextures);
|
||||
DESERIALIZE(RestoreMaterialsOnReimport);
|
||||
DESERIALIZE(GenerateSDF);
|
||||
@@ -503,11 +508,11 @@ bool ModelTool::ImportData(const String& path, ImportedModelData& data, Options&
|
||||
if (ImportDataAssimp(importPath.Get(), data, options, errorMsg))
|
||||
return true;
|
||||
#elif USE_AUTODESK_FBX_SDK
|
||||
if (ImportDataAutodeskFbxSdk(importPath.Get(), data, options, errorMsg))
|
||||
return true;
|
||||
if (ImportDataAutodeskFbxSdk(importPath.Get(), data, options, errorMsg))
|
||||
return true;
|
||||
#elif USE_OPEN_FBX
|
||||
if (ImportDataOpenFBX(importPath.Get(), data, options, errorMsg))
|
||||
return true;
|
||||
if (ImportDataOpenFBX(importPath.Get(), data, options, errorMsg))
|
||||
return true;
|
||||
#else
|
||||
LOG(Error, "Compiled without model importing backend.");
|
||||
return true;
|
||||
@@ -620,62 +625,62 @@ bool ModelTool::ImportData(const String& path, ImportedModelData& data, Options&
|
||||
|
||||
bool SortDepths(const Pair<int32, int32>& a, const Pair<int32, int32>& b)
|
||||
{
|
||||
return a.First < b.First;
|
||||
return a.First < b.First;
|
||||
}
|
||||
|
||||
void CreateLinearListFromTree(Array<SkeletonNode>& nodes, Array<int32>& mapping)
|
||||
{
|
||||
// Customized breadth first tree algorithm (each node has no direct reference to the children so we build the cache for the nodes depth level)
|
||||
const int32 count = nodes.Count();
|
||||
Array<Pair<int32, int32>> depths(count); // Pair.First = Depth, Pair.Second = Node Index
|
||||
depths.SetSize(count);
|
||||
depths.Set(-1);
|
||||
for (int32 i = 0; i < count; i++)
|
||||
{
|
||||
// Skip evaluated nodes
|
||||
if (depths[i].First != -1)
|
||||
continue;
|
||||
const int32 count = nodes.Count();
|
||||
Array<Pair<int32, int32>> depths(count); // Pair.First = Depth, Pair.Second = Node Index
|
||||
depths.Resize(count);
|
||||
depths.SetAll(-1);
|
||||
for (int32 i = 0; i < count; i++)
|
||||
{
|
||||
// Skip evaluated nodes
|
||||
if (depths[i].First != -1)
|
||||
continue;
|
||||
|
||||
// Find the first node with calculated depth and get the distance to it
|
||||
int32 end = i;
|
||||
int32 lastDepth;
|
||||
int32 relativeDepth = 0;
|
||||
do
|
||||
{
|
||||
lastDepth = depths[end].First;
|
||||
end = nodes[end].ParentIndex;
|
||||
relativeDepth++;
|
||||
} while (end != -1 && lastDepth == -1);
|
||||
// Find the first node with calculated depth and get the distance to it
|
||||
int32 end = i;
|
||||
int32 lastDepth;
|
||||
int32 relativeDepth = 0;
|
||||
do
|
||||
{
|
||||
lastDepth = depths[end].First;
|
||||
end = nodes[end].ParentIndex;
|
||||
relativeDepth++;
|
||||
} while (end != -1 && lastDepth == -1);
|
||||
|
||||
// Set the depth (second item is the node index)
|
||||
depths[i] = MakePair(lastDepth + relativeDepth, i);
|
||||
}
|
||||
for (int32 i = 0; i < count; i++)
|
||||
{
|
||||
// Strange divide by 2 but works
|
||||
depths[i].First = depths[i].First >> 1;
|
||||
}
|
||||
// Set the depth (second item is the node index)
|
||||
depths[i] = ToPair(lastDepth + relativeDepth, i);
|
||||
}
|
||||
for (int32 i = 0; i < count; i++)
|
||||
{
|
||||
// Strange divide by 2 but works
|
||||
depths[i].First = depths[i].First >> 1;
|
||||
}
|
||||
|
||||
// Order nodes by depth O(n*log(n))
|
||||
depths.Sort(SortDepths);
|
||||
// Order nodes by depth O(n*log(n))
|
||||
depths.Sort(SortDepths);
|
||||
|
||||
// Extract nodes mapping O(n^2)
|
||||
mapping.EnsureCapacity(count, false);
|
||||
mapping.SetSize(count);
|
||||
for (int32 i = 0; i < count; i++)
|
||||
{
|
||||
int32 newIndex = -1;
|
||||
for (int32 j = 0; j < count; j++)
|
||||
{
|
||||
if (depths[j].Second == i)
|
||||
{
|
||||
newIndex = j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
ASSERT(newIndex != -1);
|
||||
mapping[i] = newIndex;
|
||||
}
|
||||
// Extract nodes mapping O(n^2)
|
||||
mapping.EnsureCapacity(count, false);
|
||||
mapping.Resize(count);
|
||||
for (int32 i = 0; i < count; i++)
|
||||
{
|
||||
int32 newIndex = -1;
|
||||
for (int32 j = 0; j < count; j++)
|
||||
{
|
||||
if (depths[j].Second == i)
|
||||
{
|
||||
newIndex = j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
ASSERT(newIndex != -1);
|
||||
mapping[i] = newIndex;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -994,23 +999,6 @@ bool ModelTool::ImportModel(const String& path, ModelData& meshData, Options& op
|
||||
importedFileNames.Add(filename);
|
||||
#if COMPILE_WITH_ASSETS_IMPORTER
|
||||
auto assetPath = autoImportOutput / filename + ASSET_FILES_EXTENSION_WITH_DOT;
|
||||
CreateMaterial::Options materialOptions;
|
||||
materialOptions.Diffuse.Color = material.Diffuse.Color;
|
||||
if (material.Diffuse.TextureIndex != -1)
|
||||
materialOptions.Diffuse.Texture = data.Textures[material.Diffuse.TextureIndex].AssetID;
|
||||
materialOptions.Diffuse.HasAlphaMask = material.Diffuse.HasAlphaMask;
|
||||
materialOptions.Emissive.Color = material.Emissive.Color;
|
||||
if (material.Emissive.TextureIndex != -1)
|
||||
materialOptions.Emissive.Texture = data.Textures[material.Emissive.TextureIndex].AssetID;
|
||||
materialOptions.Opacity.Value = material.Opacity.Value;
|
||||
if (material.Opacity.TextureIndex != -1)
|
||||
materialOptions.Opacity.Texture = data.Textures[material.Opacity.TextureIndex].AssetID;
|
||||
if (material.Normals.TextureIndex != -1)
|
||||
materialOptions.Normals.Texture = data.Textures[material.Normals.TextureIndex].AssetID;
|
||||
if (material.TwoSided || material.Diffuse.HasAlphaMask)
|
||||
materialOptions.Info.CullMode = CullMode::TwoSided;
|
||||
if (!Math::IsOne(material.Opacity.Value) || material.Opacity.TextureIndex != -1)
|
||||
materialOptions.Info.BlendMode = MaterialBlendMode::Transparent;
|
||||
|
||||
// When splitting imported meshes allow only the first mesh to import assets (mesh[0] is imported after all following ones so import assets during mesh[1])
|
||||
if (!options.SplitObjects && options.ObjectIndex != 1 && options.ObjectIndex != -1)
|
||||
@@ -1022,7 +1010,42 @@ bool ModelTool::ImportModel(const String& path, ModelData& meshData, Options& op
|
||||
continue;
|
||||
}
|
||||
|
||||
AssetsImportingManager::Create(AssetsImportingManager::CreateMaterialTag, assetPath, material.AssetID, &materialOptions);
|
||||
if (options.ImportMaterialsAsInstances)
|
||||
{
|
||||
// Create material instance
|
||||
AssetsImportingManager::Create(AssetsImportingManager::CreateMaterialInstanceTag, assetPath, material.AssetID);
|
||||
if (MaterialInstance* materialInstance = Content::Load<MaterialInstance>(assetPath))
|
||||
{
|
||||
materialInstance->SetBaseMaterial(options.InstanceToImportAs);
|
||||
materialInstance->Save();
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG(Error, "Failed to load material instance after creation. ({0})", assetPath);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Create material
|
||||
CreateMaterial::Options materialOptions;
|
||||
materialOptions.Diffuse.Color = material.Diffuse.Color;
|
||||
if (material.Diffuse.TextureIndex != -1)
|
||||
materialOptions.Diffuse.Texture = data.Textures[material.Diffuse.TextureIndex].AssetID;
|
||||
materialOptions.Diffuse.HasAlphaMask = material.Diffuse.HasAlphaMask;
|
||||
materialOptions.Emissive.Color = material.Emissive.Color;
|
||||
if (material.Emissive.TextureIndex != -1)
|
||||
materialOptions.Emissive.Texture = data.Textures[material.Emissive.TextureIndex].AssetID;
|
||||
materialOptions.Opacity.Value = material.Opacity.Value;
|
||||
if (material.Opacity.TextureIndex != -1)
|
||||
materialOptions.Opacity.Texture = data.Textures[material.Opacity.TextureIndex].AssetID;
|
||||
if (material.Normals.TextureIndex != -1)
|
||||
materialOptions.Normals.Texture = data.Textures[material.Normals.TextureIndex].AssetID;
|
||||
if (material.TwoSided || material.Diffuse.HasAlphaMask)
|
||||
materialOptions.Info.CullMode = CullMode::TwoSided;
|
||||
if (!Math::IsOne(material.Opacity.Value) || material.Opacity.TextureIndex != -1)
|
||||
materialOptions.Info.BlendMode = MaterialBlendMode::Transparent;
|
||||
AssetsImportingManager::Create(AssetsImportingManager::CreateMaterialTag, assetPath, material.AssetID, &materialOptions);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -1389,24 +1412,25 @@ bool ModelTool::ImportModel(const String& path, ModelData& meshData, Options& op
|
||||
|
||||
#if USE_SKELETON_NODES_SORTING
|
||||
// Sort skeleton nodes and bones hierarchy (parents first)
|
||||
// Then it can be used with a simple linear loop update
|
||||
{
|
||||
const int32 nodesCount = data.Skeleton.Nodes.Count();
|
||||
const int32 bonesCount = data.Skeleton.Bones.Count();
|
||||
Array<int32> mapping;
|
||||
CreateLinearListFromTree(data.Skeleton.Nodes, mapping);
|
||||
for (int32 i = 0; i < nodesCount; i++)
|
||||
{
|
||||
auto& node = data.Skeleton.Nodes[i];
|
||||
node.ParentIndex = mapping[node.ParentIndex];
|
||||
}
|
||||
for (int32 i = 0; i < bonesCount; i++)
|
||||
{
|
||||
auto& bone = data.Skeleton.Bones[i];
|
||||
bone.NodeIndex = mapping[bone.NodeIndex];
|
||||
}
|
||||
}
|
||||
reorder_nodes_and_test_it_out!
|
||||
// Then it can be used with a simple linear loop update
|
||||
{
|
||||
const int32 nodesCount = data.Skeleton.Nodes.Count();
|
||||
const int32 bonesCount = data.Skeleton.Bones.Count();
|
||||
Array<int32> mapping;
|
||||
CreateLinearListFromTree(data.Skeleton.Nodes, mapping);
|
||||
for (int32 i = 0; i < nodesCount; i++)
|
||||
{
|
||||
auto& node = data.Skeleton.Nodes[i];
|
||||
node.ParentIndex = mapping[node.ParentIndex];
|
||||
}
|
||||
for (int32 i = 0; i < bonesCount; i++)
|
||||
{
|
||||
auto& bone = data.Skeleton.Bones[i];
|
||||
bone.NodeIndex = mapping[bone.NodeIndex];
|
||||
}
|
||||
}
|
||||
reorder_nodes_and_test_it_out
|
||||
!
|
||||
#endif
|
||||
}
|
||||
else if (options.Type == ModelType::Animation)
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "Engine/Graphics/Models/ModelData.h"
|
||||
#include "Engine/Graphics/Models/SkeletonData.h"
|
||||
#include "Engine/Animations/AnimationData.h"
|
||||
#include "Engine/Content/Assets/MaterialBase.h"
|
||||
|
||||
class JsonWriter;
|
||||
|
||||
@@ -263,7 +264,7 @@ public:
|
||||
// If specified, all meshes which name starts with this prefix will be imported as a separate collision data (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 especified
|
||||
// The type of collision that should be generated if has collision prefix specified.
|
||||
API_FIELD(Attributes = "EditorOrder(105), EditorDisplay(\"Geometry\"), VisibleIf(nameof(ShowGeometry))")
|
||||
CollisionDataType CollisionType = CollisionDataType::TriangleMesh;
|
||||
|
||||
@@ -338,6 +339,12 @@ public:
|
||||
// If checked, the importer will create materials for model meshes as specified in the file.
|
||||
API_FIELD(Attributes="EditorOrder(400), EditorDisplay(\"Materials\"), VisibleIf(nameof(ShowGeometry))")
|
||||
bool ImportMaterials = true;
|
||||
// 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.
|
||||
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.
|
||||
API_FIELD(Attributes="EditorOrder(410), EditorDisplay(\"Materials\"), VisibleIf(nameof(ShowGeometry))")
|
||||
bool ImportTextures = true;
|
||||
|
||||
@@ -603,8 +603,8 @@ namespace Flax.Build.Projects.VisualStudio
|
||||
{
|
||||
var profiles = new Dictionary<string, string>();
|
||||
var profile = new StringBuilder();
|
||||
var editorPath = Utilities.NormalizePath(Path.Combine(Globals.EngineRoot, Platform.GetEditorBinaryDirectory(), $"Development/FlaxEditor{Utilities.GetPlatformExecutableExt()}"));
|
||||
var workspacePath = Utilities.NormalizePath(solutionDirectory);
|
||||
var editorPath = Utilities.NormalizePath(Path.Combine(Globals.EngineRoot, Platform.GetEditorBinaryDirectory(), $"Development/FlaxEditor{Utilities.GetPlatformExecutableExt()}")).Replace('\\', '/');
|
||||
var workspacePath = Utilities.NormalizePath(solutionDirectory).Replace('\\', '/');
|
||||
foreach (var project in projects)
|
||||
{
|
||||
if (project.Type == TargetType.DotNetCore)
|
||||
|
||||
Reference in New Issue
Block a user