| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487 |
- /*
- * 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/MaterialPropertySerializer.h>
- #include <Atom/RPI.Edit/Material/MaterialPropertyId.h>
- #include <Atom/RPI.Edit/Material/MaterialUtils.h>
- #include <Atom/RPI.Edit/Material/MaterialPropertySourceData.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/Math/Color.h>
- #include <AzCore/Math/Vector2.h>
- #include <AzCore/Math/Vector3.h>
- #include <AzCore/Math/Vector4.h>
- namespace AZ
- {
- namespace RPI
- {
- namespace JsonMaterialPropertySerializerInternal
- {
- namespace Field
- {
- static constexpr const char name[] = "name";
- static constexpr const char id[] = "id"; // For backward compatibility
- static constexpr const char displayName[] = "displayName";
- static constexpr const char description[] = "description";
- static constexpr const char type[] = "type";
- static constexpr const char visibility[] = "visibility";
- static constexpr const char defaultValue[] = "defaultValue";
- static constexpr const char min[] = "min";
- static constexpr const char max[] = "max";
- static constexpr const char softMin[] = "softMin";
- static constexpr const char softMax[] = "softMax";
- static constexpr const char step[] = "step";
- static constexpr const char connection[] = "connection";
- static constexpr const char enumValues[] = "enumValues";
- static constexpr const char enumIsUv[] = "enumIsUv";
- static constexpr const char vectorLabels[] = "vectorLabels";
- }
- static const AZStd::string_view AcceptedFields[] =
- {
- Field::name,
- Field::id,
- Field::displayName,
- Field::description,
- Field::type,
- Field::visibility,
- Field::defaultValue,
- Field::min,
- Field::max,
- Field::softMin,
- Field::softMax,
- Field::step,
- Field::connection,
- Field::enumValues,
- Field::enumIsUv,
- Field::vectorLabels
- };
- }
- AZ_CLASS_ALLOCATOR_IMPL(JsonMaterialPropertySerializer, SystemAllocator);
- template<typename T>
- JsonSerializationResult::ResultCode JsonMaterialPropertySerializer::LoadVariant(
- MaterialPropertyValue& intoValue,
- const rapidjson::Value& inputValue,
- JsonDeserializerContext& context)
- {
- T value;
- JsonSerializationResult::ResultCode result = ContinueLoading(&value, azrtti_typeid<T>(), inputValue, context);
- if (result.GetOutcome() == JsonSerializationResult::Outcomes::Success)
- {
- intoValue = value;
- }
- return result;
- }
- template<typename T>
- JsonSerializationResult::ResultCode JsonMaterialPropertySerializer::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;
- }
- template<typename T>
- JsonSerializationResult::ResultCode JsonMaterialPropertySerializer::LoadNumericValues(
- MaterialPropertySourceData* intoProperty,
- const T& defaultValue,
- const rapidjson::Value& inputValue,
- JsonDeserializerContext& context)
- {
- namespace JSR = JsonSerializationResult;
- using namespace JsonMaterialPropertySerializerInternal;
- JSR::ResultCode result(JSR::Tasks::ReadField);
- if (inputValue.HasMember(Field::defaultValue))
- {
- ScopedContextPath subPath{context, Field::defaultValue};
- result.Combine(LoadVariant<T>(intoProperty->m_value, defaultValue, inputValue[Field::defaultValue], context));
- }
- else
- {
- intoProperty->m_value = defaultValue;
- result.Combine(JSR::ResultCode(JSR::Tasks::ReadField, JSR::Outcomes::PartialDefaults));
- }
- // The following do not report PartialDefault because when these are omitted/null the data in the property will also be null
- if (inputValue.HasMember(Field::min))
- {
- ScopedContextPath subPath{context, Field::min};
- result.Combine(LoadVariant<T>(intoProperty->m_min, inputValue[Field::min], context));
- }
- if (inputValue.HasMember(Field::max))
- {
- ScopedContextPath subPath{context, Field::max};
- result.Combine(LoadVariant<T>(intoProperty->m_max, inputValue[Field::max], context));
- }
- if (inputValue.HasMember(Field::softMin))
- {
- ScopedContextPath subPath{ context, Field::softMin };
- result.Combine(LoadVariant<T>(intoProperty->m_softMin, inputValue[Field::softMin], context));
- }
- if (inputValue.HasMember(Field::softMax))
- {
- ScopedContextPath subPath{ context, Field::softMax };
- result.Combine(LoadVariant<T>(intoProperty->m_softMax, inputValue[Field::softMax], context));
- }
- if (inputValue.HasMember(Field::step))
- {
- ScopedContextPath subPath{context, Field::step};
- result.Combine(LoadVariant<T>(intoProperty->m_step, inputValue[Field::step], context));
- }
- return result;
- }
- template<typename T>
- JsonSerializationResult::ResultCode JsonMaterialPropertySerializer::LoadNonNumericValues(
- MaterialPropertySourceData* intoProperty,
- const T& defaultValue,
- const rapidjson::Value& inputValue,
- JsonDeserializerContext& context)
- {
- namespace JSR = JsonSerializationResult;
- using namespace JsonMaterialPropertySerializerInternal;
- JSR::ResultCode result(JSR::Tasks::ReadField);
- if (inputValue.HasMember(Field::defaultValue))
- {
- ScopedContextPath subPath{context, Field::defaultValue};
- result.Combine(LoadVariant<T>(intoProperty->m_value, defaultValue, inputValue[Field::defaultValue], context));
- }
- else
- {
- intoProperty->m_value = defaultValue;
- result.Combine(JSR::ResultCode(JSR::Tasks::ReadField, JSR::Outcomes::PartialDefaults));
- }
- return result;
- }
- JsonSerializationResult::Result JsonMaterialPropertySerializer::Load(void* outputValue, const Uuid& outputValueTypeId,
- const rapidjson::Value& inputValue, JsonDeserializerContext& context)
- {
- namespace JSR = JsonSerializationResult;
- using namespace JsonMaterialPropertySerializerInternal;
- AZ_Assert(azrtti_typeid<MaterialPropertySourceData>() == outputValueTypeId,
- "Unable to deserialize material property to json because the provided type is %s",
- outputValueTypeId.ToString<AZStd::string>().c_str());
- AZ_UNUSED(outputValueTypeId);
- MaterialPropertySourceData* property = reinterpret_cast<MaterialPropertySourceData*>(outputValue);
- AZ_Assert(property, "Output value for JsonMaterialPropertySerializer can't be null.");
- JSR::ResultCode result(JSR::Tasks::ReadField);
- if (!inputValue.IsObject())
- {
- return context.Report(JsonSerializationResult::Tasks::ReadField, JsonSerializationResult::Outcomes::Unsupported, "Property definition must be a JSON object.");
- }
- MaterialUtils::CheckForUnrecognizedJsonFields(AcceptedFields, AZ_ARRAY_SIZE(AcceptedFields), inputValue, context, result);
- JsonSerializationResult::ResultCode nameResult = ContinueLoadingFromJsonObjectField(&property->m_name, azrtti_typeid<AZStd::string>(), inputValue, Field::name, context);
- if (nameResult.GetOutcome() == JsonSerializationResult::Outcomes::DefaultsUsed)
- {
- // This "id" key is for backward compatibility.
- result.Combine(ContinueLoadingFromJsonObjectField(&property->m_name, azrtti_typeid<AZStd::string>(), inputValue, Field::id, context));
- }
- else
- {
- result.Combine(nameResult);
- }
- result.Combine(ContinueLoadingFromJsonObjectField(&property->m_displayName, azrtti_typeid<AZStd::string>(), inputValue, Field::displayName, context));
- result.Combine(ContinueLoadingFromJsonObjectField(&property->m_description, azrtti_typeid<AZStd::string>(), inputValue, Field::description, context));
- result.Combine(ContinueLoadingFromJsonObjectField(&property->m_dataType, azrtti_typeid<MaterialPropertyDataType>(), inputValue, Field::type, context));
- switch (property->m_dataType)
- {
- case MaterialPropertyDataType::Bool:
- result.Combine(LoadNonNumericValues<bool>(property, false, inputValue, context));
- break;
- case MaterialPropertyDataType::Int:
- result.Combine(LoadNumericValues<int32_t>(property, 0, inputValue, context));
- break;
- case MaterialPropertyDataType::UInt:
- result.Combine(LoadNumericValues<uint32_t>(property, 0u, inputValue, context));
- break;
- case MaterialPropertyDataType::Float:
- result.Combine(LoadNumericValues<float>(property, 0.0f, inputValue, context));
- break;
- case MaterialPropertyDataType::Vector2:
- result.Combine(LoadNonNumericValues<Vector2>(property, Vector2{0.0f, 0.0f}, inputValue, context));
- result.Combine(LoadVectorLabels(property, inputValue, context));
- break;
- case MaterialPropertyDataType::Vector3:
- result.Combine(LoadNonNumericValues<Vector3>(property, Vector3{0.0f, 0.0f, 0.0f}, inputValue, context));
- result.Combine(LoadVectorLabels(property, inputValue, context));
- break;
- case MaterialPropertyDataType::Vector4:
- result.Combine(LoadNonNumericValues<Vector4>(property, Vector4{0.0f, 0.0f, 0.0f, 0.0f}, inputValue, context));
- result.Combine(LoadVectorLabels(property, inputValue, context));
- break;
- case MaterialPropertyDataType::Color:
- result.Combine(LoadNonNumericValues<Color>(property, AZ::Colors::White, inputValue, context));
- break;
- case MaterialPropertyDataType::Image:
- case MaterialPropertyDataType::Enum:
- result.Combine(LoadNonNumericValues<AZStd::string>(property, "", inputValue, context));
- default:
- result.Combine(JSR::ResultCode(JSR::Tasks::ReadField, JSR::Outcomes::Skipped));
- break;
- }
- result.Combine(ContinueLoadingFromJsonObjectField(&property->m_visibility, azrtti_typeid<MaterialPropertyVisibility>(), inputValue, Field::visibility, context));
-
- if (inputValue.HasMember(Field::connection))
- {
- ScopedContextPath subPath{context, Field::connection};
- if (inputValue[Field::connection].IsArray())
- {
- result.Combine(ContinueLoading(&property->m_outputConnections, azrtti_typeid(property->m_outputConnections), inputValue[Field::connection], context));
- }
- else
- {
- property->m_outputConnections.emplace_back();
- result.Combine(ContinueLoading(&property->m_outputConnections.back(), azrtti_typeid(property->m_outputConnections.back()), inputValue[Field::connection], context));
- }
- }
- if (inputValue.HasMember(Field::enumValues))
- {
- result.Combine(ContinueLoading(&property->m_enumValues, azrtti_typeid(property->m_enumValues), inputValue[Field::enumValues], context));
- }
- result.Combine(ContinueLoadingFromJsonObjectField(&property->m_enumIsUv, azrtti_typeid<bool>(), inputValue, Field::enumIsUv, context));
- if (result.GetProcessing() == JsonSerializationResult::Processing::Completed)
- {
- return context.Report(result, "Successfully loaded property definition.");
- }
- else
- {
- return context.Report(result, "Partially loaded property definition.");
- }
- }
-
- template<typename T>
- JsonSerializationResult::ResultCode JsonMaterialPropertySerializer::StoreNumericValues(
- rapidjson::Value& outputValue,
- const MaterialPropertySourceData* property,
- const T& defaultValue,
- JsonSerializerContext& context)
- {
- namespace JSR = JsonSerializationResult;
- using namespace JsonMaterialPropertySerializerInternal;
- JSR::ResultCode result(JSR::Tasks::WriteValue);
- if (property->m_value.Is<T>())
- {
- result.Combine(ContinueStoringToJsonObjectField(outputValue, Field::defaultValue, &property->m_value.GetValue<T>(), &defaultValue, azrtti_typeid<T>(), context));
- }
- if (property->m_min.Is<T>())
- {
- result.Combine(ContinueStoringToJsonObjectField(outputValue, Field::min, &property->m_min.GetValue<T>(), nullptr, azrtti_typeid<T>(), context));
- }
- if (property->m_max.Is<T>())
- {
- result.Combine(ContinueStoringToJsonObjectField(outputValue, Field::max, &property->m_max.GetValue<T>(), nullptr, azrtti_typeid<T>(), context));
- }
- if (property->m_softMin.Is<T>())
- {
- result.Combine(ContinueStoringToJsonObjectField(outputValue, Field::softMin, &property->m_softMin.GetValue<T>(), nullptr, azrtti_typeid<T>(), context));
- }
- if (property->m_softMax.Is<T>())
- {
- result.Combine(ContinueStoringToJsonObjectField(outputValue, Field::softMax, &property->m_softMax.GetValue<T>(), nullptr, azrtti_typeid<T>(), context));
- }
- if (property->m_step.Is<T>())
- {
- result.Combine(ContinueStoringToJsonObjectField(outputValue, Field::step, &property->m_step.GetValue<T>(), nullptr, azrtti_typeid<T>(), context));
- }
- return result;
- }
- template<typename T>
- JsonSerializationResult::ResultCode JsonMaterialPropertySerializer::StoreNonNumericValues(
- rapidjson::Value& outputValue,
- const MaterialPropertySourceData* property,
- const T& defaultValue,
- JsonSerializerContext& context)
- {
- namespace JSR = JsonSerializationResult;
- using namespace JsonMaterialPropertySerializerInternal;
- JsonSerializationResult::ResultCode result(JSR::Tasks::WriteValue);
- if (property->m_value.Is<T>())
- {
- result.Combine(ContinueStoringToJsonObjectField(outputValue, Field::defaultValue, &property->m_value.GetValue<T>(), &defaultValue, azrtti_typeid<T>(), context));
- }
- return result;
- }
- JsonSerializationResult::Result JsonMaterialPropertySerializer::Store(rapidjson::Value& outputValue, const void* inputValue,
- [[maybe_unused]] const void* defaultValue, const Uuid& valueTypeId, JsonSerializerContext& context)
- {
- namespace JSR = JsonSerializationResult;
- using namespace JsonMaterialPropertySerializerInternal;
- AZ_Assert(azrtti_typeid<MaterialPropertySourceData>() == valueTypeId,
- "Unable to serialize material property to json because the provided type is %s",
- valueTypeId.ToString<AZStd::string>().c_str());
- AZ_UNUSED(valueTypeId);
- const MaterialPropertySourceData* property = reinterpret_cast<const MaterialPropertySourceData*>(inputValue);
- AZ_Assert(property, "Input value for JsonMaterialPropertySerializer can't be null.");
-
- JSR::ResultCode result(JSR::Tasks::WriteValue);
- outputValue.SetObject();
- const AZStd::string emptyString;
- result.Combine(ContinueStoringToJsonObjectField(outputValue, Field::name, &property->m_name, &emptyString, azrtti_typeid<AZStd::string>(), context));
- result.Combine(ContinueStoringToJsonObjectField(outputValue, Field::displayName, &property->m_displayName, &emptyString, azrtti_typeid<AZStd::string>(), context));
- result.Combine(ContinueStoringToJsonObjectField(outputValue, Field::description, &property->m_description, &emptyString, azrtti_typeid<AZStd::string>(), context));
- MaterialPropertyDataType defaultDataType = MaterialPropertyDataType::Invalid;
- result.Combine(ContinueStoringToJsonObjectField(outputValue, Field::type, &property->m_dataType, &defaultDataType, azrtti_typeid(property->m_dataType), context));
- result.Combine(StoreVectorLabels(outputValue, property, context));
- switch (property->m_dataType)
- {
- case MaterialPropertyDataType::Bool:
- result.Combine(StoreNonNumericValues<bool>(outputValue, property, false, context));
- break;
- case MaterialPropertyDataType::Int:
- result.Combine(StoreNumericValues<int32_t>(outputValue, property, 0, context));
- break;
- case MaterialPropertyDataType::UInt:
- result.Combine(StoreNumericValues<uint32_t>(outputValue, property, 0u, context));
- break;
- case MaterialPropertyDataType::Float:
- result.Combine(StoreNumericValues<float>(outputValue, property, 0.0f, context));
- break;
- case MaterialPropertyDataType::Vector2:
- result.Combine(StoreNonNumericValues<Vector2>(outputValue, property, Vector2{0.0f, 0.0f}, context));
- break;
- case MaterialPropertyDataType::Vector3:
- result.Combine(StoreNonNumericValues<Vector3>(outputValue, property, Vector3{0.0f, 0.0f, 0.0f}, context));
- break;
- case MaterialPropertyDataType::Vector4:
- result.Combine(StoreNonNumericValues<Vector4>(outputValue, property, Vector4{0.0f, 0.0f, 0.0f, 0.0f}, context));
- break;
- case MaterialPropertyDataType::Color:
- result.Combine(StoreNonNumericValues<Color>(outputValue, property, AZ::Colors::White, context));
- break;
- case MaterialPropertyDataType::Image:
- case MaterialPropertyDataType::Enum:
- result.Combine(StoreNonNumericValues<AZStd::string>(outputValue, property, AZStd::string{""}, context));
- break;
- }
- const MaterialPropertyVisibility defaultVisibility = MaterialPropertyVisibility::Default;
- result.Combine(ContinueStoringToJsonObjectField(outputValue, Field::visibility, &property->m_visibility, &defaultVisibility, azrtti_typeid(property->m_visibility), context));
- // Support loading a "connection" property as a single entry in m_outputConnections
- MaterialPropertySourceData::Connection defaultConnection;
- if (property->m_outputConnections.size() == 1)
- {
- result.Combine(ContinueStoringToJsonObjectField(outputValue, Field::connection, &property->m_outputConnections.back(), &defaultConnection, azrtti_typeid(property->m_outputConnections.back()), context));
- }
- else
- {
- result.Combine(ContinueStoringToJsonObjectField(outputValue, Field::connection, &property->m_outputConnections, &defaultConnection, azrtti_typeid(property->m_outputConnections), context));
- }
- // Enum list
- if (property->m_enumValues.size() > 0)
- {
- result.Combine(ContinueStoringToJsonObjectField(outputValue, Field::enumValues, &property->m_enumValues, nullptr, azrtti_typeid(property->m_enumValues), context));
- }
- const bool defaultEnumIsUv = false;
- result.Combine(ContinueStoringToJsonObjectField(outputValue, Field::enumIsUv, &property->m_enumIsUv, &defaultEnumIsUv, azrtti_typeid(property->m_enumIsUv), context));
- if (result.GetProcessing() == JsonSerializationResult::Processing::Completed)
- {
- return context.Report(result, "Successfully stored property definition.");
- }
- else
- {
- return context.Report(result, "Partially stored property definition.");
- }
- }
- JsonSerializationResult::ResultCode JsonMaterialPropertySerializer::LoadVectorLabels(MaterialPropertySourceData* intoProperty,
- const rapidjson::Value& inputValue, JsonDeserializerContext& context)
- {
- namespace JSR = JsonSerializationResult;
- using namespace JsonMaterialPropertySerializerInternal;
- JSR::ResultCode result(JSR::Tasks::ReadField);
- if (inputValue.HasMember(Field::vectorLabels))
- {
- result.Combine(ContinueLoading(&intoProperty->m_vectorLabels, azrtti_typeid(intoProperty->m_vectorLabels), inputValue[Field::vectorLabels], context));
- }
- return result;
- }
- JsonSerializationResult::ResultCode JsonMaterialPropertySerializer::StoreVectorLabels(rapidjson::Value& outputValue,
- const MaterialPropertySourceData* property, JsonSerializerContext& context)
- {
- AZStd::string emptyString;
- namespace JSR = JsonSerializationResult;
- using namespace JsonMaterialPropertySerializerInternal;
- JsonSerializationResult::ResultCode result(JSR::Tasks::WriteValue);
- if (!property->m_vectorLabels.empty())
- {
- result.Combine(ContinueStoringToJsonObjectField(outputValue, Field::vectorLabels, &property->m_vectorLabels, nullptr, azrtti_typeid(property->m_vectorLabels), context));
- }
- return result;
- }
- } // namespace RPI
- } // namespace AZ
|