Files
o3de/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialPropertyValueSerializer.cpp
T
santorac a896ff11bc Changed .material serialization to avoid loading the .materialtype file, since the .material builder doesn't declare a source dependency on the .materialtype. Otherwise there can be ambiguous edge cases where changes to the .materialtype might or might not impact the baked MaterialAsset. Note that another option would have been to add a the appropriate source dependency, but that would hurt iteration time as any change to the .materialtype file would cause every .material file and .fbx to rebuild.
These changes have the added benefit of simplifying some of the serialization code. MaterialSourceDataSerializer is no longer needed, as its main purpose was to pass the MaterialTypeSourceData down to the MaterialPropertyValueSerializer.

Before, the JSON serialization system gave a lot of data flexibility because it did best-effort conversions, like allowing a float to be loaded as an int for example. But now the material serialization code doesn't know target data type, so it has to assume the data type based on what's in the .material file, and then the MaterialAsset will convert the data to the appropriate type later when Finalize() is called.

Signed-off-by: santorac <55155825+santorac@users.noreply.github.com>
2022-01-18 19:00:50 -08:00

204 lines
9.5 KiB
C++

/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <Atom/RPI.Edit/Material/MaterialPropertyValueSerializer.h>
#include <Atom/RPI.Edit/Material/MaterialTypeSourceData.h>
#include <Atom/RPI.Edit/Material/MaterialPropertySerializer.h>
#include <AzCore/Math/Color.h>
#include <AzCore/Math/Vector2.h>
#include <AzCore/Math/Vector3.h>
#include <AzCore/Math/Vector4.h>
#include <AzCore/Serialization/Json/BaseJsonSerializer.h>
#include <AzCore/Serialization/Json/JsonSerializationResult.h>
#include <AzCore/Serialization/Json/JsonSerialization.h>
#include <AzCore/Serialization/Json/StackedString.h>
#include <AzCore/Serialization/Json/JsonUtils.h>
namespace AZ
{
namespace RPI
{
AZ_CLASS_ALLOCATOR_IMPL(JsonMaterialPropertyValueSerializer, SystemAllocator, 0);
template<typename T>
JsonSerializationResult::ResultCode JsonMaterialPropertyValueSerializer::LoadVariant(
MaterialPropertyValue& intoValue,
const T& defaultValue,
const rapidjson::Value& inputValue,
JsonDeserializerContext& context)
{
T value = defaultValue;
JsonSerializationResult::ResultCode result = ContinueLoading(&value, azrtti_typeid<T>(), inputValue, context);
intoValue = value;
return result;
}
JsonSerializationResult::Result JsonMaterialPropertyValueSerializer::Load(void* outputValue, const Uuid& outputValueTypeId,
const rapidjson::Value& inputValue, JsonDeserializerContext& context)
{
namespace JSR = JsonSerializationResult;
AZ_Assert(azrtti_typeid<MaterialSourceData::Property>() == outputValueTypeId,
"Unable to deserialize material property value to json because the provided type is %s",
outputValueTypeId.ToString<AZStd::string>().c_str());
AZ_UNUSED(outputValueTypeId);
MaterialSourceData::Property* property = reinterpret_cast<MaterialSourceData::Property*>(outputValue);
AZ_Assert(property, "Output value for JsonMaterialPropertyValueSerializer can't be null.");
// Construct the full property name (groupName.propertyName) by parsing it from the JSON path string.
size_t startPropertyName = context.GetPath().Get().rfind('/');
size_t startGroupName = context.GetPath().Get().rfind('/', startPropertyName-1);
AZStd::string_view groupName = context.GetPath().Get().substr(startGroupName + 1, startPropertyName - startGroupName - 1);
AZStd::string_view propertyName = context.GetPath().Get().substr(startPropertyName + 1);
JSR::ResultCode result(JSR::Tasks::ReadField);
if (inputValue.IsBool())
{
result.Combine(LoadVariant<bool>(property->m_value, false, inputValue, context));
}
else if (inputValue.IsInt() || inputValue.IsInt64())
{
result.Combine(LoadVariant<int32_t>(property->m_value, 0, inputValue, context));
}
else if (inputValue.IsUint() || inputValue.IsUint64())
{
result.Combine(LoadVariant<uint32_t>(property->m_value, 0u, inputValue, context));
}
else if (inputValue.IsFloat() || inputValue.IsDouble())
{
result.Combine(LoadVariant<float>(property->m_value, 0.0f, inputValue, context));
}
else if (inputValue.IsArray() && inputValue.Size() == 4)
{
result.Combine(LoadVariant<Vector4>(property->m_value, Vector4{0.0f, 0.0f, 0.0f, 0.0f}, inputValue, context));
}
else if (inputValue.IsArray() && inputValue.Size() == 3)
{
result.Combine(LoadVariant<Vector3>(property->m_value, Vector3{0.0f, 0.0f, 0.0f}, inputValue, context));
}
else if (inputValue.IsArray() && inputValue.Size() == 2)
{
result.Combine(LoadVariant<Vector2>(property->m_value, Vector2{0.0f, 0.0f}, inputValue, context));
}
else if (inputValue.IsObject())
{
JsonSerializationResult::ResultCode resultCode = LoadVariant<Color>(property->m_value, Color::CreateZero(), inputValue, context);
if(resultCode.GetProcessing() != JsonSerializationResult::Processing::Completed)
{
resultCode = LoadVariant<Vector4>(property->m_value, Vector4::CreateZero(), inputValue, context);
}
if(resultCode.GetProcessing() != JsonSerializationResult::Processing::Completed)
{
resultCode = LoadVariant<Vector3>(property->m_value, Vector3::CreateZero(), inputValue, context);
}
if(resultCode.GetProcessing() != JsonSerializationResult::Processing::Completed)
{
resultCode = LoadVariant<Vector2>(property->m_value, Vector2::CreateZero(), inputValue, context);
}
if(resultCode.GetProcessing() == JsonSerializationResult::Processing::Completed)
{
result.Combine(resultCode);
}
else
{
return context.Report(JsonSerializationResult::Tasks::ReadField, JsonSerializationResult::Outcomes::Unsupported, "Unknown data type");
}
}
else if (inputValue.IsString())
{
result.Combine(LoadVariant<AZStd::string>(property->m_value, AZStd::string{}, inputValue, context));
}
else
{
return context.Report(JsonSerializationResult::Tasks::ReadField, JsonSerializationResult::Outcomes::Unsupported, "Unknown data type");
}
if (result.GetProcessing() == JsonSerializationResult::Processing::Completed)
{
return context.Report(result, "Successfully loaded property value.");
}
else
{
return context.Report(result, "Partially loaded property value.");
}
}
JsonSerializationResult::Result JsonMaterialPropertyValueSerializer::Store(rapidjson::Value& outputValue, const void* inputValue,
[[maybe_unused]] const void* defaultValue, const Uuid& valueTypeId, JsonSerializerContext& context)
{
namespace JSR = JsonSerializationResult;
AZ_Assert(azrtti_typeid<MaterialSourceData::Property>() == valueTypeId,
"Unable to serialize material property value to json because the provided type is %s",
valueTypeId.ToString<AZStd::string>().c_str());
AZ_UNUSED(valueTypeId);
const MaterialSourceData::Property* property = reinterpret_cast<const MaterialSourceData::Property*>(inputValue);
AZ_Assert(property, "Input value for JsonMaterialPropertyValueSerializer can't be null.");
JSR::ResultCode result(JSR::Tasks::WriteValue);
if (property->m_value.Is<bool>())
{
result.Combine(ContinueStoring(outputValue, &property->m_value.GetValue<bool>(), nullptr, azrtti_typeid<bool>(), context));
}
else if (property->m_value.Is<int32_t>())
{
result.Combine(ContinueStoring(outputValue, &property->m_value.GetValue<int32_t>(), nullptr, azrtti_typeid<int32_t>(), context));
}
else if (property->m_value.Is<uint32_t>())
{
result.Combine(ContinueStoring(outputValue, &property->m_value.GetValue<uint32_t>(), nullptr, azrtti_typeid<uint32_t>(), context));
}
else if (property->m_value.Is<float>())
{
result.Combine(ContinueStoring(outputValue, &property->m_value.GetValue<float>(), nullptr, azrtti_typeid<float>(), context));
}
else if (property->m_value.Is<Vector2>())
{
result.Combine(ContinueStoring(outputValue, &property->m_value.GetValue<Vector2>(), nullptr, azrtti_typeid<Vector2>(), context));
}
else if (property->m_value.Is<Vector3>())
{
result.Combine(ContinueStoring(outputValue, &property->m_value.GetValue<Vector3>(), nullptr, azrtti_typeid<Vector3>(), context));
}
else if (property->m_value.Is<Vector4>())
{
result.Combine(ContinueStoring(outputValue, &property->m_value.GetValue<Vector4>(), nullptr, azrtti_typeid<Vector4>(), context));
}
else if (property->m_value.Is<Color>())
{
result.Combine(ContinueStoring(outputValue, &property->m_value.GetValue<Color>(), nullptr, azrtti_typeid<Color>(), context));
}
else if (property->m_value.Is<AZStd::string>())
{
result.Combine(ContinueStoring(outputValue, &property->m_value.GetValue<AZStd::string>(), nullptr, azrtti_typeid<AZStd::string>(), context));
}
if (result.GetProcessing() == JsonSerializationResult::Processing::Completed)
{
return context.Report(result, "Successfully stored property value.");
}
else
{
return context.Report(result, "Partially stored property value.");
}
}
} // namespace RPI
} // namespace AZ