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)