Merge remote-tracking branch 'origin/master' into 1.13

# Conflicts:
#	Flax.flaxproj
#	Source/Engine/Level/Actors/StaticModel.cpp
#	Source/Engine/Level/Prefabs/Prefab.cpp
#	Source/Engine/Tools/ModelTool/ModelTool.cpp
This commit is contained in:
2026-06-03 17:15:38 +02:00
91 changed files with 1290 additions and 579 deletions
@@ -5,6 +5,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Serialization;
namespace FlaxEngine.Json.JsonCustomSerializers
@@ -44,6 +45,13 @@ namespace FlaxEngine.Json.JsonCustomSerializers
((JsonObjectContract)contract).ItemReferenceLoopHandling = ReferenceLoopHandling.Serialize;
}
// Check if use enum serialization as string
var type = Nullable.GetUnderlyingType(objectType) ?? objectType;
if (type.IsEnum && type.GetCustomAttribute<EnumStringAttribute>() != null)
{
contract.Converter = new StringEnumConverter();
}
return contract;
}
@@ -53,19 +61,19 @@ namespace FlaxEngine.Json.JsonCustomSerializers
var contract = base.CreateDictionaryContract(objectType);
// Override contract to save enums keys as integer
if (contract.DictionaryKeyType?.IsEnum ?? false)
var keyType = contract.DictionaryKeyType;
if ((keyType?.IsEnum ?? false) && keyType.GetCustomAttribute<EnumStringAttribute>() == null)
{
var enumType = contract.DictionaryKeyType;
contract.DictionaryKeyResolver = name =>
{
try
{
var e = Enum.Parse(enumType, name);
var e = Enum.Parse(keyType, name);
name = Convert.ToInt32(e).ToString();
}
catch
catch (Exception ex)
{
// Ignore errors
Debug.Logger.LogHandler.LogWrite(LogType.Warning, $"Failed to parse enum '{name}' as {keyType.Name}: {ex.Message}");
}
return name;
};
+54 -2
View File
@@ -49,6 +49,54 @@ void ISerializable::DeserializeIfExists(DeserializeStream& stream, const char* m
var = defaultValue;\
}
void Serialization::SerializeEnum(ISerializable::SerializeStream& stream, uint32 v, ScriptingTypeHandle typeHandle)
{
if (typeHandle)
{
// Check if serialize enum as string
const ScriptingType& type = typeHandle.GetType();
if (type.Type == ScriptingTypes::Enum && type.Enum.StringSerialization)
{
const auto items = type.Enum.Items;
for (int32 i = 0; items[i].Name; i++)
{
if (items[i].Value == v)
{
stream.String(items[i].Name);
return;
}
}
}
}
stream.Uint(v);
}
int32 Serialization::DeserializeEnum(ISerializable::DeserializeStream& stream, ScriptingTypeHandle typeHandle)
{
if (stream.IsString() && typeHandle)
{
// Deserialize enum from string
const ScriptingType& type = typeHandle.GetType();
if (type.Type == ScriptingTypes::Enum)
{
const auto str = stream.GetStringAnsiView();
const auto items = type.Enum.Items;
for (int32 i = 0; items[i].Name; i++)
{
if (str == items[i].Name)
{
return (int32)items[i].Value;
}
}
int32 result;
if (!StringUtils::Parse(stream.GetString(), &result))
return result;
LOG(Warning, "Failed to parse enum '{}' as {}", str.ToString(), type.Fullname.ToString());
}
}
return DeserializeInt(stream);
}
bool Serialization::ShouldSerialize(const VariantType& v, const void* otherObj)
{
return !otherObj || v != *(VariantType*)otherObj;
@@ -129,7 +177,6 @@ void Serialization::Serialize(ISerializable::SerializeStream& stream, const Vari
stream.Int64(v.AsInt64);
break;
case VariantType::Uint64:
case VariantType::Enum:
stream.Uint64(v.AsUint64);
break;
case VariantType::Float:
@@ -222,6 +269,9 @@ void Serialization::Serialize(ISerializable::SerializeStream& stream, const Vari
else
stream.String("", 0);
break;
case VariantType::Enum:
SerializeEnum(stream, (int32)v.AsUint64, v.Type.GetScriptingType());
break;
case VariantType::ManagedObject:
case VariantType::Structure:
{
@@ -276,7 +326,6 @@ void Serialization::Deserialize(ISerializable::DeserializeStream& stream, Varian
v.AsInt64 = value.GetInt64();
break;
case VariantType::Uint64:
case VariantType::Enum:
v.AsUint64 = value.GetUint64();
break;
case VariantType::Float:
@@ -371,6 +420,9 @@ void Serialization::Deserialize(ISerializable::DeserializeStream& stream, Varian
CHECK(value.IsString());
v.SetTypename(value.GetStringAnsiView());
break;
case VariantType::Enum:
v.AsInt64 = DeserializeEnum(value, v.Type.GetScriptingType());
break;
case VariantType::ManagedObject:
case VariantType::Structure:
{
+6 -2
View File
@@ -38,12 +38,16 @@ namespace Serialization
int32 result = 0;
if (stream.IsInt())
result = stream.GetInt();
else if (stream.IsInt64())
result = (int32)stream.GetInt64();
else if (stream.IsFloat())
result = (int32)stream.GetFloat();
else if (stream.IsString())
StringUtils::Parse(stream.GetString(), &result);
return result;
}
FLAXENGINE_API void SerializeEnum(ISerializable::SerializeStream& stream, uint32 v, ScriptingTypeHandle typeHandle);
FLAXENGINE_API int32 DeserializeEnum(ISerializable::DeserializeStream& stream, ScriptingTypeHandle typeHandle);
// In-build types
@@ -226,12 +230,12 @@ namespace Serialization
template<typename T>
inline typename TEnableIf<TIsEnum<T>::Value>::Type Serialize(ISerializable::SerializeStream& stream, const T& v, const void* otherObj)
{
stream.Uint((uint32)v);
SerializeEnum(stream, (uint32)v, StaticType<T>());
}
template<typename T>
inline typename TEnableIf<TIsEnum<T>::Value>::Type Deserialize(ISerializable::DeserializeStream& stream, T& v, ISerializeModifier* modifier)
{
v = (T)DeserializeInt(stream);
v = (T)DeserializeEnum(stream, StaticType<T>());
}
// Common types
+6
View File
@@ -288,12 +288,15 @@ void ReadStream::Read(Variant& data)
break;
}
case VariantType::Float2:
case VariantType::Int2:
ReadBytes(&data.AsData, sizeof(Float2));
break;
case VariantType::Float3:
case VariantType::Int3:
ReadBytes(&data.AsData, sizeof(Float3));
break;
case VariantType::Float4:
case VariantType::Int4:
ReadBytes(&data.AsData, sizeof(Float4));
break;
case VariantType::Double2:
@@ -687,12 +690,15 @@ void WriteStream::Write(const Variant& data)
Write(id);
break;
case VariantType::Float2:
case VariantType::Int2:
WriteBytes(data.AsData, sizeof(Float2));
break;
case VariantType::Float3:
case VariantType::Int3:
WriteBytes(data.AsData, sizeof(Float3));
break;
case VariantType::Float4:
case VariantType::Int4:
WriteBytes(data.AsData, sizeof(Float4));
break;
case VariantType::Double2: