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
|
||||
Undo.Dispose();
|
||||
Surface.VisualScriptSurface.NodesCache.Clear();
|
||||
Surface.AnimGraphSurface.NodesCache.Clear();
|
||||
Instance = null;
|
||||
|
||||
// Invoke new instance if need to open a project
|
||||
|
||||
@@ -165,12 +165,13 @@ namespace FlaxEditor.SceneGraph
|
||||
/// <summary>
|
||||
/// The scene graph raycasting data container.
|
||||
/// </summary>
|
||||
[HideInEditor]
|
||||
public struct RayCastData
|
||||
{
|
||||
/// <summary>
|
||||
/// The raycasting optional flags.
|
||||
/// </summary>
|
||||
[Flags]
|
||||
[Flags, HideInEditor]
|
||||
public enum FlagTypes
|
||||
{
|
||||
/// <summary>
|
||||
@@ -334,6 +335,7 @@ namespace FlaxEditor.SceneGraph
|
||||
/// <summary>
|
||||
/// The scene graph node state container. Used for Editor undo actions (eg. restoring deleted node).
|
||||
/// </summary>
|
||||
[HideInEditor]
|
||||
public struct StateData
|
||||
{
|
||||
/// <summary>
|
||||
|
||||
@@ -2,10 +2,14 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using FlaxEditor.Content;
|
||||
using FlaxEditor.Scripting;
|
||||
using FlaxEditor.Surface.ContextMenu;
|
||||
using FlaxEditor.Surface.Elements;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
using Animation = FlaxEditor.Surface.Archetypes.Animation;
|
||||
|
||||
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>
|
||||
/// The state machine editing context menu.
|
||||
/// </summary>
|
||||
@@ -152,6 +348,23 @@ namespace FlaxEditor.Surface
|
||||
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 />
|
||||
public override string GetTypeName(ScriptType type)
|
||||
{
|
||||
@@ -230,6 +443,8 @@ namespace FlaxEditor.Surface
|
||||
/// <inheritdoc />
|
||||
public override void OnDestroy()
|
||||
{
|
||||
if (IsDisposing)
|
||||
return;
|
||||
if (_cmStateMachineMenu != null)
|
||||
{
|
||||
_cmStateMachineMenu.Dispose();
|
||||
@@ -245,6 +460,7 @@ namespace FlaxEditor.Surface
|
||||
_isRegisteredForScriptsReload = false;
|
||||
ScriptsBuilder.ScriptsReloadBegin -= OnScriptsReloadBegin;
|
||||
}
|
||||
NodesCache.Wait();
|
||||
|
||||
base.OnDestroy();
|
||||
}
|
||||
|
||||
@@ -354,7 +354,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
Title = "Enum",
|
||||
Create = (id, context, arch, groupArch) => new EnumNode(id, context, arch, groupArch),
|
||||
Description = "Enum constant value.",
|
||||
Flags = NodeFlags.VisualScriptGraph | NodeFlags.NoSpawnViaGUI,
|
||||
Flags = NodeFlags.VisualScriptGraph | NodeFlags.AnimGraph | NodeFlags.NoSpawnViaGUI,
|
||||
Size = new Vector2(180, 20),
|
||||
DefaultValues = new object[]
|
||||
{
|
||||
|
||||
@@ -7,7 +7,6 @@ using System.Reflection;
|
||||
using FlaxEditor.Scripting;
|
||||
using FlaxEditor.Surface.Elements;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
|
||||
namespace FlaxEditor.Surface.Archetypes
|
||||
{
|
||||
@@ -355,7 +354,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
Title = "Pack Structure",
|
||||
Create = (id, context, arch, groupArch) => new PackStructureNode(id, context, arch, groupArch),
|
||||
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),
|
||||
DefaultValues = new object[]
|
||||
{
|
||||
@@ -465,7 +464,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
Title = "Unpack Structure",
|
||||
Create = (id, context, arch, groupArch) => new UnpackStructureNode(id, context, arch, groupArch),
|
||||
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),
|
||||
DefaultValues = new object[]
|
||||
{
|
||||
|
||||
@@ -119,6 +119,12 @@ void VisualScriptExecutor::Invoke(const Guid& scriptId, int32 nodeId, int32 boxI
|
||||
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)
|
||||
{
|
||||
// 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)
|
||||
{
|
||||
// Enum
|
||||
case 11:
|
||||
{
|
||||
value = node->Values[0];
|
||||
break;
|
||||
}
|
||||
default:
|
||||
VisjectExecutor::ProcessGroupConstants(box, node, value);
|
||||
break;
|
||||
@@ -188,179 +188,6 @@ void VisualScriptExecutor::ProcessGroupPacking(Box* box, Node* node, Value& valu
|
||||
{
|
||||
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:
|
||||
VisjectExecutor::ProcessGroupPacking(box, node, value);
|
||||
break;
|
||||
|
||||
@@ -39,6 +39,7 @@ public:
|
||||
void Invoke(const Guid& scriptId, int32 nodeId, int32 boxId, const Guid& instanceId, Variant& result) const;
|
||||
|
||||
private:
|
||||
void OnError(Node* node, Box* box, const StringView& message) override;
|
||||
Value eatBox(Node* caller, Box* box) override;
|
||||
Graph* GetCurrentGraph() const override;
|
||||
|
||||
|
||||
@@ -8,7 +8,11 @@
|
||||
#include "Engine/Engine/GameplayGlobals.h"
|
||||
#include "Engine/Scripting/Scripting.h"
|
||||
#include "Engine/Level/Actor.h"
|
||||
#include "Engine/Scripting/ManagedCLR/MType.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"
|
||||
|
||||
#define RAND Random::Rand()
|
||||
@@ -37,6 +41,7 @@ VisjectExecutor::~VisjectExecutor()
|
||||
void VisjectExecutor::OnError(Node* node, Box* box, const StringView& message)
|
||||
{
|
||||
Error(node, box, message);
|
||||
LOG_STR(Error, message);
|
||||
}
|
||||
|
||||
void VisjectExecutor::ProcessGroupConstants(Box* box, Node* node, Value& value)
|
||||
@@ -99,15 +104,16 @@ void VisjectExecutor::ProcessGroupConstants(Box* box, Node* node, Value& value)
|
||||
break;
|
||||
}
|
||||
case 9:
|
||||
{
|
||||
value = node->Values[0];
|
||||
break;
|
||||
}
|
||||
// PI
|
||||
case 10:
|
||||
value = PI;
|
||||
break;
|
||||
|
||||
// Enum
|
||||
case 11:
|
||||
value = node->Values[0];
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -506,6 +512,175 @@ void VisjectExecutor::ProcessGroupPacking(Box* box, Node* node, Value& value)
|
||||
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
|
||||
case 40:
|
||||
|
||||
@@ -246,7 +246,7 @@ 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 ProcessGroupMath(Box* box, Node* node, Value& value);
|
||||
|
||||
Reference in New Issue
Block a user