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/Serialization/JsonTools.h"
|
||||
#include "Engine/Content/Factories/JsonAssetFactory.h"
|
||||
#include "Engine/Core/Cache.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)
|
||||
: Asset(params, info)
|
||||
@@ -175,51 +178,54 @@ void JsonAssetBase::onRename(const StringView& newPath)
|
||||
|
||||
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)
|
||||
: JsonAssetBase(params, info)
|
||||
, Instance(nullptr)
|
||||
{
|
||||
if (UnmanagedTypes.IsEmpty())
|
||||
InitUnmanagedJsonTypes();
|
||||
}
|
||||
|
||||
Asset::LoadResult JsonAsset::loadAsset()
|
||||
{
|
||||
// Base
|
||||
auto result = JsonAssetBase::loadAsset();
|
||||
if (result != LoadResult::Ok)
|
||||
if (result != LoadResult::Ok || IsInternalType())
|
||||
return result;
|
||||
|
||||
UnmanagedJsonInstanceCreator instanceSpawner = nullptr;
|
||||
if (UnmanagedTypes.TryGet(DataTypeName, instanceSpawner))
|
||||
// Try to scripting type for this data
|
||||
const StringAsANSI<> dataTypeNameAnsi(DataTypeName.Get(), DataTypeName.Length());
|
||||
const auto typeHandle = Scripting::FindScriptingType(StringAnsiView(dataTypeNameAnsi.Get(), DataTypeName.Length()));
|
||||
if (typeHandle)
|
||||
{
|
||||
Instance = instanceSpawner();
|
||||
Instance->Deserialize(*Data, nullptr);
|
||||
auto& type = typeHandle.GetType();
|
||||
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;
|
||||
@@ -230,5 +236,11 @@ void JsonAsset::unload(bool isReloading)
|
||||
// Base
|
||||
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
|
||||
{
|
||||
DECLARE_ASSET_HEADER(JsonAsset);
|
||||
private:
|
||||
ScriptingType::Dtor _dtor;
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// The deserialized unmanaged object instance (e.g. PhysicalMaterial).
|
||||
/// </summary>
|
||||
ISerializable* Instance;
|
||||
void* Instance;
|
||||
|
||||
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
|
||||
|
||||
#include "JsonFwd.h"
|
||||
#include "Engine/Core/Compiler.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);
|
||||
};
|
||||
// ISerializable moved to Core module
|
||||
#include "Engine/Core/ISerializable.h"
|
||||
|
||||
Reference in New Issue
Block a user