diff --git a/Source/Engine/Scripting/Object.cs b/Source/Engine/Scripting/Object.cs index 1c649ba9b..a4e2424e9 100644 --- a/Source/Engine/Scripting/Object.cs +++ b/Source/Engine/Scripting/Object.cs @@ -205,6 +205,11 @@ namespace FlaxEngine return obj?.__unmanagedPtr ?? IntPtr.Zero; } + internal static IntPtr GetUnmanagedPtr(SoftObjectReference reference) + { + return GetUnmanagedPtr(reference.Get()); + } + /// public override int GetHashCode() { diff --git a/Source/Engine/Scripting/SoftObjectReference.cs b/Source/Engine/Scripting/SoftObjectReference.cs new file mode 100644 index 000000000..df098d02a --- /dev/null +++ b/Source/Engine/Scripting/SoftObjectReference.cs @@ -0,0 +1,80 @@ +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. + +using System; + +namespace FlaxEngine +{ + /// + /// The scripting object soft reference. Objects gets referenced on use (ID reference is resolving it). + /// + public struct SoftObjectReference : IComparable, IComparable + { + private Guid _id; + private Object _object; + + /// + /// Gets or sets the object identifier. + /// + public Guid ID + { + get => _id; + set + { + if (_id == value) + return; + _id = value; + _object = null; + } + } + + /// + /// Gets the object reference. + /// + /// The object type. + /// The resolved object or null. + public T Get() where T : Object + { + if (!_object) + _object = Object.Find(ref _id, typeof(T)); + return _object as T; + } + + /// + /// Sets the object reference. + /// + /// The object. + public void Set(Object obj) + { + _object = obj; + _id = obj?.ID ?? Guid.Empty; + } + + /// + public override string ToString() + { + if (_object) + return _object.ToString(); + return _id.ToString(); + } + + /// + public override int GetHashCode() + { + return _id.GetHashCode(); + } + + /// + public int CompareTo(object obj) + { + if (obj is SoftObjectReference other) + return CompareTo(other); + return 0; + } + + /// + public int CompareTo(SoftObjectReference other) + { + return _id.CompareTo(other._id); + } + } +} diff --git a/Source/Engine/Scripting/SoftObjectReference.h b/Source/Engine/Scripting/SoftObjectReference.h new file mode 100644 index 000000000..1befaa0f7 --- /dev/null +++ b/Source/Engine/Scripting/SoftObjectReference.h @@ -0,0 +1,327 @@ +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. + +#pragma once + +#include "Engine/Scripting/ScriptingObject.h" + +// Don't include Scripting.h but just FindObject method +extern FLAXENGINE_API ScriptingObject* FindObject(const Guid& id, MClass* type); + +/// +/// The scripting object soft reference. Objects gets referenced on use (ID reference is resolving it). +/// +class FLAXENGINE_API SoftObjectReferenceBase +{ +public: + + typedef Delegate<> EventType; + +protected: + + ScriptingObject* _object = nullptr; + Guid _id = Guid::Empty; + +public: + + /// + /// Action fired when reference gets changed. + /// + EventType Changed; + +public: + + /// + /// Initializes a new instance of the class. + /// + SoftObjectReferenceBase() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The object to link. + SoftObjectReferenceBase(ScriptingObject* obj) + { + OnSet(obj); + } + + /// + /// Finalizes an instance of the class. + /// + ~SoftObjectReferenceBase() + { + if (_object) + _object->Deleted.Unbind(this); + } + +public: + + /// + /// Gets the object ID. + /// + /// The object ID or Guid::Empty if nothing assigned. + Guid GetID() const + { + return _object ? _object->GetID() : _id; + } + +protected: + + /// + /// Sets the object. + /// + /// The object. + void OnSet(ScriptingObject* object) + { + auto e = _object; + if (e != object) + { + if (e) + e->Deleted.Unbind(this); + _object = e = object; + _id = e ? e->GetID() : Guid::Empty; + if (e) + e->Deleted.Bind(this); + Changed(); + } + } + + void OnDeleted(ScriptingObject* obj) + { + ASSERT(_object == obj); + _object->Deleted.Unbind(this); + _object = nullptr; + Changed(); + } +}; + +/// +/// The scripting object soft reference. Objects gets referenced on use (ID reference is resolving it). +/// +template +API_CLASS(InBuild) class SoftObjectReference : public SoftObjectReferenceBase +{ +public: + + typedef SoftObjectReference Type; + +public: + + /// + /// Initializes a new instance of the class. + /// + SoftObjectReference() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The object to link. + SoftObjectReference(T* obj) + : SoftObjectReferenceBase(obj) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The other property. + SoftObjectReference(const SoftObjectReference& other) + : SoftObjectReferenceBase(other.Get()) + { + } + + /// + /// Finalizes an instance of the class. + /// + ~SoftObjectReference() + { + } + +public: + + /// + /// Compares the property value with the given object. + /// + /// The other. + /// True if property object equals the given value. + FORCE_INLINE bool operator==(T* other) + { + return Get() == other; + } + + /// + /// Compares the property value with the other property value. + /// + /// The other property. + /// True if properties are equal. + FORCE_INLINE bool operator==(const SoftObjectReference& other) + { + return _id == other._id; + } + + /// + /// Compares the property value with the given object. + /// + /// The other. + /// True if property object not equals the given value. + FORCE_INLINE bool operator!=(T* other) + { + return Get() != other; + } + + /// + /// Compares the property value with the other property value. + /// + /// The other property. + /// True if properties are not equal. + FORCE_INLINE bool operator!=(const SoftObjectReference& other) + { + return _id != other._id; + } + + /// + /// Sets the property to the given property value. + /// + /// The other property. + /// The reference to this property. + SoftObjectReference& operator=(const SoftObjectReference& other) + { + if (this != &other) + OnSet(other.Get()); + return *this; + } + + /// + /// Sets the property to the given value. + /// + /// The object. + /// The reference to this property. + FORCE_INLINE SoftObjectReference& operator=(const T& other) + { + OnSet(&other); + return *this; + } + + /// + /// Sets the property to the given value. + /// + /// The object. + /// The reference to this property. + FORCE_INLINE SoftObjectReference& operator=(T* other) + { + OnSet(other); + return *this; + } + + /// + /// Sets the property to the object of the given ID. + /// + /// The object ID. + /// The reference to this property. + FORCE_INLINE SoftObjectReference& operator=(const Guid& id) + { + Set(id); + return *this; + } + + /// + /// Implicit conversion to the object. + /// + /// The object reference. + FORCE_INLINE operator T*() const + { + return (T*)Get(); + } + + /// + /// Implicit conversion to boolean value. + /// + /// True if object has been assigned, otherwise false + FORCE_INLINE operator bool() const + { + return _object != nullptr || _id.IsValid(); + } + + /// + /// Object accessor. + /// + /// The object reference. + FORCE_INLINE T* operator->() const + { + return (T*)Get(); + } + + /// + /// Gets the object pointer. + /// + /// The object reference. + T* Get() const + { + if (!_object) + const_cast(this)->OnSet(FindObject(_id, T::GetStaticClass())); + return (T*)_object; + } + + /// + /// Gets the object as a given type (static cast). + /// + /// Asset + template + FORCE_INLINE U* As() const + { + return static_cast(Get()); + } + +public: + + /// + /// Gets managed instance object (or null if no object linked). + /// + /// The managed object instance. + MonoObject* GetManagedInstance() const + { + auto object = Get(); + return object ? object->GetOrCreateManagedInstance() : nullptr; + } + + /// + /// Determines whether object is assigned and managed instance of the object is alive. + /// + /// True if managed object has been created and exists, otherwise false. + bool HasManagedInstance() const + { + auto object = Get(); + return object && object->HasManagedInstance(); + } + + /// + /// Gets the managed instance object or creates it if missing or null if not assigned. + /// + /// The Mono managed object. + MonoObject* GetOrCreateManagedInstance() const + { + auto object = Get(); + return object ? object->GetOrCreateManagedInstance() : nullptr; + } + + /// + /// Sets the object. + /// + /// The object ID. Uses Scripting to find the registered object of the given ID. + FORCE_INLINE void Set(const Guid& id) + { + Set(static_cast(FindObject(id, T::GetStaticClass()))); + } + + /// + /// Sets the object. + /// + /// The object. + FORCE_INLINE void Set(T* object) + { + OnSet(object); + } +}; diff --git a/Source/Engine/Serialization/JsonSerializer.cs b/Source/Engine/Serialization/JsonSerializer.cs index 535499442..d3300dd23 100644 --- a/Source/Engine/Serialization/JsonSerializer.cs +++ b/Source/Engine/Serialization/JsonSerializer.cs @@ -87,6 +87,38 @@ namespace FlaxEngine.Json } } + /// + /// Serialize SoftObjectReference as Guid in internal format. + /// + /// + internal class SoftObjectReferenceConverter : JsonConverter + { + /// + public override unsafe void WriteJson(JsonWriter writer, object value, Newtonsoft.Json.JsonSerializer serializer) + { + var id = ((SoftObjectReference)value).ID; + writer.WriteValue(JsonSerializer.GetStringID(&id)); + } + + /// + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, Newtonsoft.Json.JsonSerializer serializer) + { + var result = new SoftObjectReference(); + if (reader.TokenType == JsonToken.String) + { + JsonSerializer.ParseID((string)reader.Value, out var id); + result.ID = id; + } + return result; + } + + /// + public override bool CanConvert(Type objectType) + { + return objectType == typeof(SoftObjectReference); + } + } + /* /// /// Serialize Guid values using `N` format @@ -184,6 +216,7 @@ namespace FlaxEngine.Json ObjectConverter = new FlaxObjectConverter(); settings.Converters.Add(ObjectConverter); settings.Converters.Add(new SceneReferenceConverter()); + settings.Converters.Add(new SoftObjectReferenceConverter()); settings.Converters.Add(new VersionConverter()); //settings.Converters.Add(new GuidConverter()); return settings; diff --git a/Source/Engine/Serialization/Serialization.h b/Source/Engine/Serialization/Serialization.h index b760e9689..039d9df3e 100644 --- a/Source/Engine/Serialization/Serialization.h +++ b/Source/Engine/Serialization/Serialization.h @@ -9,6 +9,7 @@ #include "Engine/Core/Collections/Array.h" #include "Engine/Core/Collections/Dictionary.h" #include "Engine/Scripting/ScriptingObjectReference.h" +#include "Engine/Scripting/SoftObjectReference.h" #include "Engine/Content/AssetReference.h" #include "Engine/Content/WeakAssetReference.h" @@ -457,6 +458,27 @@ namespace Serialization v = id; } + // Soft Object Reference + + template + inline bool ShouldSerialize(const SoftObjectReference& v, const void* otherObj) + { + return !otherObj || v.Get() != ((SoftObjectReference*)otherObj)->Get(); + } + template + inline void Serialize(ISerializable::SerializeStream& stream, const SoftObjectReference& v, const void* otherObj) + { + stream.Guid(v.GetID()); + } + template + inline void Deserialize(ISerializable::DeserializeStream& stream, SoftObjectReference& v, ISerializeModifier* modifier) + { + Guid id; + Deserialize(stream, id, modifier); + modifier->IdsMapping.TryGet(id, id); + v = id; + } + // Asset Reference template diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs index 8b679fb7a..861d96a1a 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs @@ -132,8 +132,8 @@ namespace Flax.Build.Bindings return result; } - // ScriptingObjectReference or AssetReference or WeakAssetReference - if ((typeInfo.Type == "ScriptingObjectReference" || typeInfo.Type == "AssetReference" || typeInfo.Type == "WeakAssetReference") && typeInfo.GenericArgs != null) + // ScriptingObjectReference or AssetReference or WeakAssetReference or SoftObjectReference + if ((typeInfo.Type == "ScriptingObjectReference" || typeInfo.Type == "AssetReference" || typeInfo.Type == "WeakAssetReference" || typeInfo.Type == "SoftObjectReference") && typeInfo.GenericArgs != null) return typeInfo.GenericArgs[0].Type.Replace("::", "."); // Array or Span @@ -188,8 +188,8 @@ namespace Flax.Build.Bindings return "IntPtr"; } - // ScriptingObjectReference or AssetReference or WeakAssetReference - if ((typeInfo.Type == "ScriptingObjectReference" || typeInfo.Type == "AssetReference" || typeInfo.Type == "WeakAssetReference") && typeInfo.GenericArgs != null) + // ScriptingObjectReference or AssetReference or WeakAssetReference or SoftObjectReference + if ((typeInfo.Type == "ScriptingObjectReference" || typeInfo.Type == "AssetReference" || typeInfo.Type == "WeakAssetReference" || typeInfo.Type == "SoftObjectReference") && typeInfo.GenericArgs != null) return "IntPtr"; return GenerateCSharpNativeToManaged(buildData, typeInfo, caller); @@ -231,8 +231,8 @@ namespace Flax.Build.Bindings return "FlaxEngine.Object.GetUnmanagedPtr"; } - // ScriptingObjectReference or AssetReference or WeakAssetReference - if ((typeInfo.Type == "ScriptingObjectReference" || typeInfo.Type == "AssetReference" || typeInfo.Type == "WeakAssetReference") && typeInfo.GenericArgs != null) + // ScriptingObjectReference or AssetReference or WeakAssetReference or SoftObjectReference + if ((typeInfo.Type == "ScriptingObjectReference" || typeInfo.Type == "AssetReference" || typeInfo.Type == "WeakAssetReference" || typeInfo.Type == "SoftObjectReference") && typeInfo.GenericArgs != null) return "FlaxEngine.Object.GetUnmanagedPtr"; // Default diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs index 11fe6a414..ff2a2fadd 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs @@ -100,7 +100,7 @@ namespace Flax.Build.Bindings return value; if (typeInfo.Type == "String") return $"Variant(StringView({value}))"; - if (typeInfo.Type == "AssetReference" || typeInfo.Type == "WeakAssetReference" || typeInfo.Type == "ScriptingObjectReference") + if (typeInfo.Type == "AssetReference" || typeInfo.Type == "WeakAssetReference" || typeInfo.Type == "ScriptingObjectReference" || typeInfo.Type == "SoftObjectReference") return $"Variant({value}.Get())"; if (typeInfo.IsArray) { @@ -149,7 +149,7 @@ namespace Flax.Build.Bindings return $"((StringView){value}).GetText()"; if (typeInfo.Type == "AssetReference" || typeInfo.Type == "WeakAssetReference") return $"ScriptingObject::Cast<{typeInfo.GenericArgs[0].Type}>((Asset*){value})"; - if (typeInfo.Type == "ScriptingObjectReference") + if (typeInfo.Type == "ScriptingObjectReference" || typeInfo.Type == "SoftObjectReference") return $"ScriptingObject::Cast<{typeInfo.GenericArgs[0].Type}>((ScriptingObject*){value})"; if (typeInfo.IsArray) throw new Exception($"Not supported type to convert from the Variant to fixed-size array '{typeInfo}[{typeInfo.ArraySize}]'."); @@ -340,8 +340,8 @@ namespace Flax.Build.Bindings } } - // ScriptingObjectReference or AssetReference or WeakAssetReference - if ((typeInfo.Type == "ScriptingObjectReference" || typeInfo.Type == "AssetReference" || typeInfo.Type == "WeakAssetReference") && typeInfo.GenericArgs != null) + // ScriptingObjectReference or AssetReference or WeakAssetReference or SoftObjectReference + if ((typeInfo.Type == "ScriptingObjectReference" || typeInfo.Type == "AssetReference" || typeInfo.Type == "WeakAssetReference" || typeInfo.Type == "SoftObjectReference") && typeInfo.GenericArgs != null) { type = "MonoObject*"; return "{0}.GetManagedInstance()"; @@ -448,8 +448,8 @@ namespace Flax.Build.Bindings type = "MonoReflectionType*"; return "MUtils::UnboxVariantType({0})"; default: - // ScriptingObjectReference or AssetReference or WeakAssetReference - if ((typeInfo.Type == "ScriptingObjectReference" || typeInfo.Type == "AssetReference" || typeInfo.Type == "WeakAssetReference") && typeInfo.GenericArgs != null) + // ScriptingObjectReference or AssetReference or WeakAssetReference or SoftObjectReference + if ((typeInfo.Type == "ScriptingObjectReference" || typeInfo.Type == "AssetReference" || typeInfo.Type == "WeakAssetReference" || typeInfo.Type == "SoftObjectReference") && typeInfo.GenericArgs != null) { // For non-pod types converting only, other API converts managed to unmanaged object in C# wrapper code) if (CppNonPodTypesConvertingGeneration)