Add support for Pack/Unpack Structure nodes and Enum constant in Anim Graph
This commit is contained in:
@@ -518,6 +518,7 @@ namespace FlaxEditor
|
|||||||
// Cleanup
|
// Cleanup
|
||||||
Undo.Dispose();
|
Undo.Dispose();
|
||||||
Surface.VisualScriptSurface.NodesCache.Clear();
|
Surface.VisualScriptSurface.NodesCache.Clear();
|
||||||
|
Surface.AnimGraphSurface.NodesCache.Clear();
|
||||||
Instance = null;
|
Instance = null;
|
||||||
|
|
||||||
// Invoke new instance if need to open a project
|
// Invoke new instance if need to open a project
|
||||||
|
|||||||
@@ -165,12 +165,13 @@ namespace FlaxEditor.SceneGraph
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The scene graph raycasting data container.
|
/// The scene graph raycasting data container.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[HideInEditor]
|
||||||
public struct RayCastData
|
public struct RayCastData
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The raycasting optional flags.
|
/// The raycasting optional flags.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Flags]
|
[Flags, HideInEditor]
|
||||||
public enum FlagTypes
|
public enum FlagTypes
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -334,6 +335,7 @@ namespace FlaxEditor.SceneGraph
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The scene graph node state container. Used for Editor undo actions (eg. restoring deleted node).
|
/// The scene graph node state container. Used for Editor undo actions (eg. restoring deleted node).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[HideInEditor]
|
||||||
public struct StateData
|
public struct StateData
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -2,10 +2,14 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using FlaxEditor.Content;
|
using FlaxEditor.Content;
|
||||||
using FlaxEditor.Scripting;
|
using FlaxEditor.Scripting;
|
||||||
using FlaxEditor.Surface.ContextMenu;
|
using FlaxEditor.Surface.ContextMenu;
|
||||||
|
using FlaxEditor.Surface.Elements;
|
||||||
using FlaxEngine;
|
using FlaxEngine;
|
||||||
|
using FlaxEngine.GUI;
|
||||||
using Animation = FlaxEditor.Surface.Archetypes.Animation;
|
using Animation = FlaxEditor.Surface.Archetypes.Animation;
|
||||||
|
|
||||||
namespace FlaxEditor.Surface
|
namespace FlaxEditor.Surface
|
||||||
@@ -72,6 +76,198 @@ namespace FlaxEditor.Surface
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
internal static class NodesCache
|
||||||
|
{
|
||||||
|
private static readonly object _locker = new object();
|
||||||
|
private static int _version;
|
||||||
|
private static Task _task;
|
||||||
|
private static VisjectCM _taskContextMenu;
|
||||||
|
private static Dictionary<KeyValuePair<string, ushort>, GroupArchetype> _cache;
|
||||||
|
|
||||||
|
public static void Wait()
|
||||||
|
{
|
||||||
|
_task?.Wait();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Clear()
|
||||||
|
{
|
||||||
|
Wait();
|
||||||
|
|
||||||
|
if (_cache != null && _cache.Count != 0)
|
||||||
|
{
|
||||||
|
OnCodeEditingTypesCleared();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Get(VisjectCM contextMenu)
|
||||||
|
{
|
||||||
|
Wait();
|
||||||
|
|
||||||
|
lock (_locker)
|
||||||
|
{
|
||||||
|
if (_cache == null)
|
||||||
|
_cache = new Dictionary<KeyValuePair<string, ushort>, GroupArchetype>();
|
||||||
|
contextMenu.LockChildrenRecursive();
|
||||||
|
|
||||||
|
// Check if has cached groups
|
||||||
|
if (_cache.Count != 0)
|
||||||
|
{
|
||||||
|
// Check if context menu doesn't have the recent cached groups
|
||||||
|
if (!contextMenu.Groups.Any(g => g.Archetype.Tag is int asInt && asInt == _version))
|
||||||
|
{
|
||||||
|
var groups = contextMenu.Groups.Where(g => g.Archetype.Tag is int).ToArray();
|
||||||
|
foreach (var g in groups)
|
||||||
|
contextMenu.RemoveGroup(g);
|
||||||
|
foreach (var g in _cache.Values)
|
||||||
|
contextMenu.AddGroup(g);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Remove any old groups from context menu
|
||||||
|
var groups = contextMenu.Groups.Where(g => g.Archetype.Tag is int).ToArray();
|
||||||
|
foreach (var g in groups)
|
||||||
|
contextMenu.RemoveGroup(g);
|
||||||
|
|
||||||
|
// Register for scripting types reload
|
||||||
|
Editor.Instance.CodeEditing.TypesCleared += OnCodeEditingTypesCleared;
|
||||||
|
|
||||||
|
// Run caching on an async
|
||||||
|
_task = Task.Run(OnActiveContextMenuShowAsync);
|
||||||
|
_taskContextMenu = contextMenu;
|
||||||
|
}
|
||||||
|
|
||||||
|
contextMenu.UnlockChildrenRecursive();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void OnActiveContextMenuShowAsync()
|
||||||
|
{
|
||||||
|
Profiler.BeginEvent("Setup Anim Graph Context Menu (async)");
|
||||||
|
|
||||||
|
foreach (var scriptType in Editor.Instance.CodeEditing.All.Get())
|
||||||
|
{
|
||||||
|
if (!SurfaceUtils.IsValidVisualScriptType(scriptType))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Skip Newtonsoft.Json stuff
|
||||||
|
var scriptTypeTypeName = scriptType.TypeName;
|
||||||
|
if (scriptTypeTypeName.StartsWith("Newtonsoft.Json."))
|
||||||
|
continue;
|
||||||
|
var scriptTypeName = scriptType.Name;
|
||||||
|
|
||||||
|
// Enum
|
||||||
|
if (scriptType.IsEnum)
|
||||||
|
{
|
||||||
|
// Create node archetype
|
||||||
|
var node = (NodeArchetype)Archetypes.Constants.Nodes[10].Clone();
|
||||||
|
node.DefaultValues[0] = Activator.CreateInstance(scriptType.Type);
|
||||||
|
node.Flags &= ~NodeFlags.NoSpawnViaGUI;
|
||||||
|
node.Title = scriptTypeName;
|
||||||
|
node.Description = scriptTypeTypeName;
|
||||||
|
var attributes = scriptType.GetAttributes(false);
|
||||||
|
var tooltipAttribute = (TooltipAttribute)attributes.FirstOrDefault(x => x is TooltipAttribute);
|
||||||
|
if (tooltipAttribute != null)
|
||||||
|
node.Description += "\n" + tooltipAttribute.Text;
|
||||||
|
|
||||||
|
// Create group archetype
|
||||||
|
var groupKey = new KeyValuePair<string, ushort>(scriptTypeName, 2);
|
||||||
|
if (!_cache.TryGetValue(groupKey, out var group))
|
||||||
|
{
|
||||||
|
group = new GroupArchetype
|
||||||
|
{
|
||||||
|
GroupID = groupKey.Value,
|
||||||
|
Name = groupKey.Key,
|
||||||
|
Color = new Color(243, 156, 18),
|
||||||
|
Tag = _version,
|
||||||
|
Archetypes = new List<NodeArchetype>(),
|
||||||
|
};
|
||||||
|
_cache.Add(groupKey, group);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add node to the group
|
||||||
|
((IList<NodeArchetype>)group.Archetypes).Add(node);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Structure
|
||||||
|
if (scriptType.IsValueType)
|
||||||
|
{
|
||||||
|
if (scriptType.IsVoid)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Create group archetype
|
||||||
|
var groupKey = new KeyValuePair<string, ushort>(scriptTypeName, 4);
|
||||||
|
if (!_cache.TryGetValue(groupKey, out var group))
|
||||||
|
{
|
||||||
|
group = new GroupArchetype
|
||||||
|
{
|
||||||
|
GroupID = groupKey.Value,
|
||||||
|
Name = groupKey.Key,
|
||||||
|
Color = new Color(155, 89, 182),
|
||||||
|
Tag = _version,
|
||||||
|
Archetypes = new List<NodeArchetype>(),
|
||||||
|
};
|
||||||
|
_cache.Add(groupKey, group);
|
||||||
|
}
|
||||||
|
|
||||||
|
var attributes = scriptType.GetAttributes(false);
|
||||||
|
var tooltipAttribute = (TooltipAttribute)attributes.FirstOrDefault(x => x is TooltipAttribute);
|
||||||
|
|
||||||
|
// Create Pack node archetype
|
||||||
|
var node = (NodeArchetype)Archetypes.Packing.Nodes[6].Clone();
|
||||||
|
node.DefaultValues[0] = scriptTypeTypeName;
|
||||||
|
node.Flags &= ~NodeFlags.NoSpawnViaGUI;
|
||||||
|
node.Title = "Pack " + scriptTypeName;
|
||||||
|
node.Description = scriptTypeTypeName;
|
||||||
|
if (tooltipAttribute != null)
|
||||||
|
node.Description += "\n" + tooltipAttribute.Text;
|
||||||
|
((IList<NodeArchetype>)group.Archetypes).Add(node);
|
||||||
|
|
||||||
|
// Create Unpack node archetype
|
||||||
|
node = (NodeArchetype)Archetypes.Packing.Nodes[13].Clone();
|
||||||
|
node.DefaultValues[0] = scriptTypeTypeName;
|
||||||
|
node.Flags &= ~NodeFlags.NoSpawnViaGUI;
|
||||||
|
node.Title = "Unpack " + scriptTypeName;
|
||||||
|
node.Description = scriptTypeTypeName;
|
||||||
|
if (tooltipAttribute != null)
|
||||||
|
node.Description += "\n" + tooltipAttribute.Text;
|
||||||
|
((IList<NodeArchetype>)group.Archetypes).Add(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add group to context menu (on a main thread)
|
||||||
|
FlaxEngine.Scripting.InvokeOnUpdate(() =>
|
||||||
|
{
|
||||||
|
lock (_locker)
|
||||||
|
{
|
||||||
|
_taskContextMenu.AddGroups(_cache.Values);
|
||||||
|
_taskContextMenu = null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Profiler.EndEvent();
|
||||||
|
|
||||||
|
lock (_locker)
|
||||||
|
{
|
||||||
|
_task = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void OnCodeEditingTypesCleared()
|
||||||
|
{
|
||||||
|
Wait();
|
||||||
|
|
||||||
|
lock (_locker)
|
||||||
|
{
|
||||||
|
_cache.Clear();
|
||||||
|
_version++;
|
||||||
|
}
|
||||||
|
|
||||||
|
Editor.Instance.CodeEditing.TypesCleared -= OnCodeEditingTypesCleared;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The state machine editing context menu.
|
/// The state machine editing context menu.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -152,6 +348,23 @@ namespace FlaxEditor.Surface
|
|||||||
SetPrimaryMenu(menu);
|
SetPrimaryMenu(menu);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void OnShowPrimaryMenu(VisjectCM activeCM, Vector2 location, Box startBox)
|
||||||
|
{
|
||||||
|
Profiler.BeginEvent("Setup Anim Graph Context Menu");
|
||||||
|
NodesCache.Get(activeCM);
|
||||||
|
Profiler.EndEvent();
|
||||||
|
|
||||||
|
base.OnShowPrimaryMenu(activeCM, location, startBox);
|
||||||
|
|
||||||
|
activeCM.VisibleChanged += OnActiveContextMenuVisibleChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnActiveContextMenuVisibleChanged(Control activeCM)
|
||||||
|
{
|
||||||
|
NodesCache.Wait();
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override string GetTypeName(ScriptType type)
|
public override string GetTypeName(ScriptType type)
|
||||||
{
|
{
|
||||||
@@ -230,6 +443,8 @@ namespace FlaxEditor.Surface
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void OnDestroy()
|
public override void OnDestroy()
|
||||||
{
|
{
|
||||||
|
if (IsDisposing)
|
||||||
|
return;
|
||||||
if (_cmStateMachineMenu != null)
|
if (_cmStateMachineMenu != null)
|
||||||
{
|
{
|
||||||
_cmStateMachineMenu.Dispose();
|
_cmStateMachineMenu.Dispose();
|
||||||
@@ -245,6 +460,7 @@ namespace FlaxEditor.Surface
|
|||||||
_isRegisteredForScriptsReload = false;
|
_isRegisteredForScriptsReload = false;
|
||||||
ScriptsBuilder.ScriptsReloadBegin -= OnScriptsReloadBegin;
|
ScriptsBuilder.ScriptsReloadBegin -= OnScriptsReloadBegin;
|
||||||
}
|
}
|
||||||
|
NodesCache.Wait();
|
||||||
|
|
||||||
base.OnDestroy();
|
base.OnDestroy();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -354,7 +354,7 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
Title = "Enum",
|
Title = "Enum",
|
||||||
Create = (id, context, arch, groupArch) => new EnumNode(id, context, arch, groupArch),
|
Create = (id, context, arch, groupArch) => new EnumNode(id, context, arch, groupArch),
|
||||||
Description = "Enum constant value.",
|
Description = "Enum constant value.",
|
||||||
Flags = NodeFlags.VisualScriptGraph | NodeFlags.NoSpawnViaGUI,
|
Flags = NodeFlags.VisualScriptGraph | NodeFlags.AnimGraph | NodeFlags.NoSpawnViaGUI,
|
||||||
Size = new Vector2(180, 20),
|
Size = new Vector2(180, 20),
|
||||||
DefaultValues = new object[]
|
DefaultValues = new object[]
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ using System.Reflection;
|
|||||||
using FlaxEditor.Scripting;
|
using FlaxEditor.Scripting;
|
||||||
using FlaxEditor.Surface.Elements;
|
using FlaxEditor.Surface.Elements;
|
||||||
using FlaxEngine;
|
using FlaxEngine;
|
||||||
using FlaxEngine.GUI;
|
|
||||||
|
|
||||||
namespace FlaxEditor.Surface.Archetypes
|
namespace FlaxEditor.Surface.Archetypes
|
||||||
{
|
{
|
||||||
@@ -355,7 +354,7 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
Title = "Pack Structure",
|
Title = "Pack Structure",
|
||||||
Create = (id, context, arch, groupArch) => new PackStructureNode(id, context, arch, groupArch),
|
Create = (id, context, arch, groupArch) => new PackStructureNode(id, context, arch, groupArch),
|
||||||
Description = "Makes the structure data to from the components.",
|
Description = "Makes the structure data to from the components.",
|
||||||
Flags = NodeFlags.VisualScriptGraph | NodeFlags.NoSpawnViaGUI,
|
Flags = NodeFlags.VisualScriptGraph | NodeFlags.AnimGraph | NodeFlags.NoSpawnViaGUI,
|
||||||
Size = new Vector2(180, 20),
|
Size = new Vector2(180, 20),
|
||||||
DefaultValues = new object[]
|
DefaultValues = new object[]
|
||||||
{
|
{
|
||||||
@@ -465,7 +464,7 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
Title = "Unpack Structure",
|
Title = "Unpack Structure",
|
||||||
Create = (id, context, arch, groupArch) => new UnpackStructureNode(id, context, arch, groupArch),
|
Create = (id, context, arch, groupArch) => new UnpackStructureNode(id, context, arch, groupArch),
|
||||||
Description = "Breaks the structure data to allow extracting components from it.",
|
Description = "Breaks the structure data to allow extracting components from it.",
|
||||||
Flags = NodeFlags.VisualScriptGraph | NodeFlags.NoSpawnViaGUI,
|
Flags = NodeFlags.VisualScriptGraph | NodeFlags.AnimGraph | NodeFlags.NoSpawnViaGUI,
|
||||||
Size = new Vector2(180, 20),
|
Size = new Vector2(180, 20),
|
||||||
DefaultValues = new object[]
|
DefaultValues = new object[]
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -119,6 +119,12 @@ void VisualScriptExecutor::Invoke(const Guid& scriptId, int32 nodeId, int32 boxI
|
|||||||
stack.Stack = frame.PreviousFrame;
|
stack.Stack = frame.PreviousFrame;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void VisualScriptExecutor::OnError(Node* node, Box* box, const StringView& message)
|
||||||
|
{
|
||||||
|
VisjectExecutor::OnError(node, box, message);
|
||||||
|
PrintStack(LogType::Error);
|
||||||
|
}
|
||||||
|
|
||||||
VisjectExecutor::Value VisualScriptExecutor::eatBox(Node* caller, Box* box)
|
VisjectExecutor::Value VisualScriptExecutor::eatBox(Node* caller, Box* box)
|
||||||
{
|
{
|
||||||
// Check if graph is looped or is too deep
|
// Check if graph is looped or is too deep
|
||||||
@@ -172,12 +178,6 @@ void VisualScriptExecutor::ProcessGroupConstants(Box* box, Node* node, Value& va
|
|||||||
{
|
{
|
||||||
switch (node->TypeID)
|
switch (node->TypeID)
|
||||||
{
|
{
|
||||||
// Enum
|
|
||||||
case 11:
|
|
||||||
{
|
|
||||||
value = node->Values[0];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
default:
|
||||||
VisjectExecutor::ProcessGroupConstants(box, node, value);
|
VisjectExecutor::ProcessGroupConstants(box, node, value);
|
||||||
break;
|
break;
|
||||||
@@ -188,179 +188,6 @@ void VisualScriptExecutor::ProcessGroupPacking(Box* box, Node* node, Value& valu
|
|||||||
{
|
{
|
||||||
switch (node->TypeID)
|
switch (node->TypeID)
|
||||||
{
|
{
|
||||||
// Pack Structure
|
|
||||||
case 26:
|
|
||||||
{
|
|
||||||
// Find type
|
|
||||||
const StringView typeName(node->Values[0]);
|
|
||||||
const StringAsANSI<100> typeNameAnsi(typeName.Get(), typeName.Length());
|
|
||||||
const StringAnsiView typeNameAnsiView(typeNameAnsi.Get(), typeName.Length());
|
|
||||||
const ScriptingTypeHandle typeHandle = Scripting::FindScriptingType(typeNameAnsiView);
|
|
||||||
if (!typeHandle)
|
|
||||||
{
|
|
||||||
const auto mclass = Scripting::FindClass(typeNameAnsiView);
|
|
||||||
if (mclass)
|
|
||||||
{
|
|
||||||
// Fallback to C#-only types
|
|
||||||
bool failed = false;
|
|
||||||
auto instance = mclass->CreateInstance();
|
|
||||||
value = instance;
|
|
||||||
auto& layoutCache = node->Values[1];
|
|
||||||
CHECK(layoutCache.Type.Type == VariantType::Blob);
|
|
||||||
MemoryReadStream stream((byte*)layoutCache.AsBlob.Data, layoutCache.AsBlob.Length);
|
|
||||||
const byte version = stream.ReadByte();
|
|
||||||
if (version == 1)
|
|
||||||
{
|
|
||||||
int32 fieldsCount;
|
|
||||||
stream.ReadInt32(&fieldsCount);
|
|
||||||
for (int32 boxId = 1; boxId < node->Boxes.Count(); boxId++)
|
|
||||||
{
|
|
||||||
box = &node->Boxes[boxId];
|
|
||||||
String fieldName;
|
|
||||||
stream.ReadString(&fieldName, 11);
|
|
||||||
VariantType fieldType;
|
|
||||||
stream.ReadVariantType(&fieldType);
|
|
||||||
if (box && box->HasConnection())
|
|
||||||
{
|
|
||||||
StringAsANSI<40> fieldNameAnsi(*fieldName, fieldName.Length());
|
|
||||||
auto field = mclass->GetField(fieldNameAnsi.Get());
|
|
||||||
if (field)
|
|
||||||
{
|
|
||||||
Variant fieldValue = eatBox(node, box->FirstConnection());
|
|
||||||
field->SetValue(instance, MUtils::VariantToManagedArgPtr(fieldValue, field->GetType(), failed));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (typeName.HasChars())
|
|
||||||
{
|
|
||||||
LOG(Error, "Missing type '{0}'", typeName);
|
|
||||||
PrintStack(LogType::Error);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const ScriptingType& type = typeHandle.GetType();
|
|
||||||
|
|
||||||
// Allocate structure data and initialize it with native constructor
|
|
||||||
value.SetType(VariantType(VariantType::Structure, typeNameAnsiView));
|
|
||||||
|
|
||||||
// Setup structure fields
|
|
||||||
auto& layoutCache = node->Values[1];
|
|
||||||
CHECK(layoutCache.Type.Type == VariantType::Blob);
|
|
||||||
MemoryReadStream stream((byte*)layoutCache.AsBlob.Data, layoutCache.AsBlob.Length);
|
|
||||||
const byte version = stream.ReadByte();
|
|
||||||
if (version == 1)
|
|
||||||
{
|
|
||||||
int32 fieldsCount;
|
|
||||||
stream.ReadInt32(&fieldsCount);
|
|
||||||
for (int32 boxId = 1; boxId < node->Boxes.Count(); boxId++)
|
|
||||||
{
|
|
||||||
box = &node->Boxes[boxId];
|
|
||||||
String fieldName;
|
|
||||||
stream.ReadString(&fieldName, 11);
|
|
||||||
VariantType fieldType;
|
|
||||||
stream.ReadVariantType(&fieldType);
|
|
||||||
if (box && box->HasConnection())
|
|
||||||
{
|
|
||||||
const Variant fieldValue = eatBox(node, box->FirstConnection());
|
|
||||||
type.Struct.SetField(value.AsBlob.Data, fieldName, fieldValue);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
// Unpack Structure
|
|
||||||
case 36:
|
|
||||||
{
|
|
||||||
// Get value with structure data
|
|
||||||
const Variant structureValue = eatBox(node, node->GetBox(0)->FirstConnection());
|
|
||||||
if (!node->GetBox(0)->HasConnection())
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Find type
|
|
||||||
const StringView typeName(node->Values[0]);
|
|
||||||
const StringAsANSI<100> typeNameAnsi(typeName.Get(), typeName.Length());
|
|
||||||
const StringAnsiView typeNameAnsiView(typeNameAnsi.Get(), typeName.Length());
|
|
||||||
const ScriptingTypeHandle typeHandle = Scripting::FindScriptingType(typeNameAnsiView);
|
|
||||||
if (!typeHandle)
|
|
||||||
{
|
|
||||||
const auto mclass = Scripting::FindClass(typeNameAnsiView);
|
|
||||||
if (mclass)
|
|
||||||
{
|
|
||||||
// Fallback to C#-only types
|
|
||||||
auto instance = (MonoObject*)structureValue;
|
|
||||||
CHECK(instance);
|
|
||||||
if (structureValue.Type.Type != VariantType::ManagedObject || mono_object_get_class(instance) != mclass->GetNative())
|
|
||||||
{
|
|
||||||
LOG(Error, "Cannot unpack value of type {0} to structure of type {1}", String(MUtils::GetClassFullname(instance)), typeName);
|
|
||||||
PrintStack(LogType::Error);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
auto& layoutCache = node->Values[1];
|
|
||||||
CHECK(layoutCache.Type.Type == VariantType::Blob);
|
|
||||||
MemoryReadStream stream((byte*)layoutCache.AsBlob.Data, layoutCache.AsBlob.Length);
|
|
||||||
const byte version = stream.ReadByte();
|
|
||||||
if (version == 1)
|
|
||||||
{
|
|
||||||
int32 fieldsCount;
|
|
||||||
stream.ReadInt32(&fieldsCount);
|
|
||||||
for (int32 boxId = 1; boxId < node->Boxes.Count(); boxId++)
|
|
||||||
{
|
|
||||||
String fieldName;
|
|
||||||
stream.ReadString(&fieldName, 11);
|
|
||||||
VariantType fieldType;
|
|
||||||
stream.ReadVariantType(&fieldType);
|
|
||||||
if (box->ID == boxId)
|
|
||||||
{
|
|
||||||
StringAsANSI<40> fieldNameAnsi(*fieldName, fieldName.Length());
|
|
||||||
auto field = mclass->GetField(fieldNameAnsi.Get());
|
|
||||||
if (field)
|
|
||||||
value = MUtils::UnboxVariant(field->GetValueBoxed(instance));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (typeName.HasChars())
|
|
||||||
{
|
|
||||||
LOG(Error, "Missing type '{0}'", typeName);
|
|
||||||
PrintStack(LogType::Error);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const ScriptingType& type = typeHandle.GetType();
|
|
||||||
if (structureValue.Type.Type != VariantType::Structure || StringUtils::Compare(typeNameAnsi.Get(), structureValue.Type.TypeName) != 0)
|
|
||||||
{
|
|
||||||
LOG(Error, "Cannot unpack value of type {0} to structure of type {1}", structureValue.Type, typeName);
|
|
||||||
PrintStack(LogType::Error);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read structure field
|
|
||||||
auto& layoutCache = node->Values[1];
|
|
||||||
CHECK(layoutCache.Type.Type == VariantType::Blob);
|
|
||||||
MemoryReadStream stream((byte*)layoutCache.AsBlob.Data, layoutCache.AsBlob.Length);
|
|
||||||
const byte version = stream.ReadByte();
|
|
||||||
if (version == 1)
|
|
||||||
{
|
|
||||||
int32 fieldsCount;
|
|
||||||
stream.ReadInt32(&fieldsCount);
|
|
||||||
for (int32 boxId = 1; boxId < node->Boxes.Count(); boxId++)
|
|
||||||
{
|
|
||||||
String fieldName;
|
|
||||||
stream.ReadString(&fieldName, 11);
|
|
||||||
VariantType fieldType;
|
|
||||||
stream.ReadVariantType(&fieldType);
|
|
||||||
if (box->ID == boxId)
|
|
||||||
{
|
|
||||||
type.Struct.GetField(structureValue.AsBlob.Data, fieldName, value);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
default:
|
||||||
VisjectExecutor::ProcessGroupPacking(box, node, value);
|
VisjectExecutor::ProcessGroupPacking(box, node, value);
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ public:
|
|||||||
void Invoke(const Guid& scriptId, int32 nodeId, int32 boxId, const Guid& instanceId, Variant& result) const;
|
void Invoke(const Guid& scriptId, int32 nodeId, int32 boxId, const Guid& instanceId, Variant& result) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void OnError(Node* node, Box* box, const StringView& message) override;
|
||||||
Value eatBox(Node* caller, Box* box) override;
|
Value eatBox(Node* caller, Box* box) override;
|
||||||
Graph* GetCurrentGraph() const override;
|
Graph* GetCurrentGraph() const override;
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,11 @@
|
|||||||
#include "Engine/Engine/GameplayGlobals.h"
|
#include "Engine/Engine/GameplayGlobals.h"
|
||||||
#include "Engine/Scripting/Scripting.h"
|
#include "Engine/Scripting/Scripting.h"
|
||||||
#include "Engine/Level/Actor.h"
|
#include "Engine/Level/Actor.h"
|
||||||
|
#include "Engine/Scripting/ManagedCLR/MType.h"
|
||||||
#include "Engine/Scripting/ManagedCLR/MClass.h"
|
#include "Engine/Scripting/ManagedCLR/MClass.h"
|
||||||
|
#include "Engine/Scripting/ManagedCLR/MField.h"
|
||||||
|
#include "Engine/Scripting/ManagedCLR/MUtils.h"
|
||||||
|
#include "Engine/Serialization/MemoryReadStream.h"
|
||||||
#include "Engine/Utilities/StringConverter.h"
|
#include "Engine/Utilities/StringConverter.h"
|
||||||
|
|
||||||
#define RAND Random::Rand()
|
#define RAND Random::Rand()
|
||||||
@@ -37,6 +41,7 @@ VisjectExecutor::~VisjectExecutor()
|
|||||||
void VisjectExecutor::OnError(Node* node, Box* box, const StringView& message)
|
void VisjectExecutor::OnError(Node* node, Box* box, const StringView& message)
|
||||||
{
|
{
|
||||||
Error(node, box, message);
|
Error(node, box, message);
|
||||||
|
LOG_STR(Error, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
void VisjectExecutor::ProcessGroupConstants(Box* box, Node* node, Value& value)
|
void VisjectExecutor::ProcessGroupConstants(Box* box, Node* node, Value& value)
|
||||||
@@ -99,15 +104,16 @@ void VisjectExecutor::ProcessGroupConstants(Box* box, Node* node, Value& value)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 9:
|
case 9:
|
||||||
{
|
|
||||||
value = node->Values[0];
|
value = node->Values[0];
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
// PI
|
// PI
|
||||||
case 10:
|
case 10:
|
||||||
value = PI;
|
value = PI;
|
||||||
break;
|
break;
|
||||||
|
// Enum
|
||||||
|
case 11:
|
||||||
|
value = node->Values[0];
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -506,6 +512,175 @@ void VisjectExecutor::ProcessGroupPacking(Box* box, Node* node, Value& value)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
// Pack Structure
|
||||||
|
case 26:
|
||||||
|
{
|
||||||
|
// Find type
|
||||||
|
const StringView typeName(node->Values[0]);
|
||||||
|
const StringAsANSI<100> typeNameAnsi(typeName.Get(), typeName.Length());
|
||||||
|
const StringAnsiView typeNameAnsiView(typeNameAnsi.Get(), typeName.Length());
|
||||||
|
const ScriptingTypeHandle typeHandle = Scripting::FindScriptingType(typeNameAnsiView);
|
||||||
|
if (!typeHandle)
|
||||||
|
{
|
||||||
|
const auto mclass = Scripting::FindClass(typeNameAnsiView);
|
||||||
|
if (mclass)
|
||||||
|
{
|
||||||
|
// Fallback to C#-only types
|
||||||
|
bool failed = false;
|
||||||
|
auto instance = mclass->CreateInstance();
|
||||||
|
value = instance;
|
||||||
|
auto& layoutCache = node->Values[1];
|
||||||
|
CHECK(layoutCache.Type.Type == VariantType::Blob);
|
||||||
|
MemoryReadStream stream((byte*)layoutCache.AsBlob.Data, layoutCache.AsBlob.Length);
|
||||||
|
const byte version = stream.ReadByte();
|
||||||
|
if (version == 1)
|
||||||
|
{
|
||||||
|
int32 fieldsCount;
|
||||||
|
stream.ReadInt32(&fieldsCount);
|
||||||
|
for (int32 boxId = 1; boxId < node->Boxes.Count(); boxId++)
|
||||||
|
{
|
||||||
|
box = &node->Boxes[boxId];
|
||||||
|
String fieldName;
|
||||||
|
stream.ReadString(&fieldName, 11);
|
||||||
|
VariantType fieldType;
|
||||||
|
stream.ReadVariantType(&fieldType);
|
||||||
|
if (box && box->HasConnection())
|
||||||
|
{
|
||||||
|
StringAsANSI<40> fieldNameAnsi(*fieldName, fieldName.Length());
|
||||||
|
auto field = mclass->GetField(fieldNameAnsi.Get());
|
||||||
|
if (field)
|
||||||
|
{
|
||||||
|
Variant fieldValue = eatBox(node, box->FirstConnection());
|
||||||
|
field->SetValue(instance, MUtils::VariantToManagedArgPtr(fieldValue, field->GetType(), failed));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (typeName.HasChars())
|
||||||
|
{
|
||||||
|
OnError(node, box, String::Format(TEXT("Missing type '{0}'"), typeName));
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const ScriptingType& type = typeHandle.GetType();
|
||||||
|
|
||||||
|
// Allocate structure data and initialize it with native constructor
|
||||||
|
value.SetType(VariantType(VariantType::Structure, typeNameAnsiView));
|
||||||
|
|
||||||
|
// Setup structure fields
|
||||||
|
auto& layoutCache = node->Values[1];
|
||||||
|
CHECK(layoutCache.Type.Type == VariantType::Blob);
|
||||||
|
MemoryReadStream stream((byte*)layoutCache.AsBlob.Data, layoutCache.AsBlob.Length);
|
||||||
|
const byte version = stream.ReadByte();
|
||||||
|
if (version == 1)
|
||||||
|
{
|
||||||
|
int32 fieldsCount;
|
||||||
|
stream.ReadInt32(&fieldsCount);
|
||||||
|
for (int32 boxId = 1; boxId < node->Boxes.Count(); boxId++)
|
||||||
|
{
|
||||||
|
box = &node->Boxes[boxId];
|
||||||
|
String fieldName;
|
||||||
|
stream.ReadString(&fieldName, 11);
|
||||||
|
VariantType fieldType;
|
||||||
|
stream.ReadVariantType(&fieldType);
|
||||||
|
if (box && box->HasConnection())
|
||||||
|
{
|
||||||
|
const Variant fieldValue = eatBox(node, box->FirstConnection());
|
||||||
|
type.Struct.SetField(value.AsBlob.Data, fieldName, fieldValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// Unpack Structure
|
||||||
|
case 36:
|
||||||
|
{
|
||||||
|
// Get value with structure data
|
||||||
|
const Variant structureValue = eatBox(node, node->GetBox(0)->FirstConnection());
|
||||||
|
if (!node->GetBox(0)->HasConnection())
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Find type
|
||||||
|
const StringView typeName(node->Values[0]);
|
||||||
|
const StringAsANSI<100> typeNameAnsi(typeName.Get(), typeName.Length());
|
||||||
|
const StringAnsiView typeNameAnsiView(typeNameAnsi.Get(), typeName.Length());
|
||||||
|
const ScriptingTypeHandle typeHandle = Scripting::FindScriptingType(typeNameAnsiView);
|
||||||
|
if (!typeHandle)
|
||||||
|
{
|
||||||
|
const auto mclass = Scripting::FindClass(typeNameAnsiView);
|
||||||
|
if (mclass)
|
||||||
|
{
|
||||||
|
// Fallback to C#-only types
|
||||||
|
auto instance = (MonoObject*)structureValue;
|
||||||
|
CHECK(instance);
|
||||||
|
if (structureValue.Type.Type != VariantType::ManagedObject || mono_object_get_class(instance) != mclass->GetNative())
|
||||||
|
{
|
||||||
|
OnError(node, box, String::Format(TEXT("Cannot unpack value of type {0} to structure of type {1}"), String(MUtils::GetClassFullname(instance)), typeName));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto& layoutCache = node->Values[1];
|
||||||
|
CHECK(layoutCache.Type.Type == VariantType::Blob);
|
||||||
|
MemoryReadStream stream((byte*)layoutCache.AsBlob.Data, layoutCache.AsBlob.Length);
|
||||||
|
const byte version = stream.ReadByte();
|
||||||
|
if (version == 1)
|
||||||
|
{
|
||||||
|
int32 fieldsCount;
|
||||||
|
stream.ReadInt32(&fieldsCount);
|
||||||
|
for (int32 boxId = 1; boxId < node->Boxes.Count(); boxId++)
|
||||||
|
{
|
||||||
|
String fieldName;
|
||||||
|
stream.ReadString(&fieldName, 11);
|
||||||
|
VariantType fieldType;
|
||||||
|
stream.ReadVariantType(&fieldType);
|
||||||
|
if (box->ID == boxId)
|
||||||
|
{
|
||||||
|
StringAsANSI<40> fieldNameAnsi(*fieldName, fieldName.Length());
|
||||||
|
auto field = mclass->GetField(fieldNameAnsi.Get());
|
||||||
|
if (field)
|
||||||
|
value = MUtils::UnboxVariant(field->GetValueBoxed(instance));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (typeName.HasChars())
|
||||||
|
{
|
||||||
|
OnError(node, box, String::Format(TEXT("Missing type '{0}'"), typeName));
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const ScriptingType& type = typeHandle.GetType();
|
||||||
|
if (structureValue.Type.Type != VariantType::Structure || StringUtils::Compare(typeNameAnsi.Get(), structureValue.Type.TypeName) != 0)
|
||||||
|
{
|
||||||
|
OnError(node, box, String::Format(TEXT("Cannot unpack value of type {0} to structure of type {1}"), structureValue.Type, typeName));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read structure field
|
||||||
|
auto& layoutCache = node->Values[1];
|
||||||
|
CHECK(layoutCache.Type.Type == VariantType::Blob);
|
||||||
|
MemoryReadStream stream((byte*)layoutCache.AsBlob.Data, layoutCache.AsBlob.Length);
|
||||||
|
const byte version = stream.ReadByte();
|
||||||
|
if (version == 1)
|
||||||
|
{
|
||||||
|
int32 fieldsCount;
|
||||||
|
stream.ReadInt32(&fieldsCount);
|
||||||
|
for (int32 boxId = 1; boxId < node->Boxes.Count(); boxId++)
|
||||||
|
{
|
||||||
|
String fieldName;
|
||||||
|
stream.ReadString(&fieldName, 11);
|
||||||
|
VariantType fieldType;
|
||||||
|
stream.ReadVariantType(&fieldType);
|
||||||
|
if (box->ID == boxId)
|
||||||
|
{
|
||||||
|
type.Struct.GetField(structureValue.AsBlob.Data, fieldName, value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
// Mask X, Y, Z, W
|
// Mask X, Y, Z, W
|
||||||
case 40:
|
case 40:
|
||||||
|
|||||||
@@ -246,7 +246,7 @@ public:
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
void OnError(Node* node, Box* box, const StringView& message);
|
virtual void OnError(Node* node, Box* box, const StringView& message);
|
||||||
|
|
||||||
void ProcessGroupConstants(Box* box, Node* node, Value& value);
|
void ProcessGroupConstants(Box* box, Node* node, Value& value);
|
||||||
void ProcessGroupMath(Box* box, Node* node, Value& value);
|
void ProcessGroupMath(Box* box, Node* node, Value& value);
|
||||||
|
|||||||
Reference in New Issue
Block a user