diff --git a/Source/Engine/AI/BehaviorKnowledgeSelector.cs b/Source/Engine/AI/BehaviorKnowledgeSelector.cs
index 075d9decf..9ca6f83e2 100644
--- a/Source/Engine/AI/BehaviorKnowledgeSelector.cs
+++ b/Source/Engine/AI/BehaviorKnowledgeSelector.cs
@@ -74,7 +74,7 @@ namespace FlaxEngine
///
/// The knowledge container to access.
/// The value to set.
- /// True if set value value, otherwise false.
+ /// True if set value, otherwise false.
public bool Set(BehaviorKnowledge knowledge, object value)
{
return knowledge != null && knowledge.Set(Path, value);
diff --git a/Source/Engine/Core/Types/Variant.cpp b/Source/Engine/Core/Types/Variant.cpp
index 3734a5b95..7868b4719 100644
--- a/Source/Engine/Core/Types/Variant.cpp
+++ b/Source/Engine/Core/Types/Variant.cpp
@@ -3151,6 +3151,13 @@ Variant Variant::Parse(const StringView& text, const VariantType& type)
return result;
}
+Variant Variant::Typename(const StringAnsiView& value)
+{
+ Variant result;
+ result.SetTypename(value);
+ return result;
+}
+
bool Variant::CanCast(const Variant& v, const VariantType& to)
{
if (v.Type == to)
diff --git a/Source/Engine/Core/Types/Variant.h b/Source/Engine/Core/Types/Variant.h
index 5db8a6a83..0a7aff2aa 100644
--- a/Source/Engine/Core/Types/Variant.h
+++ b/Source/Engine/Core/Types/Variant.h
@@ -410,6 +410,8 @@ public:
return MoveTemp(v);
}
+ static Variant Typename(const StringAnsiView& value);
+
static bool CanCast(const Variant& v, const VariantType& to);
static Variant Cast(const Variant& v, const VariantType& to);
static bool NearEqual(const Variant& a, const Variant& b, float epsilon = 1e-6f);
diff --git a/Source/Engine/Scripting/SoftTypeReference.cs b/Source/Engine/Scripting/SoftTypeReference.cs
index 11067c141..67b5a2513 100644
--- a/Source/Engine/Scripting/SoftTypeReference.cs
+++ b/Source/Engine/Scripting/SoftTypeReference.cs
@@ -38,6 +38,16 @@ namespace FlaxEngine
_typeName = typeName;
}
+ ///
+ /// Implicit cast operator from type name to string.
+ ///
+ /// The soft type reference.
+ /// The type name.
+ public static implicit operator string(SoftTypeReference s)
+ {
+ return s._typeName;
+ }
+
///
/// Gets the soft type reference from full name.
///
diff --git a/Source/Engine/Scripting/SoftTypeReference.h b/Source/Engine/Scripting/SoftTypeReference.h
index 3da71d7e2..c37ad1c1f 100644
--- a/Source/Engine/Scripting/SoftTypeReference.h
+++ b/Source/Engine/Scripting/SoftTypeReference.h
@@ -12,7 +12,7 @@
/// The soft reference to the scripting type contained in the scripting assembly.
///
template
-API_STRUCT(InBuild) struct SoftTypeReference
+API_STRUCT(InBuild, MarshalAs=StringAnsi) struct SoftTypeReference
{
protected:
StringAnsi _typeName;
@@ -64,7 +64,7 @@ public:
return *this;
}
- FORCE_INLINE SoftTypeReference& operator=(const StringAnsiView& s)
+ FORCE_INLINE SoftTypeReference& operator=(const StringAnsiView& s) noexcept
{
_typeName = s;
return *this;
@@ -95,6 +95,16 @@ public:
return _typeName.HasChars();
}
+ operator StringAnsi() const
+ {
+ return _typeName;
+ }
+
+ String ToString() const
+ {
+ return _typeName.ToString();
+ }
+
public:
// Gets the type full name (eg. FlaxEngine.Actor).
StringAnsiView GetTypeName() const
diff --git a/Source/Engine/Tests/TestScripting.h b/Source/Engine/Tests/TestScripting.h
index 95cd403e4..8107b94b9 100644
--- a/Source/Engine/Tests/TestScripting.h
+++ b/Source/Engine/Tests/TestScripting.h
@@ -7,6 +7,8 @@
#include "Engine/Core/Collections/Array.h"
#include "Engine/Scripting/ScriptingObject.h"
#include "Engine/Scripting/SerializableScriptingObject.h"
+#include "Engine/Scripting/SoftTypeReference.h"
+#include "Engine/Content/SceneReference.h"
// Test default values init on fields.
API_STRUCT(NoDefault) struct TestDefaultValues
@@ -124,10 +126,17 @@ API_STRUCT(NoDefault) struct TestStruct : public ISerializable
API_FIELD() Float3 Vector = Float3::One;
// Ref
API_FIELD() ScriptingObject* Object = nullptr;
+ // Soft Type Ref
+ API_FIELD() SoftTypeReference SoftTypeRef;
+ // Scene Ref
+ API_FIELD() SceneReference SceneRef;
friend bool operator==(const TestStruct& lhs, const TestStruct& rhs)
{
- return lhs.Vector == rhs.Vector && lhs.Object == rhs.Object;
+ return lhs.Vector == rhs.Vector &&
+ lhs.Object == rhs.Object &&
+ lhs.SoftTypeRef == rhs.SoftTypeRef &&
+ lhs.SceneRef == rhs.SceneRef;
}
};
diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs
index 5d03d0489..b524e1648 100644
--- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs
+++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs
@@ -1605,41 +1605,43 @@ namespace Flax.Build.Bindings
if (fieldInfo.IsStatic || fieldInfo.IsConstexpr)
continue;
+ var marshalType = fieldInfo.Type;
+ var apiType = FindApiTypeInfo(buildData, marshalType, structureInfo);
+ if (apiType != null && apiType.MarshalAs != null)
+ marshalType = apiType.MarshalAs;
+ bool internalType = apiType is StructureInfo fieldStructureInfo && UseCustomMarshalling(buildData, fieldStructureInfo, structureInfo);
string type, originalType;
- if (fieldInfo.Type.IsArray && (fieldInfo.NoArray || structureInfo.IsPod))
+ if (marshalType.IsArray && (fieldInfo.NoArray || structureInfo.IsPod))
{
// Fixed-size array that needs to be inlined into structure instead of passing it as managed array
- fieldInfo.Type.IsArray = false;
- originalType = type = GenerateCSharpNativeToManaged(buildData, fieldInfo.Type, structureInfo);
- fieldInfo.Type.IsArray = true;
+ marshalType.IsArray = false;
+ originalType = type = GenerateCSharpNativeToManaged(buildData, marshalType, structureInfo);
+ marshalType.IsArray = true;
}
else
- originalType = type = GenerateCSharpNativeToManaged(buildData, fieldInfo.Type, structureInfo);
-
+ originalType = type = GenerateCSharpNativeToManaged(buildData, marshalType, structureInfo);
+ if (apiType != null && apiType.MarshalAs != null)
+ Log.Error("marshal as into type: " + type);
structContents.Append(structIndent).Append("public ");
-
- var apiType = FindApiTypeInfo(buildData, fieldInfo.Type, structureInfo);
- bool internalType = apiType is StructureInfo fieldStructureInfo && UseCustomMarshalling(buildData, fieldStructureInfo, structureInfo);
string internalTypeMarshaller = "";
-
- if (fieldInfo.Type.IsArray && (fieldInfo.NoArray || structureInfo.IsPod))
+ if (marshalType.IsArray && (fieldInfo.NoArray || structureInfo.IsPod))
{
#if USE_NETCORE
if (GenerateCSharpUseFixedBuffer(originalType))
{
// Use fixed statement with primitive types of buffers
- structContents.Append($"fixed {originalType} {fieldInfo.Name}0[{fieldInfo.Type.ArraySize}];").AppendLine();
+ structContents.Append($"fixed {originalType} {fieldInfo.Name}0[{marshalType.ArraySize}];").AppendLine();
// Copy fixed-size array
- toManagedContent.AppendLine($"FlaxEngine.Utils.MemoryCopy(new IntPtr(managed.{fieldInfo.Name}0), new IntPtr(unmanaged.{fieldInfo.Name}0), sizeof({originalType}) * {fieldInfo.Type.ArraySize}ul);");
- toNativeContent.AppendLine($"FlaxEngine.Utils.MemoryCopy(new IntPtr(unmanaged.{fieldInfo.Name}0), new IntPtr(managed.{fieldInfo.Name}0), sizeof({originalType}) * {fieldInfo.Type.ArraySize}ul);");
+ toManagedContent.AppendLine($"FlaxEngine.Utils.MemoryCopy(new IntPtr(managed.{fieldInfo.Name}0), new IntPtr(unmanaged.{fieldInfo.Name}0), sizeof({originalType}) * {marshalType.ArraySize}ul);");
+ toNativeContent.AppendLine($"FlaxEngine.Utils.MemoryCopy(new IntPtr(unmanaged.{fieldInfo.Name}0), new IntPtr(managed.{fieldInfo.Name}0), sizeof({originalType}) * {marshalType.ArraySize}ul);");
}
else
#endif
{
// Padding in structs for fixed-size array
structContents.Append(type).Append(' ').Append(fieldInfo.Name).Append("0;").AppendLine();
- for (int i = 1; i < fieldInfo.Type.ArraySize; i++)
+ for (int i = 1; i < marshalType.ArraySize; i++)
{
GenerateCSharpAttributes(buildData, structContents, structIndent, structureInfo, fieldInfo, fieldInfo.IsStatic);
structContents.Append(structIndent).Append("public ");
@@ -1649,7 +1651,7 @@ namespace Flax.Build.Bindings
// Copy fixed-size array item one-by-one
if (fieldInfo.Access == AccessLevel.Public || fieldInfo.Access == AccessLevel.Internal)
{
- for (int i = 0; i < fieldInfo.Type.ArraySize; i++)
+ for (int i = 0; i < marshalType.ArraySize; i++)
{
toManagedContent.AppendLine($"managed.{fieldInfo.Name}{i} = unmanaged.{fieldInfo.Name}{i};");
toNativeContent.AppendLine($"unmanaged.{fieldInfo.Name}{i} = managed.{fieldInfo.Name}{i};");
@@ -1664,23 +1666,23 @@ namespace Flax.Build.Bindings
}
else
{
- if (fieldInfo.Type.IsObjectRef || fieldInfo.Type.Type == "Dictionary")
+ if (marshalType.IsObjectRef || marshalType.Type == "Dictionary")
type = "IntPtr";
- else if (fieldInfo.Type.IsPtr && !originalType.EndsWith("*"))
+ else if (marshalType.IsPtr && !originalType.EndsWith("*"))
type = "IntPtr";
- else if (fieldInfo.Type.Type == "Array" || fieldInfo.Type.Type == "Span" || fieldInfo.Type.Type == "DataContainer" || fieldInfo.Type.Type == "BytesContainer")
+ else if (marshalType.Type == "Array" || marshalType.Type == "Span" || marshalType.Type == "DataContainer" || marshalType.Type == "BytesContainer")
{
type = "IntPtr";
- apiType = FindApiTypeInfo(buildData, fieldInfo.Type.GenericArgs[0], structureInfo);
+ apiType = FindApiTypeInfo(buildData, marshalType.GenericArgs[0], structureInfo);
internalType = apiType is StructureInfo elementStructureInfo && UseCustomMarshalling(buildData, elementStructureInfo, structureInfo);
}
- else if (fieldInfo.Type.Type == "Version")
+ else if (marshalType.Type == "Version")
type = "IntPtr";
else if (type == "string")
type = "IntPtr";
else if (type == "bool")
type = "byte";
- else if (fieldInfo.Type.Type == "Variant")
+ else if (marshalType.Type == "Variant")
type = "IntPtr";
else if (internalType)
{
@@ -1695,9 +1697,9 @@ namespace Flax.Build.Bindings
// Generate struct constructor/getter and deconstructor/setter function
toManagedContent.Append("managed.").Append(fieldInfo.Name).Append(" = ");
toNativeContent.Append("unmanaged.").Append(fieldInfo.Name).Append(" = ");
- if (fieldInfo.Type.IsObjectRef)
+ if (marshalType.IsObjectRef)
{
- var managedType = GenerateCSharpNativeToManaged(buildData, fieldInfo.Type.GenericArgs[0], structureInfo);
+ var managedType = GenerateCSharpNativeToManaged(buildData, marshalType.GenericArgs[0], structureInfo);
toManagedContent.AppendLine($"unmanaged.{fieldInfo.Name} != IntPtr.Zero ? Unsafe.As<{managedType}>(ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Target) : null;");
toNativeContent.AppendLine($"managed.{fieldInfo.Name} != null ? ManagedHandle.ToIntPtr(managed.{fieldInfo.Name}, GCHandleType.Weak) : IntPtr.Zero;");
freeContents.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Free(); }}");
@@ -1705,7 +1707,7 @@ namespace Flax.Build.Bindings
// Permanent ScriptingObject handle is passed from native side, do not release it
//freeContents2.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Free(); }}");
}
- else if (fieldInfo.Type.Type == "ScriptingObject")
+ else if (marshalType.Type == "ScriptingObject")
{
toManagedContent.AppendLine($"unmanaged.{fieldInfo.Name} != IntPtr.Zero ? Unsafe.As(ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Target) : null;");
toNativeContent.AppendLine($"managed.{fieldInfo.Name} != null ? ManagedHandle.ToIntPtr(managed.{fieldInfo.Name}, GCHandleType.Weak) : IntPtr.Zero;");
@@ -1714,7 +1716,7 @@ namespace Flax.Build.Bindings
// Permanent ScriptingObject handle is passed from native side, do not release it
//freeContents2.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Free(); }}");
}
- else if (fieldInfo.Type.IsPtr && originalType != "IntPtr" && !originalType.EndsWith("*"))
+ else if (marshalType.IsPtr && originalType != "IntPtr" && !originalType.EndsWith("*"))
{
toManagedContent.AppendLine($"unmanaged.{fieldInfo.Name} != IntPtr.Zero ? Unsafe.As<{originalType}>(ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Target) : null;");
toNativeContent.AppendLine($"managed.{fieldInfo.Name} != null ? ManagedHandle.ToIntPtr(managed.{fieldInfo.Name}, GCHandleType.Weak) : IntPtr.Zero;");
@@ -1723,14 +1725,14 @@ namespace Flax.Build.Bindings
// Permanent ScriptingObject handle is passed from native side, do not release it
//freeContents2.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Free(); }}");
}
- else if (fieldInfo.Type.Type == "Dictionary")
+ else if (marshalType.Type == "Dictionary")
{
toManagedContent.AppendLine($"unmanaged.{fieldInfo.Name} != IntPtr.Zero ? Unsafe.As<{originalType}>(ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Target) : null;");
toNativeContent.AppendLine($"ManagedHandle.ToIntPtr(managed.{fieldInfo.Name}, GCHandleType.Weak);");
freeContents.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Free(); }}");
freeContents2.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Free(); }}");
}
- else if (fieldInfo.Type.Type == "Array" || fieldInfo.Type.Type == "Span" || fieldInfo.Type.Type == "DataContainer" || fieldInfo.Type.Type == "BytesContainer")
+ else if (marshalType.Type == "Array" || marshalType.Type == "Span" || marshalType.Type == "DataContainer" || marshalType.Type == "BytesContainer")
{
string originalElementType = originalType.Substring(0, originalType.Length - 2);
if (internalType)
@@ -1744,7 +1746,7 @@ namespace Flax.Build.Bindings
freeContents.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ ManagedHandle handle = ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}); Span<{internalElementType}> values = (Unsafe.As(handle.Target)).ToSpan<{internalElementType}>(); foreach (var value in values) {{ {originalElementTypeMarshaller}.Free(value); }} (Unsafe.As(handle.Target)).Free(); handle.Free(); }}");
freeContents2.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ ManagedHandle handle = ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}); Span<{internalElementType}> values = (Unsafe.As(handle.Target)).ToSpan<{internalElementType}>(); foreach (var value in values) {{ {originalElementTypeMarshaller}.NativeToManaged.Free(value); }} (Unsafe.As(handle.Target)).Free(); handle.Free(); }}");
}
- else if (fieldInfo.Type.GenericArgs[0].IsObjectRef)
+ else if (marshalType.GenericArgs[0].IsObjectRef)
{
// Array elements passed as GCHandles
toManagedContent.AppendLine($"unmanaged.{fieldInfo.Name} != IntPtr.Zero ? NativeInterop.GCHandleArrayToManagedArray<{originalElementType}>(Unsafe.As(ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Target)) : null;");
@@ -1763,7 +1765,7 @@ namespace Flax.Build.Bindings
freeContents2.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ ManagedHandle handle = ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}); (Unsafe.As(handle.Target)).Free(); handle.Free(); }}");
}
}
- else if (fieldInfo.Type.Type == "Version")
+ else if (marshalType.Type == "Version")
{
toManagedContent.AppendLine($"unmanaged.{fieldInfo.Name} != IntPtr.Zero ? Unsafe.As<{originalType}>(ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Target) : null;");
toNativeContent.AppendLine($"ManagedHandle.ToIntPtr(managed.{fieldInfo.Name}, GCHandleType.Weak);");
@@ -1782,7 +1784,7 @@ namespace Flax.Build.Bindings
toManagedContent.AppendLine($"unmanaged.{fieldInfo.Name} != 0;");
toNativeContent.AppendLine($"managed.{fieldInfo.Name} ? (byte)1 : (byte)0;");
}
- else if (fieldInfo.Type.Type == "Variant")
+ else if (marshalType.Type == "Variant")
{
// Variant passed as boxed object handle
toManagedContent.AppendLine($"ManagedHandleMarshaller.NativeToManaged.ConvertToManaged(unmanaged.{fieldInfo.Name});");
diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs
index a2c5f6934..49c0fa97f 100644
--- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs
+++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs
@@ -169,6 +169,8 @@ namespace Flax.Build.Bindings
return $"Variant(StringAnsiView({value}))";
if (typeInfo.IsObjectRef)
return $"Variant({value}.Get())";
+ if (typeInfo.Type == "SoftTypeReference")
+ return $"Variant::Typename(StringAnsiView({value}))";
if (typeInfo.IsArray)
{
var wrapperName = GenerateCppWrapperNativeToVariantMethodName(typeInfo);
@@ -227,6 +229,8 @@ namespace Flax.Build.Bindings
return $"ScriptingObject::Cast<{typeInfo.GenericArgs[0].Type}>((ScriptingObject*){value})";
if (typeInfo.IsObjectRef)
return $"ScriptingObject::Cast<{typeInfo.GenericArgs[0].Type}>((Asset*){value})";
+ if (typeInfo.Type == "SoftTypeReference")
+ return $"(StringAnsiView){value}";
if (typeInfo.IsArray)
throw new Exception($"Not supported type to convert from the Variant to fixed-size array '{typeInfo}[{typeInfo.ArraySize}]'.");
if (typeInfo.Type == "Array" && typeInfo.GenericArgs != null)
@@ -258,10 +262,24 @@ namespace Flax.Build.Bindings
if (apiType.IsScriptingObject)
return $"ScriptingObject::Cast<{typeInfo.Type}>((ScriptingObject*){value})";
if (apiType.IsStruct && !CppInBuildVariantStructures.Contains(apiType.Name))
+ {
+ var name = apiType.FullNameNative;
+ if (typeInfo.GenericArgs != null)
+ {
+ name += '<';
+ for (var i = 0; i < typeInfo.GenericArgs.Count; i++)
+ {
+ if (i != 0)
+ name += ", ";
+ name += typeInfo.GenericArgs[i];
+ }
+ name += '>';
+ }
if (typeInfo.IsPtr)
- return $"({apiType.FullNameNative}*){value}.AsBlob.Data";
+ return $"({name}*){value}.AsBlob.Data";
else
- return $"*({apiType.FullNameNative}*){value}.AsBlob.Data";
+ return $"*({name}*){value}.AsBlob.Data";
+ }
}
if (typeInfo.IsPtr)