Add support for loading JsonAsset instance objects if they implement ISerializable interface
This commit is contained in:
@@ -10,7 +10,10 @@
|
|||||||
#include "Engine/Core/Log.h"
|
#include "Engine/Core/Log.h"
|
||||||
#include "Engine/Serialization/JsonTools.h"
|
#include "Engine/Serialization/JsonTools.h"
|
||||||
#include "Engine/Content/Factories/JsonAssetFactory.h"
|
#include "Engine/Content/Factories/JsonAssetFactory.h"
|
||||||
|
#include "Engine/Core/Cache.h"
|
||||||
#include "Engine/Debug/Exceptions/JsonParseException.h"
|
#include "Engine/Debug/Exceptions/JsonParseException.h"
|
||||||
|
#include "Engine/Scripting/Scripting.h"
|
||||||
|
#include "Engine/Utilities/StringConverter.h"
|
||||||
|
|
||||||
JsonAssetBase::JsonAssetBase(const SpawnParams& params, const AssetInfo* info)
|
JsonAssetBase::JsonAssetBase(const SpawnParams& params, const AssetInfo* info)
|
||||||
: Asset(params, info)
|
: Asset(params, info)
|
||||||
@@ -175,51 +178,54 @@ void JsonAssetBase::onRename(const StringView& newPath)
|
|||||||
|
|
||||||
REGISTER_JSON_ASSET(JsonAsset, "FlaxEngine.JsonAsset");
|
REGISTER_JSON_ASSET(JsonAsset, "FlaxEngine.JsonAsset");
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
#include "Engine/Physics/PhysicalMaterial.h"
|
|
||||||
|
|
||||||
// Unmanaged json asset types that are serialized to JsonAsset and should be created by auto by asset.
|
|
||||||
// This allows to reuse JsonAsset without creating dedicated asset types. It has been designed for lightweight resources.
|
|
||||||
|
|
||||||
typedef ISerializable* (*UnmanagedJsonInstanceCreator)();
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
ISerializable* Create()
|
|
||||||
{
|
|
||||||
return New<T>();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Key: managed class typename, Value: unmanaged instance spawner function
|
|
||||||
Dictionary<String, UnmanagedJsonInstanceCreator> UnmanagedTypes(32);
|
|
||||||
|
|
||||||
void InitUnmanagedJsonTypes()
|
|
||||||
{
|
|
||||||
UnmanagedTypes[TEXT("FlaxEngine.PhysicalMaterial")] = &Create<PhysicalMaterial>;
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
JsonAsset::JsonAsset(const SpawnParams& params, const AssetInfo* info)
|
JsonAsset::JsonAsset(const SpawnParams& params, const AssetInfo* info)
|
||||||
: JsonAssetBase(params, info)
|
: JsonAssetBase(params, info)
|
||||||
, Instance(nullptr)
|
, Instance(nullptr)
|
||||||
{
|
{
|
||||||
if (UnmanagedTypes.IsEmpty())
|
|
||||||
InitUnmanagedJsonTypes();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Asset::LoadResult JsonAsset::loadAsset()
|
Asset::LoadResult JsonAsset::loadAsset()
|
||||||
{
|
{
|
||||||
// Base
|
// Base
|
||||||
auto result = JsonAssetBase::loadAsset();
|
auto result = JsonAssetBase::loadAsset();
|
||||||
if (result != LoadResult::Ok)
|
if (result != LoadResult::Ok || IsInternalType())
|
||||||
return result;
|
return result;
|
||||||
|
|
||||||
UnmanagedJsonInstanceCreator instanceSpawner = nullptr;
|
// Try to scripting type for this data
|
||||||
if (UnmanagedTypes.TryGet(DataTypeName, instanceSpawner))
|
const StringAsANSI<> dataTypeNameAnsi(DataTypeName.Get(), DataTypeName.Length());
|
||||||
|
const auto typeHandle = Scripting::FindScriptingType(StringAnsiView(dataTypeNameAnsi.Get(), DataTypeName.Length()));
|
||||||
|
if (typeHandle)
|
||||||
{
|
{
|
||||||
Instance = instanceSpawner();
|
auto& type = typeHandle.GetType();
|
||||||
Instance->Deserialize(*Data, nullptr);
|
switch (type.Type)
|
||||||
|
{
|
||||||
|
case ScriptingTypes::Class:
|
||||||
|
{
|
||||||
|
// Ensure that object can deserialized
|
||||||
|
const ScriptingType::InterfaceImplementation* interfaces = type.GetInterface(&ISerializable::TypeInitializer);
|
||||||
|
if (!interfaces)
|
||||||
|
{
|
||||||
|
LOG(Warning, "Cannot deserialize {0} from Json Asset because it doesn't implement ISerializable interface.", type.ToString());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allocate object
|
||||||
|
const auto instance = Allocator::Allocate(type.Size);
|
||||||
|
if (!instance)
|
||||||
|
return LoadResult::Failed;
|
||||||
|
Instance = instance;
|
||||||
|
_dtor = type.Class.Dtor;
|
||||||
|
type.Class.Ctor(instance);
|
||||||
|
|
||||||
|
// Deserialize object
|
||||||
|
auto modifier = Cache::ISerializeModifier.Get();
|
||||||
|
modifier->EngineBuild = DataEngineBuild;
|
||||||
|
((ISerializable*)((byte*)instance + interfaces->VTableOffset))->Deserialize(*Data, modifier.Value);
|
||||||
|
// TODO: delete object when containing BinaryModule gets unloaded
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: ;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
@@ -230,5 +236,11 @@ void JsonAsset::unload(bool isReloading)
|
|||||||
// Base
|
// Base
|
||||||
JsonAssetBase::unload(isReloading);
|
JsonAssetBase::unload(isReloading);
|
||||||
|
|
||||||
SAFE_DELETE(Instance);
|
if (Instance)
|
||||||
|
{
|
||||||
|
_dtor(Instance);
|
||||||
|
Allocator::Free(Instance);
|
||||||
|
Instance = nullptr;
|
||||||
|
_dtor = nullptr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -78,13 +78,15 @@ protected:
|
|||||||
API_CLASS(NoSpawn) class JsonAsset : public JsonAssetBase
|
API_CLASS(NoSpawn) class JsonAsset : public JsonAssetBase
|
||||||
{
|
{
|
||||||
DECLARE_ASSET_HEADER(JsonAsset);
|
DECLARE_ASSET_HEADER(JsonAsset);
|
||||||
|
private:
|
||||||
|
ScriptingType::Dtor _dtor;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The deserialized unmanaged object instance (e.g. PhysicalMaterial).
|
/// The deserialized unmanaged object instance (e.g. PhysicalMaterial).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
ISerializable* Instance;
|
void* Instance;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
|
|||||||
60
Source/Engine/Core/ISerializable.h
Normal file
60
Source/Engine/Core/ISerializable.h
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Engine/Serialization/JsonFwd.h"
|
||||||
|
#include "Engine/Core/Compiler.h"
|
||||||
|
#include "Engine/Core/Config.h"
|
||||||
|
|
||||||
|
class JsonWriter;
|
||||||
|
class ISerializeModifier;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Interface for objects that can be serialized/deserialized to/from JSON format.
|
||||||
|
/// </summary>
|
||||||
|
API_INTERFACE() class FLAXENGINE_API ISerializable
|
||||||
|
{
|
||||||
|
DECLARE_SCRIPTING_TYPE_MINIMAL(ISerializable);
|
||||||
|
public:
|
||||||
|
|
||||||
|
typedef rapidjson_flax::Document SerializeDocument;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Serialization output stream
|
||||||
|
/// </summary>
|
||||||
|
typedef rapidjson_flax::Value DeserializeStream;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Serialization input stream
|
||||||
|
/// </summary>
|
||||||
|
typedef JsonWriter SerializeStream;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Finalizes an instance of the <see cref="ISerializable"/> class.
|
||||||
|
/// </summary>
|
||||||
|
virtual ~ISerializable() = default;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Serialize object to the output stream compared to the values of the other object instance (eg. default class object). If other object is null then serialize all properties.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="stream">The output stream.</param>
|
||||||
|
/// <param name="otherObj">The instance of the object to compare with and serialize only the modified properties. If null, then serialize all properties.</param>
|
||||||
|
virtual void Serialize(SerializeStream& stream, const void* otherObj) = 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Deserialize object from the input stream
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="stream">The input stream.</param>
|
||||||
|
/// <param name="modifier">The deserialization modifier object. Always valid.</param>
|
||||||
|
virtual void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) = 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Deserialize object from the input stream child member. Won't deserialize it if member is missing.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="stream">The input stream.</param>
|
||||||
|
/// <param name="memberName">The input stream member to lookup.</param>
|
||||||
|
/// <param name="modifier">The deserialization modifier object. Always valid.</param>
|
||||||
|
void DeserializeIfExists(DeserializeStream& stream, const char* memberName, ISerializeModifier* modifier);
|
||||||
|
};
|
||||||
@@ -2,57 +2,5 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "JsonFwd.h"
|
// ISerializable moved to Core module
|
||||||
#include "Engine/Core/Compiler.h"
|
#include "Engine/Core/ISerializable.h"
|
||||||
|
|
||||||
class JsonWriter;
|
|
||||||
class ISerializeModifier;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Interface for objects that can be serialized/deserialized to/from JSON format.
|
|
||||||
/// </summary>
|
|
||||||
class FLAXENGINE_API ISerializable
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
typedef rapidjson_flax::Document SerializeDocument;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Serialization output stream
|
|
||||||
/// </summary>
|
|
||||||
typedef rapidjson_flax::Value DeserializeStream;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Serialization input stream
|
|
||||||
/// </summary>
|
|
||||||
typedef JsonWriter SerializeStream;
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Finalizes an instance of the <see cref="ISerializable"/> class.
|
|
||||||
/// </summary>
|
|
||||||
virtual ~ISerializable() = default;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Serialize object to the output stream compared to the values of the other object instance (eg. default class object). If other object is null then serialize all properties.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="stream">The output stream.</param>
|
|
||||||
/// <param name="otherObj">The instance of the object to compare with and serialize only the modified properties. If null, then serialize all properties.</param>
|
|
||||||
virtual void Serialize(SerializeStream& stream, const void* otherObj) = 0;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Deserialize object from the input stream
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="stream">The input stream.</param>
|
|
||||||
/// <param name="modifier">The deserialization modifier object. Always valid.</param>
|
|
||||||
virtual void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) = 0;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Deserialize object from the input stream child member. Won't deserialize it if member is missing.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="stream">The input stream.</param>
|
|
||||||
/// <param name="memberName">The input stream member to lookup.</param>
|
|
||||||
/// <param name="modifier">The deserialization modifier object. Always valid.</param>
|
|
||||||
void DeserializeIfExists(DeserializeStream& stream, const char* memberName, ISerializeModifier* modifier);
|
|
||||||
};
|
|
||||||
|
|||||||
Reference in New Issue
Block a user