Merge branch 'main' into mp_editor_pipeline

This commit is contained in:
puvvadar
2021-05-07 14:14:17 -07:00
162 changed files with 3179 additions and 1268 deletions
@@ -0,0 +1,485 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <AzCore/Math/MathMatrixSerializer.h>
#include <AzCore/Math/Matrix3x3.h>
#include <AzCore/Math/Matrix3x4.h>
#include <AzCore/Math/Matrix4x4.h>
#include <AzCore/Serialization/Json/JsonSerialization.h>
#include <AzCore/Serialization/Json/RegistrationContext.h>
#include <AzCore/Serialization/Json/StackedString.h>
#include <AzCore/std/algorithm.h>
#include <AzCore/std/string/osstring.h>
#include <AzCore/Casting/numeric_cast.h>
namespace AZ::JsonMathMatrixSerializerInternal
{
template<typename MatrixType, size_t RowCount, size_t ColumnCount>
JsonSerializationResult::Result LoadArray(MatrixType& output, const rapidjson::Value& inputValue, JsonDeserializerContext& context)
{
namespace JSR = JsonSerializationResult; // Used remove name conflicts in AzCore in uber builds.
constexpr size_t ElementCount = RowCount * ColumnCount;
static_assert(ElementCount == 9 || ElementCount == 12 || ElementCount == 16,
"MathMatrixSerializer only support Matrix3x3, Matrix3x4 and Matrix4x4.");
rapidjson::SizeType arraySize = inputValue.Size();
if (arraySize < ElementCount)
{
return context.Report(JSR::Tasks::ReadField, JSR::Outcomes::Unsupported,
"Not enough numbers in JSON array to load math matrix from.");
}
AZ::BaseJsonSerializer* floatSerializer = context.GetRegistrationContext()->GetSerializerForType(azrtti_typeid<float>());
if (!floatSerializer)
{
return context.Report(JSR::Tasks::ReadField, JSR::Outcomes::Catastrophic, "Failed to find the JSON float serializer.");
}
constexpr const char* names[] = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15"};
float values[ElementCount];
for (int i = 0; i < ElementCount; ++i)
{
ScopedContextPath subPath(context, names[i]);
JSR::Result intermediate = floatSerializer->Load(values + i, azrtti_typeid<float>(), inputValue[i], context);
if (intermediate.GetResultCode().GetProcessing() != JSR::Processing::Completed)
{
return intermediate;
}
}
size_t valueIndex = 0;
for (size_t r = 0; r < RowCount; ++r)
{
for (size_t c = 0; c < ColumnCount; ++c)
{
output.SetElement(aznumeric_caster(r), aznumeric_caster(c), values[valueIndex++]);
}
}
return context.Report(JSR::Tasks::ReadField, JSR::Outcomes::Success, "Successfully read math matrix.");
}
JsonSerializationResult::Result LoadFloatFromObject(
float& output,
const rapidjson::Value& inputValue,
JsonDeserializerContext& context,
const char* name,
const char* altName)
{
namespace JSR = JsonSerializationResult; // Used remove name conflicts in AzCore in uber builds.
AZ::BaseJsonSerializer* floatSerializer = context.GetRegistrationContext()->GetSerializerForType(azrtti_typeid<float>());
if (!floatSerializer)
{
return context.Report(JSR::Tasks::ReadField, JSR::Outcomes::Catastrophic, "Failed to find the json float serializer.");
}
const char* nameUsed = name;
JSR::ResultCode result(JSR::Tasks::ReadField);
auto iterator = inputValue.FindMember(rapidjson::StringRef(name));
if (iterator == inputValue.MemberEnd())
{
nameUsed = altName;
iterator = inputValue.FindMember(rapidjson::StringRef(altName));
if (iterator == inputValue.MemberEnd())
{
// field not found so leave default value
result.Combine(JSR::ResultCode(JSR::Tasks::ReadField, JSR::Outcomes::DefaultsUsed));
nameUsed = nullptr;
}
}
if (nameUsed)
{
ScopedContextPath subPath(context, nameUsed);
JSR::Result intermediate = floatSerializer->Load(&output, azrtti_typeid<float>(), iterator->value, context);
if (intermediate.GetResultCode().GetProcessing() != JSR::Processing::Completed)
{
return intermediate;
}
else
{
result.Combine(JSR::ResultCode(JSR::Tasks::ReadField, JSR::Outcomes::Success));
}
}
return context.Report(result, "Successfully read float.");
}
JsonSerializationResult::Result LoadVector3FromObject(
Vector3& output,
const rapidjson::Value& inputValue,
JsonDeserializerContext& context,
AZStd::fixed_vector<AZStd::string_view, 6> names)
{
namespace JSR = JsonSerializationResult; // Used remove name conflicts in AzCore in uber builds.
constexpr size_t ElementCount = 3; // Vector3
JSR::ResultCode result(JSR::Tasks::ReadField);
float values[ElementCount];
for (int i = 0; i < ElementCount; ++i)
{
values[i] = output.GetElement(i);
auto name = names[i * 2];
auto altName = names[(i * 2) + 1];
JSR::Result intermediate = LoadFloatFromObject(values[i], inputValue, context, name.data(), altName.data());
if (intermediate.GetResultCode().GetProcessing() != JSR::Processing::Completed)
{
return intermediate;
}
else
{
result.Combine(JSR::ResultCode(JSR::Tasks::ReadField, JSR::Outcomes::Success));
}
}
for (int i = 0; i < ElementCount; ++i)
{
output.SetElement(i, values[i]);
}
return context.Report(result, "Successfully read math matrix.");
}
JsonSerializationResult::Result LoadQuaternionAndScale(
AZ::Quaternion& quaternion,
float& scale,
const rapidjson::Value& inputValue,
JsonDeserializerContext& context)
{
namespace JSR = JsonSerializationResult; // Used remove name conflicts in AzCore in uber builds.
JSR::ResultCode result(JSR::Tasks::ReadField);
scale = 1.0f;
JSR::Result intermediateScale = LoadFloatFromObject(scale, inputValue, context, "scale", "Scale");
if (intermediateScale.GetResultCode().GetProcessing() != JSR::Processing::Completed)
{
return intermediateScale;
}
result.Combine(intermediateScale);
if (AZ::IsClose(scale, 0.0f))
{
result.Combine({ JSR::Tasks::ReadField, JSR::Outcomes::Unsupported });
return context.Report(result, "Scale can not be zero.");
}
AZ::Vector3 degreesRollPitchYaw = AZ::Vector3::CreateZero();
JSR::Result intermediateDegrees = LoadVector3FromObject(degreesRollPitchYaw, inputValue, context, { "roll", "Roll", "pitch", "Pitch", "yaw", "Yaw" });
if (intermediateDegrees.GetResultCode().GetProcessing() != JSR::Processing::Completed)
{
return intermediateDegrees;
}
result.Combine(intermediateDegrees);
// the quaternion should be equivalent to a series of rotations in the order z, then y, then x
const AZ::Vector3 eulerRadians = AZ::Vector3DegToRad(degreesRollPitchYaw);
quaternion = AZ::Quaternion::CreateRotationX(eulerRadians.GetX()) *
AZ::Quaternion::CreateRotationY(eulerRadians.GetY()) *
AZ::Quaternion::CreateRotationZ(eulerRadians.GetZ());
return context.Report(result, "Successfully read math yaw, pitch, roll, and scale.");
}
template<typename MatrixType>
JsonSerializationResult::Result LoadObject(MatrixType& output, const rapidjson::Value& inputValue, JsonDeserializerContext& context)
{
namespace JSR = JsonSerializationResult; // Used remove name conflicts in AzCore in uber builds.
output = MatrixType::CreateIdentity();
JSR::ResultCode result(JSR::Tasks::ReadField);
float scale;
AZ::Quaternion rotation;
JSR::Result intermediate = LoadQuaternionAndScale(rotation, scale, inputValue, context);
if (intermediate.GetResultCode().GetProcessing() != JSR::Processing::Completed)
{
return intermediate;
}
result.Combine(intermediate);
AZ::Vector3 translation = AZ::Vector3::CreateZero();
JSR::Result intermediateTranslation = LoadVector3FromObject(translation, inputValue, context, { "x", "X", "y", "Y", "z", "Z" });
if (intermediateTranslation.GetResultCode().GetProcessing() != JSR::Processing::Completed)
{
return intermediateTranslation;
}
result.Combine(intermediateTranslation);
// composed a matrix by rotation, then scale, then translation
auto matrix = MatrixType::CreateFromQuaternion(rotation);
matrix.MultiplyByScale(Vector3{ scale });
matrix.SetTranslation(translation);
if (matrix == MatrixType::CreateIdentity())
{
return context.Report(JSR::Tasks::ReadField, JSR::Outcomes::DefaultsUsed, "Using identity matrix for empty object.");
}
output = matrix;
return context.Report(result, "Successfully read math matrix.");
}
template<>
JsonSerializationResult::Result LoadObject<Matrix3x3>(Matrix3x3& output, const rapidjson::Value& inputValue, JsonDeserializerContext& context)
{
namespace JSR = JsonSerializationResult; // Used remove name conflicts in AzCore in uber builds.
output = Matrix3x3::CreateIdentity();
JSR::ResultCode result(JSR::Tasks::ReadField);
float scale;
AZ::Quaternion rotation;
JSR::Result intermediate = LoadQuaternionAndScale(rotation, scale, inputValue, context);
if (intermediate.GetResultCode().GetProcessing() != JSR::Processing::Completed)
{
return intermediate;
}
result.Combine(intermediate);
// composed a matrix by rotation then scale
auto matrix = Matrix3x3::CreateFromQuaternion(rotation);
matrix.MultiplyByScale(Vector3{ scale });
if (matrix == Matrix3x3::CreateIdentity())
{
return context.Report(JSR::Tasks::ReadField, JSR::Outcomes::DefaultsUsed, "Using identity matrix for empty object.");
}
output = matrix;
return context.Report(result, "Successfully read math matrix.");
}
template<typename MatrixType, size_t RowCount, size_t ColumnCount>
JsonSerializationResult::Result Load(void* outputValue, const Uuid& outputValueTypeId,
const rapidjson::Value& inputValue, JsonDeserializerContext& context)
{
namespace JSR = JsonSerializationResult; // Used remove name conflicts in AzCore in uber builds.
constexpr size_t ElementCount = RowCount * ColumnCount;
static_assert(ElementCount == 9 || ElementCount == 12 || ElementCount == 16,
"MathMatrixSerializer only support Matrix3x3, Matrix3x4 and Matrix4x4.");
AZ_Assert(azrtti_typeid<MatrixType>() == outputValueTypeId,
"Unable to deserialize Matrix%zux%zu to json because the provided type is %s",
RowCount, ColumnCount, outputValueTypeId.ToString<OSString>().c_str());
AZ_UNUSED(outputValueTypeId);
MatrixType* matrix = reinterpret_cast<MatrixType*>(outputValue);
AZ_Assert(matrix, "Output value for JsonMatrix%zux%zuSerializer can't be null.", RowCount, ColumnCount);
switch (inputValue.GetType())
{
case rapidjson::kArrayType:
return LoadArray<MatrixType, RowCount, ColumnCount>(*matrix, inputValue, context);
case rapidjson::kObjectType:
return LoadObject<MatrixType>(*matrix, inputValue, context);
case rapidjson::kStringType:
[[fallthrough]];
case rapidjson::kNumberType:
[[fallthrough]];
case rapidjson::kNullType:
[[fallthrough]];
case rapidjson::kFalseType:
[[fallthrough]];
case rapidjson::kTrueType:
return context.Report(JSR::Tasks::ReadField, JSR::Outcomes::Unsupported,
"Unsupported type. Math matrix can only be read from arrays or objects.");
default:
return context.Report(JSR::Tasks::ReadField, JSR::Outcomes::Unknown,
"Unknown json type encountered in math matrix.");
}
}
template<typename MatrixType>
AZ::Quaternion CreateQuaternion(const MatrixType& matrix);
template<>
AZ::Quaternion CreateQuaternion<AZ::Matrix3x3>(const AZ::Matrix3x3& matrix)
{
return Quaternion::CreateFromMatrix3x3(matrix);
}
template<>
AZ::Quaternion CreateQuaternion<AZ::Matrix3x4>(const AZ::Matrix3x4& matrix)
{
return Quaternion::CreateFromMatrix3x4(matrix);
}
template<>
AZ::Quaternion CreateQuaternion<AZ::Matrix4x4>(const AZ::Matrix4x4& matrix)
{
return Quaternion::CreateFromMatrix4x4(matrix);
}
template<typename MatrixType>
JsonSerializationResult::Result StoreRotationAndScale(rapidjson::Value& outputValue, const void* inputValue, const void* defaultValue,
const Uuid& valueTypeId, JsonSerializerContext& context)
{
namespace JSR = JsonSerializationResult; // Used remove name conflicts in AzCore in uber builds.
AZ_UNUSED(valueTypeId);
const MatrixType* matrix = reinterpret_cast<const MatrixType*>(inputValue);
AZ_Assert(matrix, "Input value for JsonMatrixSerializer can't be null.");
const MatrixType* defaultMatrix = reinterpret_cast<const MatrixType*>(defaultValue);
if (!context.ShouldKeepDefaults() && defaultMatrix && *matrix == *defaultMatrix)
{
return context.Report(JSR::Tasks::WriteValue, JSR::Outcomes::DefaultsUsed, "Default math Matrix used.");
}
MatrixType matrixToExport = *matrix;
AZ::Vector3 scale = matrixToExport.ExtractScale();
AZ::Quaternion rotation = CreateQuaternion(matrixToExport);
auto degrees = rotation.GetEulerDegrees();
outputValue.AddMember(rapidjson::StringRef("roll"), degrees.GetX(), context.GetJsonAllocator());
outputValue.AddMember(rapidjson::StringRef("pitch"), degrees.GetY(), context.GetJsonAllocator());
outputValue.AddMember(rapidjson::StringRef("yaw"), degrees.GetZ(), context.GetJsonAllocator());
outputValue.AddMember(rapidjson::StringRef("scale"), scale.GetX(), context.GetJsonAllocator());
return context.Report(JSR::Tasks::WriteValue, JSR::Outcomes::Success, "Math Matrix successfully stored.");
}
template<typename MatrixType>
JsonSerializationResult::Result StoreTranslation(rapidjson::Value& outputValue, const void* inputValue,
const void* defaultValue, const Uuid& valueTypeId, JsonSerializerContext& context)
{
namespace JSR = JsonSerializationResult; // Used remove name conflicts in AzCore in uber builds.
AZ_UNUSED(valueTypeId);
const MatrixType* matrix = reinterpret_cast<const MatrixType*>(inputValue);
AZ_Assert(matrix, "Input value for JsonMatrixSerializer can't be null.");
const MatrixType* defaultMatrix = reinterpret_cast<const MatrixType*>(defaultValue);
if (!context.ShouldKeepDefaults() && defaultMatrix && *matrix == *defaultMatrix)
{
return context.Report(JSR::Tasks::WriteValue, JSR::Outcomes::DefaultsUsed, "Default math Matrix used.");
}
auto translation = matrix->GetTranslation();
outputValue.AddMember(rapidjson::StringRef("x"), translation.GetX(), context.GetJsonAllocator());
outputValue.AddMember(rapidjson::StringRef("y"), translation.GetY(), context.GetJsonAllocator());
outputValue.AddMember(rapidjson::StringRef("z"), translation.GetZ(), context.GetJsonAllocator());
return context.Report(JSR::Tasks::WriteValue, JSR::Outcomes::Success, "Math Matrix successfully stored.");
}
}
namespace AZ
{
// Matrix3x3
AZ_CLASS_ALLOCATOR_IMPL(JsonMatrix3x3Serializer, SystemAllocator, 0);
JsonSerializationResult::Result JsonMatrix3x3Serializer::Load(void* outputValue, const Uuid& outputValueTypeId,
const rapidjson::Value& inputValue, JsonDeserializerContext& context)
{
return JsonMathMatrixSerializerInternal::Load<Matrix3x3, 3, 3>(
outputValue,
outputValueTypeId,
inputValue,
context);
}
JsonSerializationResult::Result JsonMatrix3x3Serializer::Store(rapidjson::Value& outputValue, const void* inputValue,
const void* defaultValue, const Uuid& valueTypeId, JsonSerializerContext& context)
{
outputValue.SetObject();
return JsonMathMatrixSerializerInternal::StoreRotationAndScale<Matrix3x3>(
outputValue,
inputValue,
defaultValue,
valueTypeId,
context);
}
// Matrix3x4
AZ_CLASS_ALLOCATOR_IMPL(JsonMatrix3x4Serializer, SystemAllocator, 0);
JsonSerializationResult::Result JsonMatrix3x4Serializer::Load(void* outputValue, const Uuid& outputValueTypeId,
const rapidjson::Value& inputValue, JsonDeserializerContext& context)
{
return JsonMathMatrixSerializerInternal::Load<Matrix3x4, 3, 4>(
outputValue,
outputValueTypeId,
inputValue,
context);
}
JsonSerializationResult::Result JsonMatrix3x4Serializer::Store(rapidjson::Value& outputValue, const void* inputValue,
const void* defaultValue, const Uuid& valueTypeId, JsonSerializerContext& context)
{
outputValue.SetObject();
auto result = JsonMathMatrixSerializerInternal::StoreRotationAndScale<Matrix3x4>(
outputValue,
inputValue,
defaultValue,
valueTypeId,
context);
auto resultTranslation = JsonMathMatrixSerializerInternal::StoreTranslation<Matrix3x4>(
outputValue,
inputValue,
defaultValue,
valueTypeId,
context);
result.GetResultCode().Combine(resultTranslation);
return result;
}
// Matrix4x4
AZ_CLASS_ALLOCATOR_IMPL(JsonMatrix4x4Serializer, SystemAllocator, 0);
JsonSerializationResult::Result JsonMatrix4x4Serializer::Load(void* outputValue, const Uuid& outputValueTypeId,
const rapidjson::Value& inputValue, JsonDeserializerContext& context)
{
return JsonMathMatrixSerializerInternal::Load<Matrix4x4, 4, 4>(
outputValue,
outputValueTypeId,
inputValue,
context);
}
JsonSerializationResult::Result JsonMatrix4x4Serializer::Store(rapidjson::Value& outputValue, const void* inputValue,
const void* defaultValue, const Uuid& valueTypeId, JsonSerializerContext& context)
{
outputValue.SetObject();
auto result = JsonMathMatrixSerializerInternal::StoreRotationAndScale<Matrix4x4>(
outputValue,
inputValue,
defaultValue,
valueTypeId,
context);
auto resultTranslation = JsonMathMatrixSerializerInternal::StoreTranslation<Matrix4x4>(
outputValue,
inputValue,
defaultValue,
valueTypeId,
context);
result.GetResultCode().Combine(resultTranslation);
return result;
}
}
@@ -0,0 +1,54 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/Serialization/Json/BaseJsonSerializer.h>
namespace AZ
{
class JsonMatrix3x3Serializer
: public BaseJsonSerializer
{
public:
AZ_RTTI(JsonMatrix3x3Serializer, "{8C76CD6A-8576-4604-A746-CF7A7F20F366}", BaseJsonSerializer);
AZ_CLASS_ALLOCATOR_DECL;
JsonSerializationResult::Result Load(void* outputValue, const Uuid& outputValueTypeId, const rapidjson::Value& inputValue,
JsonDeserializerContext& context) override;
JsonSerializationResult::Result Store(rapidjson::Value& outputValue, const void* inputValue, const void* defaultValue,
const Uuid& valueTypeId, JsonSerializerContext& context) override;
};
class JsonMatrix3x4Serializer
: public BaseJsonSerializer
{
public:
AZ_RTTI(JsonMatrix3x4Serializer, "{E801333B-4AF1-4F43-976C-579670B02DC5}", BaseJsonSerializer);
AZ_CLASS_ALLOCATOR_DECL;
JsonSerializationResult::Result Load(void* outputValue, const Uuid& outputValueTypeId, const rapidjson::Value& inputValue,
JsonDeserializerContext& context) override;
JsonSerializationResult::Result Store(rapidjson::Value& outputValue, const void* inputValue, const void* defaultValue,
const Uuid& valueTypeId, JsonSerializerContext& context) override;
};
class JsonMatrix4x4Serializer
: public BaseJsonSerializer
{
public:
AZ_RTTI(JsonMatrix4x4Serializer, "{46E888FC-248A-4910-9221-4E101A10AEA1}", BaseJsonSerializer);
AZ_CLASS_ALLOCATOR_DECL;
JsonSerializationResult::Result Load(void* outputValue, const Uuid& outputValueTypeId, const rapidjson::Value& inputValue,
JsonDeserializerContext& context) override;
JsonSerializationResult::Result Store(rapidjson::Value& outputValue, const void* inputValue, const void* defaultValue,
const Uuid& valueTypeId, JsonSerializerContext& context) override;
};
}
@@ -24,6 +24,7 @@
#include <AzCore/Math/Vector2.h>
#include <AzCore/Math/Vector3.h>
#include <AzCore/Math/Vector4.h>
#include <AzCore/Math/MathMatrixSerializer.h>
#include <AzCore/Math/MathVectorSerializer.h>
#include <AzCore/Math/Color.h>
#include <AzCore/Math/ColorSerializer.h>
@@ -366,6 +367,9 @@ namespace AZ
{
context.Serializer<JsonColorSerializer>()->HandlesType<Color>();
context.Serializer<JsonUuidSerializer>()->HandlesType<Uuid>();
context.Serializer<JsonMatrix3x3Serializer>()->HandlesType<Matrix3x3>();
context.Serializer<JsonMatrix3x4Serializer>()->HandlesType<Matrix3x4>();
context.Serializer<JsonMatrix4x4Serializer>()->HandlesType<Matrix4x4>();
context.Serializer<JsonVector2Serializer>()->HandlesType<Vector2>();
context.Serializer<JsonVector3Serializer>()->HandlesType<Vector3>();
context.Serializer<JsonVector4Serializer>()->HandlesType<Vector4>();
@@ -53,6 +53,10 @@ namespace AZ
//! RemoveableByUser : A bool which determines if the component can be removed by the user.
//! Setting this to false prevents the user from removing this component. Default behavior is removeable by user.
const static AZ::Crc32 RemoveableByUser = AZ_CRC("RemoveableByUser", 0x32c7fd50);
//! An int which, if specified, causes a component to be forced to a particular position in the sorted list of
//! components on an entity, and prevents dragging or moving operations which would affect that position.
const static AZ::Crc32 FixedComponentListIndex = AZ_CRC_CE("FixedComponentListIndex");
const static AZ::Crc32 AppearsInAddComponentMenu = AZ_CRC("AppearsInAddComponentMenu", 0x53790e31);
const static AZ::Crc32 ForceAutoExpand = AZ_CRC("ForceAutoExpand", 0x1a5c79d2); // Ignores expansion state set by user, enforces expansion.
const static AZ::Crc32 AutoExpand = AZ_CRC("AutoExpand", 0x306ff5c0); // Expands automatically unless user changes expansion state.
@@ -74,7 +74,7 @@ namespace AZ
"Unable to retrieve the correct container information for AZStd::array instance.");
}
Flags flags = Flags::None;
ContinuationFlags flags = ContinuationFlags::None;
Uuid elementTypeId = Uuid::CreateNull();
auto typeEnumCallback = [&elementTypeId, &flags](const Uuid&, const SerializeContext::ClassElement* genericClassElement)
{
@@ -82,7 +82,7 @@ namespace AZ
elementTypeId = genericClassElement->m_typeId;
if (genericClassElement->m_flags & SerializeContext::ClassElement::Flags::FLG_POINTER)
{
flags = Flags::ResolvePointer;
flags = ContinuationFlags::ResolvePointer;
}
return false;
};
@@ -161,7 +161,7 @@ namespace AZ
"Not enough entries in JSON array to load an AZStd::array from.");
}
Flags flags = Flags::None;
ContinuationFlags flags = ContinuationFlags::None;
Uuid elementTypeId = Uuid::CreateNull();
auto typeEnumCallback = [&elementTypeId, &flags](const Uuid&, const SerializeContext::ClassElement* genericClassElement)
{
@@ -169,7 +169,7 @@ namespace AZ
elementTypeId = genericClassElement->m_typeId;
if (genericClassElement->m_flags & SerializeContext::ClassElement::Flags::FLG_POINTER)
{
flags = Flags::ResolvePointer;
flags = ContinuationFlags::ResolvePointer;
}
return false;
};
@@ -208,22 +208,28 @@ namespace AZ
// BaseJsonSerializer
//
JsonSerializationResult::ResultCode BaseJsonSerializer::ContinueLoading(void* object, const Uuid& typeId, const rapidjson::Value& value,
JsonDeserializerContext& context, Flags flags)
BaseJsonSerializer::OperationFlags BaseJsonSerializer::GetOperationsFlags() const
{
return flags & Flags::ResolvePointer ?
JsonDeserializer::LoadToPointer(object, typeId, value, context) :
JsonDeserializer::Load(object, typeId, value, context);
return OperationFlags::None;
}
JsonSerializationResult::ResultCode BaseJsonSerializer::ContinueStoring(rapidjson::Value& output, const void* object,
const void* defaultObject, const Uuid& typeId, JsonSerializerContext& context, Flags flags)
JsonSerializationResult::ResultCode BaseJsonSerializer::ContinueLoading(
void* object, const Uuid& typeId, const rapidjson::Value& value, JsonDeserializerContext& context, ContinuationFlags flags)
{
return (flags & ContinuationFlags::ResolvePointer) == ContinuationFlags::ResolvePointer
? JsonDeserializer::LoadToPointer(object, typeId, value, context)
: JsonDeserializer::Load(object, typeId, value, context);
}
JsonSerializationResult::ResultCode BaseJsonSerializer::ContinueStoring(
rapidjson::Value& output, const void* object, const void* defaultObject, const Uuid& typeId, JsonSerializerContext& context,
ContinuationFlags flags)
{
using namespace JsonSerializationResult;
if (flags & Flags::ReplaceDefault && !context.ShouldKeepDefaults())
if ((flags & ContinuationFlags::ReplaceDefault) == ContinuationFlags::ReplaceDefault && !context.ShouldKeepDefaults())
{
if (flags & Flags::ResolvePointer)
if ((flags & ContinuationFlags::ResolvePointer) == ContinuationFlags::ResolvePointer)
{
return JsonSerializer::StoreFromPointer(output, object, nullptr, typeId, context);
}
@@ -248,7 +254,7 @@ namespace AZ
}
}
return flags & Flags::ResolvePointer ?
return (flags & ContinuationFlags::ResolvePointer) == ContinuationFlags::ResolvePointer ?
JsonSerializer::StoreFromPointer(output, object, defaultObject, typeId, context) :
JsonSerializer::Store(output, object, defaultObject, typeId, context);
}
@@ -265,8 +271,9 @@ namespace AZ
return JsonSerializer::StoreTypeName(output, typeId, context);
}
JsonSerializationResult::ResultCode BaseJsonSerializer::ContinueLoadingFromJsonObjectField(void* object, const Uuid& typeId, const rapidjson::Value& value,
rapidjson::Value::StringRefType memberName, JsonDeserializerContext& context, Flags flags)
JsonSerializationResult::ResultCode BaseJsonSerializer::ContinueLoadingFromJsonObjectField(
void* object, const Uuid& typeId, const rapidjson::Value& value, rapidjson::Value::StringRefType memberName,
JsonDeserializerContext& context, ContinuationFlags flags)
{
using namespace JsonSerializationResult;
@@ -291,7 +298,7 @@ namespace AZ
JsonSerializationResult::ResultCode BaseJsonSerializer::ContinueStoringToJsonObjectField(rapidjson::Value& output,
rapidjson::Value::StringRefType newMemberName, const void* object, const void* defaultObject,
const Uuid& typeId, JsonSerializerContext& context, Flags flags)
const Uuid& typeId, JsonSerializerContext& context, ContinuationFlags flags)
{
using namespace JsonSerializationResult;
@@ -161,13 +161,19 @@ namespace AZ
public:
AZ_RTTI(BaseJsonSerializer, "{7291FFDC-D339-40B5-BB26-EA067A327B21}");
enum Flags
enum class ContinuationFlags
{
None = 0, //! No extra flags.
None = 0, //! No extra flags.
ResolvePointer = 1 << 0, //! The pointer passed in contains a pointer. The (de)serializer will attempt to resolve to an instance.
ReplaceDefault = 1 << 1 //! The default value provided for storing will be replaced with a newly created one.
};
enum class OperationFlags
{
None = 0, //! No flags that control how the custom json serializer is used.
ManualDefault = 1 << 0 //! Even if an (explicit) default is found the custom json serializer will still be called.
};
virtual ~BaseJsonSerializer() = default;
//! Transforms the data from the rapidjson Value to outputValue, if the conversion is possible and supported.
@@ -180,6 +186,9 @@ namespace AZ
virtual JsonSerializationResult::Result Store(rapidjson::Value& outputValue, const void* inputValue, const void* defaultValue,
const Uuid& valueTypeId, JsonSerializerContext& context) = 0;
//! Returns the operation flags which tells the Json Serialization how this custom json serializer can be used.
virtual OperationFlags GetOperationsFlags() const;
protected:
//! Continues loading of a (sub)value. Use this function to load member variables for instance. This is more optimal than
//! directly calling the json serialization.
@@ -187,8 +196,9 @@ namespace AZ
//! @param typeId Type id of the object passed in.
//! @param value The value in the JSON document where the deserializer will start reading data from.
//! @param context The context used during deserialization. Use the value passed in from Load.
JsonSerializationResult::ResultCode ContinueLoading(void* object, const Uuid& typeId, const rapidjson::Value& value,
JsonDeserializerContext& context, Flags flags = Flags::None);
JsonSerializationResult::ResultCode ContinueLoading(
void* object, const Uuid& typeId, const rapidjson::Value& value, JsonDeserializerContext& context,
ContinuationFlags flags = ContinuationFlags::None);
//! Continues storing of a (sub)value. Use this function to store member variables for instance. This is more optimal than
//! directly calling the json serialization.
@@ -200,8 +210,9 @@ namespace AZ
//! the settings.
//! @param typeId The type id of the object and default object.
//! @param context The context used during serialization. Use the value passed in from Store.
JsonSerializationResult::ResultCode ContinueStoring(rapidjson::Value& output, const void* object, const void* defaultObject,
const Uuid& typeId, JsonSerializerContext& context, Flags flags = Flags::None);
JsonSerializationResult::ResultCode ContinueStoring(
rapidjson::Value& output, const void* object, const void* defaultObject, const Uuid& typeId, JsonSerializerContext& context,
ContinuationFlags flags = ContinuationFlags::None);
//! Retrieves the type id from a json object or json string.
//! @param typeId The retrieved type id.
@@ -222,12 +233,14 @@ namespace AZ
const Uuid& typeId, JsonSerializerContext& context);
//! Helper function similar to ContinueLoading, but loads the data as a member of 'value' rather than 'value' itself, if it exists.
JsonSerializationResult::ResultCode ContinueLoadingFromJsonObjectField(void* object, const Uuid& typeId, const rapidjson::Value& value,
rapidjson::Value::StringRefType memberName, JsonDeserializerContext& context, Flags flags = Flags::None);
JsonSerializationResult::ResultCode ContinueLoadingFromJsonObjectField(
void* object, const Uuid& typeId, const rapidjson::Value& value, rapidjson::Value::StringRefType memberName,
JsonDeserializerContext& context, ContinuationFlags flags = ContinuationFlags::None);
//! Helper function similar to ContinueStoring, but stores the data as a member of 'output' rather than overwriting 'output'.
JsonSerializationResult::ResultCode ContinueStoringToJsonObjectField(rapidjson::Value& output, rapidjson::Value::StringRefType newMemberName,
const void* object, const void* defaultObject, const Uuid& typeId, JsonSerializerContext& context, Flags flags = Flags::None);
JsonSerializationResult::ResultCode ContinueStoringToJsonObjectField(
rapidjson::Value& output, rapidjson::Value::StringRefType newMemberName, const void* object, const void* defaultObject,
const Uuid& typeId, JsonSerializerContext& context, ContinuationFlags flags = ContinuationFlags::None);
//! Checks if a value is an explicit default. This useful for containers where not storing anything as a default would mean
//! a slot wouldn't be used so something has to be added to represent the fully default target.
@@ -238,6 +251,7 @@ namespace AZ
rapidjson::Value GetExplicitDefault();
};
AZ_DEFINE_ENUM_BITWISE_OPERATORS(AZ::BaseJsonSerializer::Flags)
AZ_DEFINE_ENUM_BITWISE_OPERATORS(AZ::BaseJsonSerializer::ContinuationFlags)
AZ_DEFINE_ENUM_BITWISE_OPERATORS(AZ::BaseJsonSerializer::OperationFlags)
} // namespace AZ
@@ -75,9 +75,10 @@ namespace AZ
auto elementCallback = [this, &array, &retVal, &index, &context]
(void* elementPtr, const Uuid& elementId, const SerializeContext::ClassData*, const SerializeContext::ClassElement* classElement)
{
Flags flags = classElement->m_flags & SerializeContext::ClassElement::Flags::FLG_POINTER ?
Flags::ResolvePointer : Flags::None;
flags |= Flags::ReplaceDefault;
ContinuationFlags flags = classElement->m_flags & SerializeContext::ClassElement::Flags::FLG_POINTER
? ContinuationFlags::ResolvePointer
: ContinuationFlags::None;
flags |= ContinuationFlags::ReplaceDefault;
ScopedContextPath subPath(context, index);
index++;
@@ -161,8 +162,9 @@ namespace AZ
container->EnumTypes(typeEnumCallback);
AZ_Assert(classElement, "No class element found for the type in the basic container.");
Flags flags = classElement->m_flags & SerializeContext::ClassElement::Flags::FLG_POINTER ?
Flags::ResolvePointer : Flags::None;
ContinuationFlags flags = classElement->m_flags & SerializeContext::ClassElement::Flags::FLG_POINTER
? ContinuationFlags::ResolvePointer
: ContinuationFlags::None;
const size_t capacity = container->IsFixedCapacity() ? container->Capacity(outputValue) : std::numeric_limits<size_t>::max();
@@ -22,6 +22,19 @@
namespace AZ
{
JsonSerializationResult::ResultCode JsonDeserializer::DeserializerDefaultCheck(BaseJsonSerializer* serializer, void* object,
const Uuid& typeId, const rapidjson::Value& value, JsonDeserializerContext& context)
{
using namespace AZ::JsonSerializationResult;
bool isExplicitDefault = IsExplicitDefault(value);
bool manuallyDefaults = (serializer->GetOperationsFlags() & BaseJsonSerializer::OperationFlags::ManualDefault) ==
BaseJsonSerializer::OperationFlags::ManualDefault;
return !isExplicitDefault || (isExplicitDefault && manuallyDefaults)
? serializer->Load(object, typeId, value, context)
: context.Report(Tasks::ReadField, Outcomes::DefaultsUsed, "Value has an explicit default.");
}
JsonSerializationResult::ResultCode JsonDeserializer::Load(void* object, const Uuid& typeId, const rapidjson::Value& value,
JsonDeserializerContext& context)
{
@@ -33,17 +46,12 @@ namespace AZ
"Target object for Json Serialization is pointing to nothing during loading.");
}
if (IsExplicitDefault(value))
{
return context.Report(Tasks::ReadField, Outcomes::DefaultsUsed, "Value has an explicit default.");
}
BaseJsonSerializer* serializer = context.GetRegistrationContext()->GetSerializerForType(typeId);
if (serializer)
{
return serializer->Load(object, typeId, value, context);
return DeserializerDefaultCheck(serializer, object, typeId, value, context);
}
const SerializeContext::ClassData* classData = context.GetSerializeContext()->FindClassData(typeId);
if (!classData)
{
@@ -56,9 +64,14 @@ namespace AZ
serializer = context.GetRegistrationContext()->GetSerializerForType(classData->m_azRtti->GetGenericTypeId());
if (serializer)
{
return serializer->Load(object, typeId, value, context);
return DeserializerDefaultCheck(serializer, object, typeId, value, context);
}
}
if (IsExplicitDefault(value))
{
return context.Report(Tasks::ReadField, Outcomes::DefaultsUsed, "Value has an explicit default.");
}
if (classData->m_azRtti && (classData->m_azRtti->GetTypeTraits() & AZ::TypeTraits::is_enum) == AZ::TypeTraits::is_enum)
{
@@ -113,5 +113,13 @@ namespace AZ
//! Checks if a value is an explicit default. This means the value is an object with no members.
static bool IsExplicitDefault(const rapidjson::Value& value);
private:
static JsonSerializationResult::ResultCode DeserializerDefaultCheck(
BaseJsonSerializer* serializer,
void* object,
const Uuid& typeId,
const rapidjson::Value& value,
JsonDeserializerContext& context);
};
} // namespace AZ
@@ -215,10 +215,10 @@ namespace AZ
// Load key
void* keyAddress = pairContainer->GetElementByIndex(address, pairElement, 0);
AZ_Assert(keyAddress, "Element reserved for associative container, but unable to retrieve address of the key.");
Flags keyLoadFlags = Flags::None;
ContinuationFlags keyLoadFlags = ContinuationFlags::None;
if (keyElement->m_flags & SerializeContext::ClassElement::Flags::FLG_POINTER)
{
keyLoadFlags = Flags::ResolvePointer;
keyLoadFlags = ContinuationFlags::ResolvePointer;
*reinterpret_cast<void**>(keyAddress) = nullptr;
}
JSR::ResultCode keyResult = ContinueLoading(keyAddress, keyElement->m_typeId, key, context, keyLoadFlags);
@@ -231,10 +231,10 @@ namespace AZ
// Load value
void* valueAddress = pairContainer->GetElementByIndex(address, pairElement, 1);
AZ_Assert(valueAddress, "Element reserved for associative container, but unable to retrieve address of the value.");
Flags valueLoadFlags = Flags::None;
ContinuationFlags valueLoadFlags = ContinuationFlags::None;
if (valueElement->m_flags & SerializeContext::ClassElement::Flags::FLG_POINTER)
{
valueLoadFlags = Flags::ResolvePointer;
valueLoadFlags = ContinuationFlags::ResolvePointer;
*reinterpret_cast<void**>(valueAddress) = nullptr;
}
JSR::ResultCode valueResult = ContinueLoading(valueAddress, valueElement->m_typeId, value, context, valueLoadFlags);
@@ -82,7 +82,7 @@ namespace AZ
{
// If the target type is the same as the type already stored in the smart pointer than no new
// instance is created and the existing instance will be updated with the data in the json document.
result = ContinueLoading(instance, elementClassId, inputValue, context, Flags::ResolvePointer);
result = ContinueLoading(instance, elementClassId, inputValue, context, ContinuationFlags::ResolvePointer);
return false;
}
}
@@ -93,7 +93,7 @@ namespace AZ
// the wrong address. In these cases explicitly reset the smart pointer. This will erase the existing
// data but that's fine as it's not being used.
void* element = nullptr;
result = ContinueLoading(&element, elementClassId, inputValue, context, Flags::ResolvePointer);
result = ContinueLoading(&element, elementClassId, inputValue, context, ContinuationFlags::ResolvePointer);
if (result.GetProcessing() != JSR::Processing::Halted && result.GetProcessing() != JSR::Processing::Altered)
{
void* elementPtr = container->ReserveElement(instance, nullptr);
@@ -155,8 +155,14 @@ namespace AZ
container->EnumElements(const_cast<void*>(defaultValue), defaultInputCallback);
}
JSR::ResultCode result = ContinueStoring(outputValue, inputValue, defaultValue, inputPtrType, context, Flags::ResolvePointer);
JSR::ResultCode result =
ContinueStoring(outputValue, inputValue, defaultValue, inputPtrType, context, ContinuationFlags::ResolvePointer);
return context.Report(result, result.GetProcessing() != JSR::Processing::Halted ?
"Successfully processed smart pointer." : "A problem occurred while processing a smart pointer.");
}
BaseJsonSerializer::OperationFlags JsonSmartPointerSerializer::GetOperationsFlags() const
{
return OperationFlags::ManualDefault;
}
} // namespace AZ
@@ -28,5 +28,7 @@ namespace AZ
JsonDeserializerContext& context) override;
JsonSerializationResult::Result Store(rapidjson::Value& outputValue, const void* inputValue, const void* defaultValue,
const Uuid& valueTypeId, JsonSerializerContext& context) override;
OperationFlags GetOperationsFlags() const override;
};
} // namespace AZ
@@ -99,8 +99,9 @@ namespace AZ
ScopedContextPath subPath(context, i);
Flags flags = classElements[i]->m_flags & SerializeContext::ClassElement::Flags::FLG_POINTER ?
Flags::ResolvePointer : Flags::None;
ContinuationFlags flags = classElements[i]->m_flags & SerializeContext::ClassElement::Flags::FLG_POINTER
? ContinuationFlags::ResolvePointer
: ContinuationFlags::None;
JSR::ResultCode result = ContinueStoring(elementValues[i], elementAddress, defaultElementAddress,
classElements[i]->m_typeId, context, flags);
@@ -179,8 +180,9 @@ namespace AZ
void* elementAddress = container->GetElementByIndex(outputValue, nullptr, i);
AZ_Assert(elementAddress, "Address of AZStd::pair or AZStd::tuple element %zu could not be retrieved.", i);
Flags flags = classElements[i]->m_flags & SerializeContext::ClassElement::Flags::FLG_POINTER ?
Flags::ResolvePointer : Flags::None;
ContinuationFlags flags = classElements[i]->m_flags & SerializeContext::ClassElement::Flags::FLG_POINTER
? ContinuationFlags::ResolvePointer
: ContinuationFlags::None;
while (arrayIndex < inputValue.Size())
{
@@ -290,6 +290,8 @@ set(FILES
Math/MathScriptHelpers.h
Math/MathUtils.cpp
Math/MathUtils.h
Math/MathMatrixSerializer.h
Math/MathMatrixSerializer.cpp
Math/MathVectorSerializer.h
Math/MathVectorSerializer.cpp
Math/Matrix3x3.cpp
@@ -104,6 +104,11 @@ namespace JsonSerializationTests
AZ::AllocatorInstance<AZ::PoolAllocator>::Destroy();
}
void Reflect(AZStd::unique_ptr<AZ::SerializeContext>& context) override
{
context->RegisterGenericType<Asset>();
}
AZStd::shared_ptr<AZ::BaseJsonSerializer> CreateSerializer() override
{
return AZStd::make_shared<AZ::Data::AssetJsonSerializer>();
@@ -119,7 +119,8 @@ namespace JsonSerializationTests
int value = 0;
int* ptrValue = &value;
ResultCode result = ContinueLoading(&ptrValue, azrtti_typeid<int>(), json, *m_jsonDeserializationContext, Flags::ResolvePointer);
ResultCode result =
ContinueLoading(&ptrValue, azrtti_typeid<int>(), json, *m_jsonDeserializationContext, ContinuationFlags::ResolvePointer);
EXPECT_EQ(Processing::Completed, result.GetProcessing());
ASSERT_NE(nullptr, ptrValue);
@@ -134,7 +135,8 @@ namespace JsonSerializationTests
json.Set(42);
int* ptrValue = nullptr;
ResultCode result = ContinueLoading(&ptrValue, azrtti_typeid<int>(), json, *m_jsonDeserializationContext, Flags::ResolvePointer);
ResultCode result =
ContinueLoading(&ptrValue, azrtti_typeid<int>(), json, *m_jsonDeserializationContext, ContinuationFlags::ResolvePointer);
EXPECT_EQ(Processing::Completed, result.GetProcessing());
ASSERT_NE(nullptr, ptrValue);
@@ -150,7 +152,8 @@ namespace JsonSerializationTests
rapidjson::Value json(rapidjson::kObjectType);
int* ptrValue = nullptr;
ResultCode result = ContinueLoading(&ptrValue, azrtti_typeid<int>(), json, *m_jsonDeserializationContext, Flags::ResolvePointer);
ResultCode result =
ContinueLoading(&ptrValue, azrtti_typeid<int>(), json, *m_jsonDeserializationContext, ContinuationFlags::ResolvePointer);
EXPECT_EQ(Processing::Completed, result.GetProcessing());
ASSERT_NE(nullptr, ptrValue);
@@ -165,7 +168,8 @@ namespace JsonSerializationTests
rapidjson::Value json(rapidjson::kNullType);
int* ptrValue = reinterpret_cast<int*>(azmalloc(sizeof(int), alignof(int), AZ::SystemAllocator));
ResultCode result = ContinueLoading(&ptrValue, azrtti_typeid<int>(), json, *m_jsonDeserializationContext, Flags::ResolvePointer);
ResultCode result =
ContinueLoading(&ptrValue, azrtti_typeid<int>(), json, *m_jsonDeserializationContext, ContinuationFlags::ResolvePointer);
EXPECT_EQ(Processing::Completed, result.GetProcessing());
ASSERT_EQ(nullptr, ptrValue);
@@ -194,8 +198,8 @@ namespace JsonSerializationTests
int value = 42;
int* ptrValue = &value;
ResultCode result = ContinueStoring(*m_jsonDocument, &ptrValue, nullptr, azrtti_typeid<int>(), *m_jsonSerializationContext,
Flags::ResolvePointer);
ResultCode result = ContinueStoring(
*m_jsonDocument, &ptrValue, nullptr, azrtti_typeid<int>(), *m_jsonSerializationContext, ContinuationFlags::ResolvePointer);
EXPECT_EQ(Processing::Completed, result.GetProcessing());
Expect_DocStrEq("42");
@@ -210,8 +214,9 @@ namespace JsonSerializationTests
int value2 = 42;
int* defaultPtrValue = &value2;
ResultCode result =
ContinueStoring(*m_jsonDocument, &ptrValue, &defaultPtrValue, azrtti_typeid<int>(), *m_jsonSerializationContext, Flags::ResolvePointer);
ResultCode result = ContinueStoring(
*m_jsonDocument, &ptrValue, &defaultPtrValue, azrtti_typeid<int>(), *m_jsonSerializationContext,
ContinuationFlags::ResolvePointer);
EXPECT_EQ(Processing::Completed, result.GetProcessing());
Expect_DocStrEq("{}");
@@ -224,7 +229,7 @@ namespace JsonSerializationTests
int* ptrValue = nullptr;
ResultCode result = ContinueStoring(
*m_jsonDocument, &ptrValue, nullptr, azrtti_typeid<int>(), *m_jsonSerializationContext, Flags::ResolvePointer);
*m_jsonDocument, &ptrValue, nullptr, azrtti_typeid<int>(), *m_jsonSerializationContext, ContinuationFlags::ResolvePointer);
EXPECT_EQ(Processing::Completed, result.GetProcessing());
Expect_DocStrEq("null");
@@ -238,8 +243,9 @@ namespace JsonSerializationTests
int value2 = 42;
int* defaultPtrValue = &value2;
ResultCode result =
ContinueStoring(*m_jsonDocument, &ptrValue, &defaultPtrValue, azrtti_typeid<int>(), *m_jsonSerializationContext, Flags::ResolvePointer);
ResultCode result = ContinueStoring(
*m_jsonDocument, &ptrValue, &defaultPtrValue, azrtti_typeid<int>(), *m_jsonSerializationContext,
ContinuationFlags::ResolvePointer);
EXPECT_EQ(Processing::Completed, result.GetProcessing());
Expect_DocStrEq("null");
@@ -252,8 +258,9 @@ namespace JsonSerializationTests
int* ptrValue = nullptr;
int* defaultPtrValue = nullptr;
ResultCode result =
ContinueStoring(*m_jsonDocument, &ptrValue, &defaultPtrValue, azrtti_typeid<int>(), *m_jsonSerializationContext, Flags::ResolvePointer);
ResultCode result = ContinueStoring(
*m_jsonDocument, &ptrValue, &defaultPtrValue, azrtti_typeid<int>(), *m_jsonSerializationContext,
ContinuationFlags::ResolvePointer);
EXPECT_EQ(Processing::Completed, result.GetProcessing());
Expect_DocStrEq("null");
@@ -265,8 +272,8 @@ namespace JsonSerializationTests
int value = 42;
ResultCode result = ContinueStoring(*m_jsonDocument, &value, nullptr, azrtti_typeid<int>(), *m_jsonSerializationContext,
Flags::ReplaceDefault);
ResultCode result = ContinueStoring(
*m_jsonDocument, &value, nullptr, azrtti_typeid<int>(), *m_jsonSerializationContext, ContinuationFlags::ReplaceDefault);
EXPECT_EQ(Processing::Completed, result.GetProcessing());
Expect_DocStrEq("42");
@@ -280,7 +287,7 @@ namespace JsonSerializationTests
int* ptrValue = &value;
ResultCode result = ContinueStoring(*m_jsonDocument, &ptrValue, nullptr, azrtti_typeid<int>(), *m_jsonSerializationContext,
Flags::ResolvePointer | Flags::ReplaceDefault);
ContinuationFlags::ResolvePointer | ContinuationFlags::ReplaceDefault);
EXPECT_EQ(Processing::Completed, result.GetProcessing());
Expect_DocStrEq("42");
@@ -293,8 +300,8 @@ namespace JsonSerializationTests
int value = 42;
AZ::Uuid unknownType("{09AE3CEC-EBFC-41EC-A7F6-949721521716}");
ResultCode result = ContinueStoring(*m_jsonDocument, &value, nullptr, unknownType, *m_jsonSerializationContext,
Flags::ReplaceDefault);
ResultCode result =
ContinueStoring(*m_jsonDocument, &value, nullptr, unknownType, *m_jsonSerializationContext, ContinuationFlags::ReplaceDefault);
EXPECT_EQ(Processing::Halted, result.GetProcessing());
}
@@ -90,9 +90,14 @@ namespace JsonSerializationTests
virtual ~JsonSerializerConformityTestDescriptor() = default;
virtual AZStd::shared_ptr<AZ::BaseJsonSerializer> CreateSerializer() = 0;
//! Create an instance of the target type with all values set to default.
virtual AZStd::shared_ptr<T> CreateDefaultInstance() = 0;
//! Create an instance of the target type that constructed with default constructor.
//! This will be the same instance that Json Serialization creates for dynamic types. Typically it's the same
//! as from CreateDefaultInstance(), except of types, such as pointers, that need to do minimal (de)serialization
//! to initialize an object.
virtual AZStd::shared_ptr<T> CreateDefaultConstructedInstance() { return CreateDefaultInstance(); }
//! Create an instance of the target type with some values set and some kept on defaults.
//! If the target type doesn't support partial specialization this can be ignored and
//! tests for partial support will be skipped.
@@ -316,10 +321,10 @@ namespace JsonSerializationTests
ASSERT_FALSE(this->m_jsonDocument->HasParseError());
auto serializer = this->m_description.CreateSerializer();
auto instance = this->m_description.CreateDefaultInstance();
auto instance = this->m_description.CreateDefaultConstructedInstance();
auto original = this->m_description.CreateDefaultInstance();
ResultCode result = serializer->Load(instance.get(), azrtti_typeid(*original),
ResultCode result = serializer->Load(instance.get(), azrtti_typeid(*instance),
*this->m_jsonDocument, *this->m_jsonDeserializationContext);
if (this->m_features.m_mandatoryFields.empty())
@@ -339,6 +344,42 @@ namespace JsonSerializationTests
}
}
TYPED_TEST_P(JsonSerializerConformityTests, Load_DeserializeEmptyObjectThroughMainLoad_SucceedsAndObjectMatchesDefaults)
{
using namespace AZ::JsonSerializationResult;
if (this->m_features.SupportsJsonType(rapidjson::kObjectType))
{
this->m_jsonDocument->Parse("{}");
ASSERT_FALSE(this->m_jsonDocument->HasParseError());
auto serializer = this->m_description.CreateSerializer();
auto instance = this->m_description.CreateDefaultConstructedInstance();
auto original = this->m_description.CreateDefaultInstance();
AZ::JsonDeserializerSettings settings;
settings.m_serializeContext = this->m_jsonDeserializationContext->GetSerializeContext();
settings.m_registrationContext = this->m_jsonDeserializationContext->GetRegistrationContext();
ResultCode result = AZ::JsonSerialization::Load(
instance.get(), azrtti_typeid(*instance), *this->m_jsonDocument, settings);
if (this->m_features.m_mandatoryFields.empty())
{
EXPECT_EQ(Outcomes::DefaultsUsed, result.GetOutcome());
EXPECT_EQ(Processing::Completed, result.GetProcessing());
}
else
{
EXPECT_EQ(Outcomes::Unsupported, result.GetOutcome());
bool validProcessing =
result.GetProcessing() == Processing::Altered ||
result.GetProcessing() == Processing::PartialAlter;
EXPECT_TRUE(validProcessing);
}
EXPECT_TRUE(this->m_description.AreEqual(*original, *instance));
}
}
TYPED_TEST_P(JsonSerializerConformityTests, Load_DeserializeEmptyArray_SucceedsAndObjectMatchesDefaults)
{
using namespace AZ::JsonSerializationResult;
@@ -349,7 +390,7 @@ namespace JsonSerializationTests
ASSERT_FALSE(this->m_jsonDocument->HasParseError());
auto serializer = this->m_description.CreateSerializer();
auto instance = this->m_description.CreateDefaultInstance();
auto instance = this->m_description.CreateDefaultConstructedInstance();
auto original = this->m_description.CreateDefaultInstance();
this->m_deserializationSettings->m_clearContainers = false;
@@ -384,7 +425,7 @@ namespace JsonSerializationTests
ASSERT_FALSE(this->m_jsonDocument->HasParseError());
auto serializer = this->m_description.CreateSerializer();
auto instance = this->m_description.CreateDefaultInstance();
auto instance = this->m_description.CreateDefaultConstructedInstance();
auto original = this->m_description.CreateDefaultInstance();
this->m_deserializationSettings->m_clearContainers = true;
@@ -488,7 +529,7 @@ namespace JsonSerializationTests
ASSERT_FALSE(this->m_jsonDocument->HasParseError());
auto serializer = this->m_description.CreateSerializer();
auto instance = this->m_description.CreateDefaultInstance();
auto instance = this->m_description.CreateDefaultConstructedInstance();
auto compare = this->m_description.CreateFullySetInstance();
ResultCode result = serializer->Load(instance.get(), azrtti_typeid(*instance),
@@ -499,6 +540,28 @@ namespace JsonSerializationTests
EXPECT_TRUE(this->m_description.AreEqual(*instance, *compare));
}
TYPED_TEST_P(JsonSerializerConformityTests, Load_DeserializeFullySetInstanceThroughMainLoad_SucceedsAndObjectMatchesFullySetInstance)
{
using namespace AZ::JsonSerializationResult;
AZStd::string_view json = this->m_description.GetJsonFor_Load_DeserializeFullySetInstance();
this->m_jsonDocument->Parse(json.data());
ASSERT_FALSE(this->m_jsonDocument->HasParseError());
auto serializer = this->m_description.CreateSerializer();
auto instance = this->m_description.CreateDefaultConstructedInstance();
auto compare = this->m_description.CreateFullySetInstance();
AZ::JsonDeserializerSettings settings;
settings.m_serializeContext = this->m_jsonDeserializationContext->GetSerializeContext();
settings.m_registrationContext = this->m_jsonDeserializationContext->GetRegistrationContext();
ResultCode result = AZ::JsonSerialization::Load(instance.get(), azrtti_typeid(*instance), *this->m_jsonDocument, settings);
EXPECT_EQ(Outcomes::Success, result.GetOutcome());
EXPECT_EQ(Processing::Completed, result.GetProcessing());
EXPECT_TRUE(this->m_description.AreEqual(*instance, *compare));
}
TYPED_TEST_P(JsonSerializerConformityTests, Load_DeserializeWithMissingMandatoryField_LoadFailedAndUnsupportedReported)
{
using namespace AZ::JsonSerializationResult;
@@ -518,7 +581,7 @@ namespace JsonSerializationTests
ASSERT_NE(this->m_jsonDocument->MemberEnd(), memberToErase);
this->m_jsonDocument->RemoveMember(memberToErase);
auto instance = this->m_description.CreateDefaultInstance();
auto instance = this->m_description.CreateDefaultConstructedInstance();
ResultCode result = serializer->Load(instance.get(), azrtti_typeid(*instance),
*this->m_jsonDocument, *this->m_jsonDeserializationContext);
@@ -546,7 +609,7 @@ namespace JsonSerializationTests
ASSERT_FALSE(this->m_jsonDocument->HasParseError());
auto serializer = this->m_description.CreateSerializer();
auto instance = this->m_description.CreateDefaultInstance();
auto instance = this->m_description.CreateDefaultConstructedInstance();
auto compare = this->m_description.CreatePartialDefaultInstance();
ASSERT_NE(nullptr, compare);
@@ -567,7 +630,7 @@ namespace JsonSerializationTests
ASSERT_FALSE(this->m_jsonDocument->HasParseError());
auto serializer = this->m_description.CreateSerializer();
auto instance = this->m_description.CreateDefaultInstance();
auto instance = this->m_description.CreateDefaultConstructedInstance();
AZ::ScopedContextReporter reporter(*this->m_jsonDeserializationContext,
[](AZStd::string_view message, ResultCode result, AZStd::string_view path) -> ResultCode
@@ -604,7 +667,7 @@ namespace JsonSerializationTests
}
auto serializer = this->m_description.CreateSerializer();
auto instance = this->m_description.CreateDefaultInstance();
auto instance = this->m_description.CreateDefaultConstructedInstance();
auto compare = this->m_description.CreateFullySetInstance();
ResultCode result = serializer->Load(instance.get(), azrtti_typeid(*instance),
@@ -635,7 +698,7 @@ namespace JsonSerializationTests
}
auto serializer = this->m_description.CreateSerializer();
auto instance = this->m_description.CreateDefaultInstance();
auto instance = this->m_description.CreateDefaultConstructedInstance();
ResultCode result = serializer->Load(instance.get(), azrtti_typeid(*instance),
*this->m_jsonDocument, *this->m_jsonDeserializationContext);
@@ -693,6 +756,36 @@ namespace JsonSerializationTests
}
}
TYPED_TEST_P(JsonSerializerConformityTests, Store_SerializeDefaultInstanceThroughMainStore_EmptyJsonReturned)
{
using namespace AZ::JsonSerializationResult;
auto serializer = this->m_description.CreateSerializer();
auto instance = this->m_description.CreateDefaultInstance();
rapidjson::Value convertedValue = this->CreateExplicitDefault();
AZ::JsonSerializerSettings settings;
settings.m_serializeContext = this->m_jsonDeserializationContext->GetSerializeContext();
settings.m_registrationContext = this->m_jsonDeserializationContext->GetRegistrationContext();
ResultCode result = AZ::JsonSerialization::Store(
convertedValue, this->m_jsonDocument->GetAllocator(), instance.get(), instance.get(), azrtti_typeid(*instance), settings);
EXPECT_EQ(Processing::Completed, result.GetProcessing());
if (convertedValue.IsObject() && !this->m_features.m_mandatoryFields.empty())
{
ASSERT_EQ(convertedValue.MemberCount(), this->m_features.m_mandatoryFields.size());
for (const AZStd::string& mandatoryField : this->m_features.m_mandatoryFields)
{
EXPECT_NE(convertedValue.MemberEnd(), convertedValue.FindMember(mandatoryField.c_str()));
}
}
else
{
EXPECT_EQ(Outcomes::DefaultsUsed, result.GetOutcome());
this->Expect_ExplicitDefault(convertedValue);
}
}
TYPED_TEST_P(JsonSerializerConformityTests, Store_SerializeWithDefaultsKept_FullyWrittenJson)
{
using namespace AZ::JsonSerializationResult;
@@ -924,6 +1017,20 @@ namespace JsonSerializationTests
}
}
TYPED_TEST_P(JsonSerializerConformityTests, GetOperationsFlags_ManualDefaultSetIfNeeded_ManualDefaultOperationSetIfMandatoryFieldsAreDeclared)
{
if (this->m_features.SupportsJsonType(rapidjson::kObjectType))
{
if (!this->m_features.m_mandatoryFields.empty())
{
auto serializer = this->m_description.CreateSerializer();
bool manuallyHandlesDefaults = (serializer->GetOperationsFlags() & AZ::BaseJsonSerializer::OperationFlags::ManualDefault) ==
AZ::BaseJsonSerializer::OperationFlags::ManualDefault;
EXPECT_TRUE(manuallyHandlesDefaults);
}
}
}
REGISTER_TYPED_TEST_CASE_P(JsonSerializerConformityTests,
Registration_SerializerIsRegisteredWithContext_SerializerFound,
@@ -934,14 +1041,16 @@ namespace JsonSerializationTests
Load_InvalidTypeOfArrayType_ReturnsUnsupported,
Load_InvalidTypeOfStringType_ReturnsUnsupported,
Load_InvalidTypeOfNumberType_ReturnsUnsupported,
Load_DeserializeUnreflectedType_ReturnsUnsupported,
Load_DeserializeEmptyObject_SucceedsAndObjectMatchesDefaults,
Load_DeserializeEmptyObjectThroughMainLoad_SucceedsAndObjectMatchesDefaults,
Load_DeserializeEmptyArray_SucceedsAndObjectMatchesDefaults,
Load_DeserializeEmptyArrayWithClearEnabled_SucceedsAndObjectMatchesDefaults,
Load_DeserializeEmptyArrayWithClearedTarget_SucceedsAndObjectMatchesDefaults,
Load_InterruptClearingTarget_ContainerIsNotCleared,
Load_DeserializeFullySetInstance_SucceedsAndObjectMatchesFullySetInstance,
Load_DeserializeFullySetInstanceThroughMainLoad_SucceedsAndObjectMatchesFullySetInstance,
Load_DeserializePartialInstance_SucceedsAndObjectMatchesParialInstance,
Load_DeserializeWithMissingMandatoryField_LoadFailedAndUnsupportedReported,
Load_InsertAdditionalData_SucceedsAndObjectMatchesFullySetInstance,
@@ -950,6 +1059,7 @@ namespace JsonSerializationTests
Store_SerializeUnreflectedType_ReturnsUnsupported,
Store_SerializeDefaultInstance_EmptyJsonReturned,
Store_SerializeDefaultInstanceThroughMainStore_EmptyJsonReturned,
Store_SerializeWithDefaultsKept_FullyWrittenJson,
Store_SerializeFullySetInstance_StoredSuccessfullyAndJsonMatches,
Store_SerializeWithoutDefault_StoredSuccessfullyAndJsonMatches,
@@ -957,10 +1067,12 @@ namespace JsonSerializationTests
Store_SerializePartialInstance_StoredSuccessfullyAndJsonMatches,
Store_SerializeEmptyArray_StoredSuccessfullyAndJsonMatches,
Store_HaltedThroughCallback_StoreFailsAndHaltReported,
StoreLoad_RoundTripWithPartialDefault_IdenticalInstances,
StoreLoad_RoundTripWithFullSet_IdenticalInstances,
StoreLoad_RoundTripWithDefaultsKept_IdenticalInstances);
StoreLoad_RoundTripWithDefaultsKept_IdenticalInstances,
GetOperationsFlags_ManualDefaultSetIfNeeded_ManualDefaultOperationSetIfMandatoryFieldsAreDeclared);
} // namespace JsonSerializationTests
namespace AZ
@@ -0,0 +1,562 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <AzCore/Casting/numeric_cast.h>
#include <AzCore/Math/MathMatrixSerializer.h>
#include <AzCore/Math/Matrix3x3.h>
#include <AzCore/Math/Matrix3x4.h>
#include <AzCore/Math/Matrix4x4.h>
#include <AzCore/Math/Random.h>
#include <AzCore/Math/MathUtils.h>
#include <AzCore/Serialization/Json/DoubleSerializer.h>
#include <AzCore/Serialization/Json/RegistrationContext.h>
#include <Tests/Serialization/Json/BaseJsonSerializerFixture.h>
#include <Tests/Serialization/Json/JsonSerializerConformityTests.h>
namespace JsonSerializationTests
{
namespace DataHelper
{
// Build Matrix
template <typename MatrixType>
MatrixType BuildMatrixRotationWithSale(const AZ::Vector3& angles, float scale)
{
// start a matrix with angle degrees
const AZ::Vector3 eulerRadians = AZ::Vector3DegToRad(angles);
const auto rotX = MatrixType::CreateRotationX(eulerRadians.GetX());
const auto rotY = MatrixType::CreateRotationY(eulerRadians.GetY());
const auto rotZ = MatrixType::CreateRotationZ(eulerRadians.GetZ());
auto matrix = rotX * rotY * rotZ;
// apply a scale
matrix.MultiplyByScale(AZ::Vector3{ scale });
return matrix;
}
template <typename MatrixType>
MatrixType BuildMatrix(const AZ::Vector3& angles, float scale, const AZ::Vector3& translation)
{
auto matrix = BuildMatrixRotationWithSale<MatrixType>(angles, scale);
matrix.SetTranslation(translation);
return matrix;
}
template <>
AZ::Matrix3x3 BuildMatrix(const AZ::Vector3& angles, float scale, const AZ::Vector3&)
{
return BuildMatrixRotationWithSale<AZ::Matrix3x3>(angles, scale);
}
// Arbitrary Matrix
template <typename MatrixType>
MatrixType CreateArbitraryMatrixRotationAndSale(AZ::SimpleLcgRandom& random)
{
// start a matrix with arbitrary degrees
float roll = random.GetRandomFloat() * 360.0f;
float pitch = random.GetRandomFloat() * 360.0f;
float yaw = random.GetRandomFloat() * 360.0f;
const AZ::Vector3 eulerRadians = AZ::Vector3DegToRad(AZ::Vector3{ roll, pitch, yaw });
const auto rotX = MatrixType::CreateRotationX(eulerRadians.GetX());
const auto rotY = MatrixType::CreateRotationY(eulerRadians.GetY());
const auto rotZ = MatrixType::CreateRotationZ(eulerRadians.GetZ());
auto matrix = rotX * rotY * rotZ;
// apply a scale
matrix.MultiplyByScale(AZ::Vector3{ random.GetRandomFloat() });
return matrix;
}
template <typename MatrixType>
void AssignArbitrarySetTranslation(MatrixType& matrix, AZ::SimpleLcgRandom& random)
{
float x = random.GetRandomFloat() * 10000.0f;
float y = random.GetRandomFloat() * 10000.0f;
float z = random.GetRandomFloat() * 10000.0f;
matrix.SetTranslation(AZ::Vector3{ x, y, z });
}
template <typename MatrixType>
MatrixType CreateArbitraryMatrix(size_t seed);
template <>
AZ::Matrix3x3 CreateArbitraryMatrix(size_t seed)
{
AZ::SimpleLcgRandom random(seed);
return CreateArbitraryMatrixRotationAndSale<AZ::Matrix3x3>(random);
}
template <>
AZ::Matrix3x4 CreateArbitraryMatrix(size_t seed)
{
AZ::SimpleLcgRandom random(seed);
auto matrix = CreateArbitraryMatrixRotationAndSale<AZ::Matrix3x4>(random);
AssignArbitrarySetTranslation<AZ::Matrix3x4>(matrix, random);
return matrix;
}
template <>
AZ::Matrix4x4 CreateArbitraryMatrix(size_t seed)
{
AZ::SimpleLcgRandom random(seed);
auto matrix = CreateArbitraryMatrixRotationAndSale<AZ::Matrix4x4>(random);
AssignArbitrarySetTranslation<AZ::Matrix4x4>(matrix, random);
return matrix;
}
// CreateQuaternion
template<typename MatrixType>
AZ::Quaternion CreateQuaternion(const MatrixType& matrix);
template<>
AZ::Quaternion CreateQuaternion<AZ::Matrix3x3>(const AZ::Matrix3x3& matrix)
{
return AZ::Quaternion::CreateFromMatrix3x3(matrix);
}
template<>
AZ::Quaternion CreateQuaternion<AZ::Matrix3x4>(const AZ::Matrix3x4& matrix)
{
return AZ::Quaternion::CreateFromMatrix3x4(matrix);
}
template<>
AZ::Quaternion CreateQuaternion<AZ::Matrix4x4>(const AZ::Matrix4x4& matrix)
{
return AZ::Quaternion::CreateFromMatrix4x4(matrix);
}
template<typename MatrixType>
void AddRotation(rapidjson::Value& value, const MatrixType& matrix, rapidjson::Document::AllocatorType& allocator)
{
AZ::Quaternion rotation = CreateQuaternion<MatrixType>(matrix);
const auto degrees = rotation.GetEulerDegrees();
value.AddMember("yaw", degrees.GetX(), allocator);
value.AddMember("pitch", degrees.GetY(), allocator);
value.AddMember("roll", degrees.GetZ(), allocator);
}
void AddScale(rapidjson::Value& value, float scale, rapidjson::Document::AllocatorType& allocator)
{
value.AddMember("scale", scale, allocator);
}
void AddTranslation(rapidjson::Value& value, const AZ::Vector3& translation, rapidjson::Document::AllocatorType& allocator)
{
value.AddMember("x", translation.GetX(), allocator);
value.AddMember("y", translation.GetY(), allocator);
value.AddMember("z", translation.GetZ(), allocator);
}
template <typename MatrixType>
void AddData(rapidjson::Value& value, const MatrixType& matrix, rapidjson::Document::AllocatorType& allocator);
template <>
void AddData(rapidjson::Value& value, const AZ::Matrix3x3& matrix, rapidjson::Document::AllocatorType& allocator)
{
AddScale(value, matrix.RetrieveScale().GetX(), allocator);
AddRotation(value, matrix, allocator);
}
template <>
void AddData(rapidjson::Value& value, const AZ::Matrix3x4& matrix, rapidjson::Document::AllocatorType& allocator)
{
AddScale(value, matrix.RetrieveScale().GetX(), allocator);
AddTranslation(value, matrix.GetTranslation(), allocator);
AddRotation(value, matrix, allocator);
}
template <>
void AddData(rapidjson::Value& value, const AZ::Matrix4x4& matrix, rapidjson::Document::AllocatorType& allocator)
{
AddScale(value, matrix.RetrieveScale().GetX(), allocator);
AddTranslation(value, matrix.GetTranslation(), allocator);
AddRotation(value, matrix, allocator);
}
};
template<typename MatrixType, size_t RowCount, size_t ColumnCount, typename Serializer>
class MathMatrixSerializerTestDescription :
public JsonSerializerConformityTestDescriptor<MatrixType>
{
public:
AZStd::shared_ptr<AZ::BaseJsonSerializer> CreateSerializer() override
{
return AZStd::make_shared<Serializer>();
}
AZStd::shared_ptr<MatrixType> CreateDefaultInstance() override
{
return AZStd::make_shared<MatrixType>(MatrixType::CreateIdentity());
}
AZStd::shared_ptr<MatrixType> CreateFullySetInstance() override
{
auto angles = AZ::Vector3 { 0.0f, 0.0f, 0.0f };
auto scale = 10.0f;
auto translation = AZ::Vector3{ 10.0f, 20.0f, 30.0f };
auto matrix = DataHelper::BuildMatrix<MatrixType>(angles, scale, translation);
return AZStd::make_shared<MatrixType>(matrix);
}
AZStd::string_view GetJsonForFullySetInstance() override
{
if constexpr (RowCount * ColumnCount == 9)
{
return "{\"roll\":0.0,\"pitch\":0.0,\"yaw\":0.0,\"scale\":10.0}";
}
else if constexpr (RowCount * ColumnCount == 12)
{
return "{\"roll\":0.0,\"pitch\":0.0,\"yaw\":0.0,\"scale\":10.0,\"x\":10.0,\"y\":20.0,\"z\":30.0}";
}
else if constexpr (RowCount * ColumnCount == 16)
{
return "{\"roll\":0.0,\"pitch\":0.0,\"yaw\":0.0,\"scale\":10.0,\"x\":10.0,\"y\":20.0,\"z\":30.0}";
}
else
{
static_assert((RowCount >= 3 && RowCount <= 4) && (ColumnCount >= 3 && ColumnCount <= 4),
"Only matrix 3x3, 3x4 or 4x4 are supported by this test.");
}
return "{}";
}
void ConfigureFeatures(JsonSerializerConformityTestDescriptorFeatures& features) override
{
features.EnableJsonType(rapidjson::kArrayType);
features.EnableJsonType(rapidjson::kObjectType);
features.m_fixedSizeArray = true;
features.m_supportsPartialInitialization = false;
features.m_supportsInjection = false;
}
bool AreEqual(const MatrixType& lhs, const MatrixType& rhs) override
{
for (int r = 0; r < RowCount; ++r)
{
for (int c = 0; c < ColumnCount; ++c)
{
if (!AZ::IsClose(lhs.GetElement(r, c), rhs.GetElement(r, c), AZ::Constants::Tolerance))
{
return false;
}
}
}
return true;
}
};
using MathMatrixSerializerConformityTestTypes = ::testing::Types<
MathMatrixSerializerTestDescription<AZ::Matrix3x3, 3, 3, AZ::JsonMatrix3x3Serializer>,
MathMatrixSerializerTestDescription<AZ::Matrix3x4, 3, 4, AZ::JsonMatrix3x4Serializer>,
MathMatrixSerializerTestDescription<AZ::Matrix4x4, 4, 4, AZ::JsonMatrix4x4Serializer>
>;
INSTANTIATE_TYPED_TEST_CASE_P(JsonMathMatrixSerializer, JsonSerializerConformityTests, MathMatrixSerializerConformityTestTypes);
template<typename T>
class JsonMathMatrixSerializerTests
: public BaseJsonSerializerFixture
{
public:
using Descriptor = T;
void SetUp() override
{
BaseJsonSerializerFixture::SetUp();
m_serializer = AZStd::make_unique<typename T::Serializer>();
}
void TearDown() override
{
m_serializer.reset();
BaseJsonSerializerFixture::TearDown();
}
protected:
AZStd::unique_ptr<typename T::Serializer> m_serializer;
};
struct Matrix3x3Descriptor
{
using MatrixType = AZ::Matrix3x3;
using Serializer = AZ::JsonMatrix3x3Serializer;
constexpr static size_t RowCount = 3;
constexpr static size_t ColumnCount = 3;
constexpr static size_t ElementCount = RowCount * ColumnCount;
constexpr static bool HasTranslation = false;
};
struct Matrix3x4Descriptor
{
using MatrixType = AZ::Matrix3x4;
using Serializer = AZ::JsonMatrix3x4Serializer;
constexpr static size_t RowCount = 3;
constexpr static size_t ColumnCount = 4;
constexpr static size_t ElementCount = RowCount * ColumnCount;
constexpr static bool HasTranslation = true;
};
struct Matrix4x4Descriptor
{
using MatrixType = AZ::Matrix4x4;
using Serializer = AZ::JsonMatrix4x4Serializer;
constexpr static size_t RowCount = 4;
constexpr static size_t ColumnCount = 4;
constexpr static size_t ElementCount = RowCount * ColumnCount;
constexpr static bool HasTranslation = true;
};
using JsonMathMatrixSerializerTypes = ::testing::Types <
Matrix3x3Descriptor, Matrix3x4Descriptor, Matrix4x4Descriptor>;
TYPED_TEST_CASE(JsonMathMatrixSerializerTests, JsonMathMatrixSerializerTypes);
// Load array tests
TYPED_TEST(JsonMathMatrixSerializerTests, Load_Array_ReturnsConvertAndLoadsMatrix)
{
using namespace AZ::JsonSerializationResult;
rapidjson::Value& arrayValue = this->m_jsonDocument->SetArray();
for (size_t i = 0; i < JsonMathMatrixSerializerTests<TypeParam>::Descriptor::ElementCount; ++i)
{
arrayValue.PushBack(static_cast<float>(i + 1), this->m_jsonDocument->GetAllocator());
}
auto output = JsonMathMatrixSerializerTests<TypeParam>::Descriptor::MatrixType::CreateZero();
ResultCode result = this->m_serializer->Load(
&output,
azrtti_typeid<typename JsonMathMatrixSerializerTests<TypeParam>::Descriptor::MatrixType>(),
*this->m_jsonDocument,
*this->m_jsonDeserializationContext);
ASSERT_EQ(Outcomes::Success, result.GetOutcome());
for (int r = 0; r < JsonMathMatrixSerializerTests<TypeParam>::Descriptor::RowCount; ++r)
{
for (int c = 0; c < JsonMathMatrixSerializerTests<TypeParam>::Descriptor::ColumnCount; ++c)
{
auto testValue = static_cast<float>((r * JsonMathMatrixSerializerTests<TypeParam>::Descriptor::ColumnCount) + c + 1);
EXPECT_FLOAT_EQ(testValue, output.GetElement(r, c));
}
}
}
TYPED_TEST(JsonMathMatrixSerializerTests, Load_InvalidEntries_ReturnsUnsupportedAndLeavesMatrixUntouched)
{
using namespace AZ::JsonSerializationResult;
rapidjson::Value& arrayValue = this->m_jsonDocument->SetArray();
for (size_t i = 0; i < JsonMathMatrixSerializerTests<TypeParam>::Descriptor::ElementCount; ++i)
{
if (i == 1)
{
arrayValue.PushBack(rapidjson::StringRef("Invalid"), this->m_jsonDocument->GetAllocator());
}
else
{
arrayValue.PushBack(static_cast<float>(i + 1), this->m_jsonDocument->GetAllocator());
}
}
auto output = JsonMathMatrixSerializerTests<TypeParam>::Descriptor::MatrixType::CreateZero();
ResultCode result = this->m_serializer->Load(
&output,
azrtti_typeid<typename JsonMathMatrixSerializerTests<TypeParam>::Descriptor::MatrixType>(),
*this->m_jsonDocument,
*this->m_jsonDeserializationContext);
EXPECT_EQ(Outcomes::Unsupported, result.GetOutcome());
for (int r = 0; r < JsonMathMatrixSerializerTests<TypeParam>::Descriptor::RowCount; ++r)
{
for (int c = 0; c < JsonMathMatrixSerializerTests<TypeParam>::Descriptor::ColumnCount; ++c)
{
EXPECT_FLOAT_EQ(0.0f, output.GetElement(r, c));
}
}
}
TYPED_TEST(JsonMathMatrixSerializerTests, Load_FloatSerializerMissingForArray_ReturnsCatastrophic)
{
using namespace AZ::JsonSerializationResult;
this->m_jsonRegistrationContext->EnableRemoveReflection();
this->m_jsonRegistrationContext->template Serializer<AZ::JsonFloatSerializer>()->template HandlesType<float>();
this->m_jsonRegistrationContext->DisableRemoveReflection();
rapidjson::Value& arrayValue = this->m_jsonDocument->SetArray();
for (size_t i = 0; i < JsonMathMatrixSerializerTests<TypeParam>::Descriptor::ElementCount + 1; ++i)
{
arrayValue.PushBack(static_cast<float>(i + 1), this->m_jsonDocument->GetAllocator());
}
typename JsonMathMatrixSerializerTests<TypeParam>::Descriptor::MatrixType output;
ResultCode result = this->m_serializer->Load(
&output,
azrtti_typeid<typename JsonMathMatrixSerializerTests<TypeParam>::Descriptor::MatrixType>(),
*this->m_jsonDocument,
*this->m_jsonDeserializationContext);
EXPECT_EQ(Outcomes::Catastrophic, result.GetOutcome());
this->m_jsonRegistrationContext->template Serializer<AZ::JsonFloatSerializer>()->template HandlesType<float>();
}
// Load object tests
TYPED_TEST(JsonMathMatrixSerializerTests, Load_ValidObjectLowerCase_ReturnsSuccessAndLoadsMatrix)
{
using namespace AZ::JsonSerializationResult;
rapidjson::Value& objectValue = this->m_jsonDocument->SetObject();
auto input = JsonMathMatrixSerializerTests<TypeParam>::Descriptor::MatrixType::CreateIdentity();
DataHelper::AddData(objectValue, input, this->m_jsonDocument->GetAllocator());
auto output = JsonMathMatrixSerializerTests<TypeParam>::Descriptor::MatrixType::CreateZero();
ResultCode result = this->m_serializer->Load(
&output,
azrtti_typeid<typename JsonMathMatrixSerializerTests<TypeParam>::Descriptor::MatrixType>(),
*this->m_jsonDocument,
*this->m_jsonDeserializationContext);
ASSERT_EQ(Outcomes::DefaultsUsed, result.GetOutcome());
EXPECT_TRUE(input == output);
}
TYPED_TEST(JsonMathMatrixSerializerTests, Load_ValidObjectWithExtraFields_ReturnsPartialConvertAndLoadsMatrix)
{
using namespace AZ::JsonSerializationResult;
rapidjson::Value& objectValue = this->m_jsonDocument->SetObject();
auto input = JsonMathMatrixSerializerTests<TypeParam>::Descriptor::MatrixType::CreateIdentity();
DataHelper::AddScale(objectValue, input.RetrieveScale().GetX(), this->m_jsonDocument->GetAllocator());
DataHelper::AddRotation(objectValue, input, this->m_jsonDocument->GetAllocator());
objectValue.AddMember(rapidjson::StringRef("extra"), "no value", this->m_jsonDocument->GetAllocator());
auto output = JsonMathMatrixSerializerTests<TypeParam>::Descriptor::MatrixType::CreateZero();
ResultCode result = this->m_serializer->Load(
&output,
azrtti_typeid<typename JsonMathMatrixSerializerTests<TypeParam>::Descriptor::MatrixType>(),
*this->m_jsonDocument,
*this->m_jsonDeserializationContext);
ASSERT_EQ(Outcomes::DefaultsUsed, result.GetOutcome());
EXPECT_TRUE(input == output);
}
TYPED_TEST(JsonMathMatrixSerializerTests, SaveLoad_Identity_LoadsDefaultMatrixWithIdentity)
{
using namespace AZ::JsonSerializationResult;
auto defaultValue = JsonMathMatrixSerializerTests<TypeParam>::Descriptor::MatrixType::CreateIdentity();
rapidjson::Value& objectInput = this->m_jsonDocument->SetObject();
this->m_serializer->Store(
objectInput,
&defaultValue,
&defaultValue,
azrtti_typeid<typename JsonMathMatrixSerializerTests<TypeParam>::Descriptor::MatrixType>(),
*this->m_jsonSerializationContext);
rapidjson::StringBuffer buffer;
rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
objectInput.Accept(writer);
auto output = defaultValue;
ResultCode result = this->m_serializer->Load(
&output,
azrtti_typeid<typename JsonMathMatrixSerializerTests<TypeParam>::Descriptor::MatrixType>(),
*this->m_jsonDocument,
*this->m_jsonDeserializationContext);
EXPECT_TRUE(defaultValue == output);
}
TYPED_TEST(JsonMathMatrixSerializerTests, LoadSave_Zero_SavesAndLoadsIdentityMatrix)
{
using namespace AZ::JsonSerializationResult;
auto defaultValue = JsonMathMatrixSerializerTests<TypeParam>::Descriptor::MatrixType::CreateIdentity();
auto input = JsonMathMatrixSerializerTests<TypeParam>::Descriptor::MatrixType::CreateZero();
rapidjson::Value& objectInput = this->m_jsonDocument->SetObject();
this->m_serializer->Store(
objectInput,
&input,
&defaultValue,
azrtti_typeid<typename JsonMathMatrixSerializerTests<TypeParam>::Descriptor::MatrixType>(),
*this->m_jsonSerializationContext);
auto output = defaultValue;
ResultCode result = this->m_serializer->Load(
&output,
azrtti_typeid<typename JsonMathMatrixSerializerTests<TypeParam>::Descriptor::MatrixType>(),
*this->m_jsonDocument,
*this->m_jsonDeserializationContext);
ASSERT_EQ(Outcomes::Unsupported, result.GetOutcome());
EXPECT_TRUE(defaultValue == output);
}
TYPED_TEST(JsonMathMatrixSerializerTests, Load_InvalidFields_ReturnsUnsupportedAndLeavesMatrixUntouched)
{
using namespace AZ::JsonSerializationResult;
using Descriptor = typename JsonMathMatrixSerializerTests<TypeParam>::Descriptor;
const auto defaultValue = Descriptor::MatrixType::CreateIdentity();
rapidjson::Value& objectValue = this->m_jsonDocument->SetObject();
auto input = Descriptor::MatrixType::CreateIdentity();
DataHelper::AddData(objectValue, input, this->m_jsonDocument->GetAllocator());
objectValue["yaw"] = "Invalid";
auto output = Descriptor::MatrixType::CreateZero();
ResultCode result = this->m_serializer->Load(
&output,
azrtti_typeid<typename Descriptor::MatrixType>(),
*this->m_jsonDocument,
*this->m_jsonDeserializationContext);
ASSERT_EQ(Outcomes::Unsupported, result.GetOutcome());
EXPECT_TRUE(input == output);
}
TYPED_TEST(JsonMathMatrixSerializerTests, LoadSave_Arbitrary_SavesAndLoadsArbitraryMatrix)
{
using namespace AZ::JsonSerializationResult;
using Descriptor = typename JsonMathMatrixSerializerTests<TypeParam>::Descriptor;
auto defaultValue = Descriptor::MatrixType::CreateIdentity();
size_t elementCount = Descriptor::RowCount * Descriptor::ColumnCount;
auto input = DataHelper::CreateArbitraryMatrix<typename Descriptor::MatrixType>(elementCount);
rapidjson::Value& objectInput = this->m_jsonDocument->SetObject();
this->m_serializer->Store(
objectInput,
&input,
&defaultValue,
azrtti_typeid<typename Descriptor::MatrixType>(),
*this->m_jsonSerializationContext);
auto output = defaultValue;
ResultCode result = this->m_serializer->Load(
&output,
azrtti_typeid<typename Descriptor::MatrixType>(),
*this->m_jsonDocument,
*this->m_jsonDeserializationContext);
EXPECT_EQ(Processing::Completed, result.GetProcessing());
for (int r = 0; r < Descriptor::RowCount; ++r)
{
for (int c = 0; c < Descriptor::ColumnCount; ++c)
{
EXPECT_NEAR(input.GetElement(r, c), output.GetElement(r, c), AZ::Constants::Tolerance);
}
}
}
} // namespace JsonSerializationTests
@@ -32,6 +32,11 @@ namespace JsonSerializationTests
return AZStd::make_shared<AZ::JsonSmartPointerSerializer>();
}
AZStd::shared_ptr<SmartPointer> CreateDefaultConstructedInstance() override
{
return AZStd::make_shared<SmartPointer>();
}
void Reflect(AZStd::unique_ptr<AZ::SerializeContext>& context) override
{
context->RegisterGenericType<SmartPointer>();
@@ -228,13 +233,19 @@ namespace JsonSerializationTests
public:
using SmartPointer = typename SmartPointerSimpleDerivedClassTestDescription<T>::SmartPointer;
AZStd::shared_ptr<SmartPointer> CreateDefaultInstance() override
// This test is specific for derived classes being used as a default value.
AZStd::shared_ptr<SmartPointer> CreateDefaultConstructedInstance() override
{
auto result = AZStd::make_shared<SmartPointer>();
*result = SmartPointer(aznew SimpleInheritence());
return result;
}
AZStd::shared_ptr<SmartPointer> CreateDefaultInstance() override
{
return CreateDefaultConstructedInstance();
}
AZStd::string_view GetJsonForPartialDefaultInstance() override
{
return R"(
@@ -386,13 +397,19 @@ namespace JsonSerializationTests
public:
using SmartPointer = typename SmartPointerComplexDerivedClassTestDescription<T>::SmartPointer;
AZStd::shared_ptr<SmartPointer> CreateDefaultInstance() override
// This test is specific for derived classes being used as a default value.
AZStd::shared_ptr<SmartPointer> CreateDefaultConstructedInstance() override
{
auto result = AZStd::make_shared<SmartPointer>();
*result = SmartPointer(aznew MultipleInheritence());
return result;
}
AZStd::shared_ptr<SmartPointer> CreateDefaultInstance() override
{
return CreateDefaultConstructedInstance();
}
AZStd::string_view GetJsonForPartialDefaultInstance() override
{
return R"(
@@ -111,6 +111,7 @@ set(FILES
Serialization/Json/JsonSerializerMock.h
Serialization/Json/MapSerializerTests.cpp
Serialization/Json/MathVectorSerializerTests.cpp
Serialization/Json/MathMatrixSerializerTests.cpp
Serialization/Json/SmartPointerSerializerTests.cpp
Serialization/Json/StringSerializerTests.cpp
Serialization/Json/TestCases.h
@@ -36,6 +36,8 @@ namespace AzFramework
void NonUniformScaleComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
{
incompatible.push_back(AZ_CRC_CE("NonUniformScaleService"));
incompatible.push_back(AZ_CRC_CE("DebugDrawObbService"));
incompatible.push_back(AZ_CRC_CE("DebugDrawService"));
incompatible.push_back(AZ_CRC_CE("EMotionFXActorService"));
@@ -19,44 +19,29 @@
namespace AzPhysics
{
struct SimulatedBody;
}
namespace Physics
{
//! Requests for generic physical world bodies
class WorldBodyRequests
//! Requests for physics simulated body components.
class SimulatedBodyComponentRequests
: public AZ::ComponentBus
{
public:
using MutexType = AZStd::recursive_mutex;
//! Enable physics for this body
//! Enable physics for this body.
virtual void EnablePhysics() = 0;
//! Disable physics for this body
//! Disable physics for this body.
virtual void DisablePhysics() = 0;
//! Retrieve whether physics is enabled for this body
//! Retrieve whether physics is enabled for this body.
virtual bool IsPhysicsEnabled() const = 0;
//! Retrieves the AABB(aligned-axis bounding box) for this body
//! Retrieves the AABB(aligned-axis bounding box) for this body.
virtual AZ::Aabb GetAabb() const = 0;
//! Retrieves current WorldBody* for this body. Note: Do not hold a reference to AzPhysics::SimulatedBody* as could be deleted
virtual AzPhysics::SimulatedBody* GetWorldBody() = 0;
//! Perform a single-object raycast against this body
//! Get the Simulated Body Handle for this body.
virtual AzPhysics::SimulatedBodyHandle GetSimulatedBodyHandle() const = 0;
//! Retrieves current WorldBody* for this body.
//! @note Do not hold a reference to AzPhysics::SimulatedBody* as it could be deleted or moved.
virtual AzPhysics::SimulatedBody* GetSimulatedBody() = 0;
//! Perform a single-object raycast against this body.
virtual AzPhysics::SceneQueryHit RayCast(const AzPhysics::RayCastRequest& request) = 0;
};
using WorldBodyRequestBus = AZ::EBus<WorldBodyRequests>;
//! Notifications for generic physical world bodies
class WorldBodyNotifications
: public AZ::ComponentBus
{
public:
//! Notification for physics enabled
virtual void OnPhysicsEnabled() = 0;
//! Notification for physics disabled
virtual void OnPhysicsDisabled() = 0;
};
using WorldBodyNotificationBus = AZ::EBus<WorldBodyNotifications>;
using SimulatedBodyComponentRequestsBus = AZ::EBus<SimulatedBodyComponentRequests>;
}
@@ -48,6 +48,9 @@ namespace Physics
{
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext
->RegisterGenericType<AZStd::shared_ptr<SphereShapeConfiguration>>();
serializeContext->Class<SphereShapeConfiguration, ShapeConfiguration>()
->Version(1)
->Field("Radius", &SphereShapeConfiguration::m_radius)
@@ -76,6 +79,9 @@ namespace Physics
{
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext
->RegisterGenericType<AZStd::shared_ptr<BoxShapeConfiguration>>();
serializeContext->Class<BoxShapeConfiguration, ShapeConfiguration>()
->Version(1)
->Field("Configuration", &BoxShapeConfiguration::m_dimensions)
@@ -104,6 +110,9 @@ namespace Physics
{
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext
->RegisterGenericType<AZStd::shared_ptr<CapsuleShapeConfiguration>>();
serializeContext->Class<CapsuleShapeConfiguration, ShapeConfiguration>()
->Version(1)
->Field("Height", &CapsuleShapeConfiguration::m_height)
@@ -153,6 +162,9 @@ namespace Physics
{
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext
->RegisterGenericType<AZStd::shared_ptr<PhysicsAssetShapeConfiguration>>();
serializeContext->Class<PhysicsAssetShapeConfiguration, ShapeConfiguration>()
->Version(1)
->Field("PhysicsAsset", &PhysicsAssetShapeConfiguration::m_asset)
@@ -185,6 +197,9 @@ namespace Physics
{
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext
->RegisterGenericType<AZStd::shared_ptr<NativeShapeConfiguration>>();
serializeContext->Class<NativeShapeConfiguration, ShapeConfiguration>()
->Version(1)
->Field("Scale", &NativeShapeConfiguration::m_nativeShapeScale)
@@ -208,6 +223,9 @@ namespace Physics
{
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext
->RegisterGenericType<AZStd::shared_ptr<CookedMeshShapeConfiguration>>();
serializeContext->Class<CookedMeshShapeConfiguration, ShapeConfiguration>()
->Version(1)
->Field("CookedData", &CookedMeshShapeConfiguration::m_cookedData)
@@ -21,7 +21,7 @@
#include <AzFramework/Physics/ShapeConfiguration.h>
#include <AzCore/Serialization/EditContext.h>
#include <AzFramework/Physics/CollisionBus.h>
#include <AzFramework/Physics/WorldBodyBus.h>
#include <AzFramework/Physics/Components/SimulatedBodyComponentBus.h>
#include <AzFramework/Physics/WindBus.h>
#include <AzFramework/Physics/PhysicsSystem.h>
#include <AzFramework/Physics/Collision/CollisionEvents.h>
@@ -39,19 +39,19 @@ namespace Physics
{
namespace ReflectionUtils
{
void ReflectWorldBodyBus(AZ::ReflectContext* context)
void ReflectSimulatedBodyComponentRequestsBus(AZ::ReflectContext* context)
{
if (auto* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
{
behaviorContext->EBus<Physics::WorldBodyRequestBus>("WorldBodyRequestBus")
behaviorContext->EBus<AzPhysics::SimulatedBodyComponentRequestsBus>("SimulatedBodyComponentRequestBus")
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
->Attribute(AZ::Script::Attributes::Module, "physics")
->Attribute(AZ::Script::Attributes::Category, "PhysX")
->Event("EnablePhysics", &WorldBodyRequests::EnablePhysics)
->Event("DisablePhysics", &WorldBodyRequests::DisablePhysics)
->Event("IsPhysicsEnabled", &WorldBodyRequests::IsPhysicsEnabled)
->Event("GetAabb", &WorldBodyRequests::GetAabb)
->Event("RayCast", &WorldBodyRequests::RayCast)
->Event("EnablePhysics", &AzPhysics::SimulatedBodyComponentRequests::EnablePhysics)
->Event("DisablePhysics", &AzPhysics::SimulatedBodyComponentRequests::DisablePhysics)
->Event("IsPhysicsEnabled", &AzPhysics::SimulatedBodyComponentRequests::IsPhysicsEnabled)
->Event("GetAabb", &AzPhysics::SimulatedBodyComponentRequests::GetAabb)
->Event("RayCast", &AzPhysics::SimulatedBodyComponentRequests::RayCast)
;
}
}
@@ -131,7 +131,7 @@ namespace Physics
AnimationConfiguration::Reflect(context);
CharacterConfiguration::Reflect(context);
AzPhysics::SimulatedBody::Reflect(context);
ReflectWorldBodyBus(context);
ReflectSimulatedBodyComponentRequestsBus(context);
CollisionFilteringRequests::Reflect(context);
AzPhysics::SceneQuery::ReflectSceneQueryObjects(context);
ReflectWindBus(context);
@@ -48,15 +48,21 @@ namespace AzFramework
};
//! The interface used by MultiViewportController to manage individual instances.
template <class TController>
class MultiViewportControllerInstanceInterface
{
public:
explicit MultiViewportControllerInstanceInterface(ViewportId viewport)
using ControllerType = TController;
MultiViewportControllerInstanceInterface(ViewportId viewport, ControllerType* controller)
: m_viewportId(viewport)
, m_controller(controller)
{
}
ViewportId GetViewportId() const { return m_viewportId; }
ControllerType* GetController() { return m_controller; }
const ControllerType* GetController() const { return m_controller; }
virtual bool HandleInputChannelEvent([[maybe_unused]]const ViewportControllerInputEvent& event) { return false; }
virtual void ResetInputChannels() {}
@@ -64,6 +70,7 @@ namespace AzFramework
private:
ViewportId m_viewportId;
ControllerType* m_controller;
};
} //namespace AzFramework
@@ -17,8 +17,8 @@ namespace AzFramework
MultiViewportController<TViewportControllerInstance, Priority>::~MultiViewportController()
{
static_assert(
AZStd::is_constructible<TViewportControllerInstance, ViewportId>::value,
"TViewportControllerInstance must implement a TViewportControllerInstance(ViewportId) constructor"
AZStd::is_same<TViewportControllerInstance, decltype(TViewportControllerInstance(0, nullptr))>::value,
"TViewportControllerInstance must implement a TViewportControllerInstance(ViewportId, ViewportController) constructor"
);
}
@@ -50,7 +50,7 @@ namespace AzFramework
template <class TViewportControllerInstance, ViewportControllerPriority Priority>
void MultiViewportController<TViewportControllerInstance, Priority>::RegisterViewportContext(ViewportId viewport)
{
m_instances[viewport] = AZStd::make_unique<TViewportControllerInstance>(viewport);
m_instances[viewport] = AZStd::make_unique<TViewportControllerInstance>(viewport, static_cast<typename TViewportControllerInstance::ControllerType*>(this));
}
template <class TViewportControllerInstance, ViewportControllerPriority Priority>
@@ -213,6 +213,12 @@ set(FILES
StreamingInstall/StreamingInstall.cpp
StreamingInstall/StreamingInstallRequests.h
StreamingInstall/StreamingInstallNotifications.h
Physics/Collision/CollisionEvents.h
Physics/Collision/CollisionEvents.cpp
Physics/Collision/CollisionLayers.h
Physics/Collision/CollisionLayers.cpp
Physics/Collision/CollisionGroups.h
Physics/Collision/CollisionGroups.cpp
Physics/Common/PhysicsSceneQueries.h
Physics/Common/PhysicsSceneQueries.cpp
Physics/Common/PhysicsEvents.h
@@ -223,12 +229,7 @@ set(FILES
Physics/Common/PhysicsSimulatedBodyEvents.h
Physics/Common/PhysicsSimulatedBodyEvents.cpp
Physics/Common/PhysicsTypes.h
Physics/Collision/CollisionEvents.h
Physics/Collision/CollisionEvents.cpp
Physics/Collision/CollisionLayers.h
Physics/Collision/CollisionLayers.cpp
Physics/Collision/CollisionGroups.h
Physics/Collision/CollisionGroups.cpp
Physics/Components/SimulatedBodyComponentBus.h
Physics/Configuration/CollisionConfiguration.h
Physics/Configuration/CollisionConfiguration.cpp
Physics/Configuration/RigidBodyConfiguration.h
@@ -265,7 +266,6 @@ set(FILES
Physics/ShapeConfiguration.h
Physics/ShapeConfiguration.cpp
Physics/SystemBus.h
Physics/WorldBodyBus.h
Physics/ColliderComponentBus.h
Physics/RagdollPhysicsBus.h
Physics/CharacterPhysicsDataBus.h
@@ -0,0 +1,78 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
// Description : Utilities for iOS and Mac OS X. Needs to be separated
// due to conflict with the system headers.
#ifndef CRYINCLUDE_CRYSYSTEM_SYSTEMUTILSAPPLE_H
#define CRYINCLUDE_CRYSYSTEM_SYSTEMUTILSAPPLE_H
#pragma once
#include <sys/resource.h>
#include <sys/types.h>
#include <AzCore/std/string/string.h>
namespace SystemUtilsApple
{
// Get the path to the application's bundle.
// - Returns length of the string or 0 on failure.
size_t GetPathToApplicationBundle(char* buffer, size_t bufferLen);
// Get the path to the application's executable.
// - Returns length of the string or 0 on failure.
size_t GetPathToApplicationExecutable(char* buffer, size_t bufferLen);
// Get the path to the application's resources.
// - Returns length of the string or 0 on failure.
size_t GetPathToApplicationResources(char* buffer, size_t bufferLen);
// Get the path to the user domain's application support directory.
// - Returns length of the string or 0 on failure.
// - Used to store application generated content.
// - iOS: Not available through file sharing.
// - Persistent, backed up by iTunes.
size_t GetPathToUserApplicationSupportDirectory(char* buffer, size_t bufferLen);
// Get the path to the user domain's caches directory.
// - Returns length of the string or 0 on failure.
// - Used to store application generated content.
// - iOS: Not available through file sharing.
// - Temporary, not backed up by iTunes.
size_t GetPathToUserCachesDirectory(char* buffer, size_t bufferLen);
// Get the path to the user domain's document directory.
// - Returns length of the string or 0 on failure.
// - Used to store user generated content.
// - iOS: Available through file sharing.
// - Persistent, backed up by iTunes.
size_t GetPathToUserDocumentDirectory(char* buffer, size_t bufferLen);
// Get the path to the user domain's library directory.
// - Returns length of the string or 0 on failure.
// - Parent directory of app support and caches.
// - iOS: Not available through file sharing.
// - Persistent, backed up by iTunes.
size_t GetPathToUserLibraryDirectory(char* buffer, size_t bufferLen);
// Get the user's name.
// - Returns length of the string or 0 on failure.
size_t GetUserName(char* buffer, size_t bufferLen);
// Get the device's machine name
// - Returns string representing device identifier or empty string on failure
// - Unique for each model
AZStd::string GetMachineName();
}
#endif // CRYINCLUDE_CRYSYSTEM_SYSTEMUTILSAPPLE_H
@@ -0,0 +1,131 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#include "SystemUtilsApple.h"
#include <AzCore/base.h>
#include <AzCore/Debug/Trace.h>
#include <Foundation/Foundation.h>
#include <mach-o/dyld.h>
#include <pthread.h>
#include <sys/utsname.h>
////////////////////////////////////////////////////////////////////////////////////////////////////
namespace SystemUtilsApplePrivate
{
////////////////////////////////////////////////////////////////////////////////////////////////
// Performs a 'safe' string copy from 'NString* source' to 'char* buffer' of 'size_t bufferLen'.
// Returns the length of the string, or 0 if the buffer is not large enough to hold the source.
// Copying an empty or null string will return 0, and null-terminate the buffer if possible.
////////////////////////////////////////////////////////////////////////////////////////////////
size_t CopyNSStringToBuffer(NSString* source, char* buffer, const size_t bufferLen)
{
if (!buffer || !bufferLen)
{
return 0;
}
if (source)
{
const char* src = [source UTF8String];
const size_t srcLen = strlen(src);
if (srcLen < bufferLen - 1)
{
azstrncpy(buffer, bufferLen, src, srcLen);
buffer[srcLen] = '\0';
return srcLen;
}
}
// Could not copy the source to the destination buffer.
buffer[0] = '\0';
return 0;
}
////////////////////////////////////////////////////////////////////////////////////////////////
// Get the path to the specified user domain directory.
// Returns length of the string or 0 on failure.
////////////////////////////////////////////////////////////////////////////////////////////////
size_t GetPathToUserDirectory(NSSearchPathDirectory dir, char* buffer, const size_t bufferLen)
{
NSArray* userDomainDirectoryPaths = NSSearchPathForDirectoriesInDomains(dir, NSUserDomainMask, YES);
if ([userDomainDirectoryPaths count] != 0)
{
NSString* userDomainDirectoryPath = static_cast<NSString*>([userDomainDirectoryPaths objectAtIndex:0]);
return CopyNSStringToBuffer(userDomainDirectoryPath, buffer, bufferLen);
}
return 0;
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
size_t SystemUtilsApple::GetPathToApplicationBundle(char* buffer, const size_t bufferLen)
{
NSString* bundlePath = [[NSBundle mainBundle] bundlePath];
return SystemUtilsApplePrivate::CopyNSStringToBuffer(bundlePath, buffer, bufferLen);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
size_t SystemUtilsApple::GetPathToApplicationExecutable(char* buffer, const size_t bufferLen)
{
NSString* executablePath = [[NSBundle mainBundle] executablePath];
return SystemUtilsApplePrivate::CopyNSStringToBuffer(executablePath, buffer, bufferLen);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
size_t SystemUtilsApple::GetPathToApplicationResources(char* buffer, const size_t bufferLen)
{
NSString* resourcesPath = [[NSBundle mainBundle] resourcePath];
return SystemUtilsApplePrivate::CopyNSStringToBuffer(resourcesPath, buffer, bufferLen);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
size_t SystemUtilsApple::GetPathToUserApplicationSupportDirectory(char* buffer, const size_t bufferLen)
{
return SystemUtilsApplePrivate::GetPathToUserDirectory(NSApplicationSupportDirectory, buffer, bufferLen);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
size_t SystemUtilsApple::GetPathToUserCachesDirectory(char* buffer, const size_t bufferLen)
{
return SystemUtilsApplePrivate::GetPathToUserDirectory(NSCachesDirectory, buffer, bufferLen);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
size_t SystemUtilsApple::GetPathToUserDocumentDirectory(char* buffer, const size_t bufferLen)
{
return SystemUtilsApplePrivate::GetPathToUserDirectory(NSDocumentDirectory, buffer, bufferLen);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
size_t SystemUtilsApple::GetPathToUserLibraryDirectory(char* buffer, const size_t bufferLen)
{
return SystemUtilsApplePrivate::GetPathToUserDirectory(NSLibraryDirectory, buffer, bufferLen);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
size_t SystemUtilsApple::GetUserName(char* buffer, const size_t bufferLen)
{
return SystemUtilsApplePrivate::CopyNSStringToBuffer(NSUserName(), buffer, bufferLen);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
AZStd::string SystemUtilsApple::GetMachineName()
{
utsname systemInfo;
if (uname(&systemInfo) == -1)
{
return "";
}
return systemInfo.machine;
}
@@ -0,0 +1,16 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#pragma once
#include "../../../Common/Apple/AzFramework/Utils/SystemUtilsApple.h"
@@ -36,4 +36,6 @@ set(FILES
../Common/Unimplemented/AzFramework/Input/Devices/VirtualKeyboard/InputDeviceVirtualKeyboard_Unimplemented.cpp
AzFramework/Archive/ArchiveVars_Platform.h
AzFramework/Archive/ArchiveVars_Mac.h
../Common/Apple/AzFramework/Utils/SystemUtilsApple.h
../Common/Apple/AzFramework/Utils/SystemUtilsApple.mm
)
@@ -0,0 +1,15 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include "../../../Common/Apple/AzFramework/Utils/SystemUtilsApple.h"
@@ -36,5 +36,7 @@ set(FILES
AzFramework/Process/ProcessCommon.h
AzFramework/Process/ProcessWatcher_iOS.cpp
AzFramework/Process/ProcessCommunicator_iOS.cpp
../Common/Apple/AzFramework/Utils/SystemUtilsApple.h
../Common/Apple/AzFramework/Utils/SystemUtilsApple.mm
)
@@ -31,6 +31,10 @@ namespace AzToolsFramework
//! Allows a component to get the list of selected entities
//! \param selectedEntityIds the return vector holding the entities required
virtual void GetSelectedEntities(EntityIdList& selectedEntityIds) = 0;
//! Explicitly sets a component as having been the most recently added.
//! This means that the next time the UI refreshes, that component will be ensured to be visible.
virtual void SetNewComponentId(AZ::ComponentId componentId) = 0;
};
using EntityPropertyEditorRequestBus = AZ::EBus<EntityPropertyEditorRequests>;
@@ -39,9 +39,10 @@ namespace AzToolsFramework
editContext->Class<EditorNonUniformScaleComponent>("Non-uniform Scale",
"Non-uniform scale for this entity only (does not propagate through hierarchy)")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::Category, "Non-uniform Scale")
->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC_CE("Game"))
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
->Attribute(AZ::Edit::Attributes::FixedComponentListIndex, 1)
->Attribute(AZ::Edit::Attributes::RemoveableByUser, true)
->Attribute(AZ::Edit::Attributes::Icon, "Icons/Components/NonUniformScale.svg")
->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/NonUniformScale.svg")
->DataElement(
AZ::Edit::UIHandlers::Default, &EditorNonUniformScaleComponent::m_scale, "Non-uniform Scale",
"Non-uniform scale for this entity only (does not propagate through hierarchy)")
@@ -61,6 +62,8 @@ namespace AzToolsFramework
void EditorNonUniformScaleComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
{
incompatible.push_back(AZ_CRC_CE("NonUniformScaleService"));
incompatible.push_back(AZ_CRC_CE("DebugDrawObbService"));
incompatible.push_back(AZ_CRC_CE("DebugDrawService"));
incompatible.push_back(AZ_CRC_CE("EMotionFXActorService"));
@@ -25,11 +25,15 @@
#include <AzCore/Serialization/SerializeContext.h>
#include <AzFramework/API/ApplicationAPI.h>
#include <AzFramework/Components/TransformComponent.h>
#include <AzToolsFramework/API/EntityCompositionRequestBus.h>
#include <AzToolsFramework/API/EntityPropertyEditorRequestsBus.h>
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
#include <AzToolsFramework/Entity/EditorEntityContextBus.h>
#include <AzToolsFramework/Prefab/PrefabPublicInterface.h>
#include <AzToolsFramework/ToolsComponents/TransformComponentBus.h>
#include <AzToolsFramework/ToolsComponents/TransformScalePropertyHandler.h>
#include <AzToolsFramework/ToolsComponents/EditorInspectorComponentBus.h>
#include <AzToolsFramework/ToolsComponents/EditorPendingCompositionBus.h>
#include <AzToolsFramework/ViewportSelection/EditorSelectionUtil.h>
#include <AzToolsFramework/Viewport/ViewportMessages.h>
@@ -1196,6 +1200,66 @@ namespace AzToolsFramework
destinationComponent->SetWorldTM(const_cast<TransformComponent*>(sourceComponent)->GetWorldTM());
}
AZ::Component* TransformComponent::FindPresentOrPendingComponent(AZ::Uuid componentUuid)
{
// first check if the component is present and valid
if (AZ::Component* foundComponent = GetEntity()->FindComponent(componentUuid))
{
return foundComponent;
}
// then check to see if there's a component pending because it's in an invalid state
AZStd::vector<AZ::Component*> pendingComponents;
AzToolsFramework::EditorPendingCompositionRequestBus::Event(GetEntityId(),
&AzToolsFramework::EditorPendingCompositionRequests::GetPendingComponents, pendingComponents);
for (const auto pendingComponent : pendingComponents)
{
if (pendingComponent->RTTI_IsTypeOf(componentUuid))
{
return pendingComponent;
}
}
return nullptr;
}
bool TransformComponent::IsAddNonUniformScaleButtonReadOnly()
{
return FindPresentOrPendingComponent(EditorNonUniformScaleComponent::TYPEINFO_Uuid()) != nullptr;
}
AZ::Crc32 TransformComponent::OnAddNonUniformScaleButtonPressed()
{
// if there is already a non-uniform scale component, do nothing
if (FindPresentOrPendingComponent(EditorNonUniformScaleComponent::TYPEINFO_Uuid()))
{
return AZ::Edit::PropertyRefreshLevels::None;
}
const AZStd::vector<AZ::EntityId> entityList = { GetEntityId() };
const AZ::ComponentTypeList componentsToAdd = { EditorNonUniformScaleComponent::TYPEINFO_Uuid() };
AzToolsFramework::EntityCompositionRequests::AddComponentsOutcome addComponentsOutcome;
AzToolsFramework::EntityCompositionRequestBus::BroadcastResult(addComponentsOutcome,
&AzToolsFramework::EntityCompositionRequests::AddComponentsToEntities, entityList, componentsToAdd);
const auto nonUniformScaleComponent = FindPresentOrPendingComponent(EditorNonUniformScaleComponent::RTTI_Type());
AZ::ComponentId nonUniformScaleComponentId =
nonUniformScaleComponent ? nonUniformScaleComponent->GetId() : AZ::InvalidComponentId;
if (!addComponentsOutcome.IsSuccess() || !nonUniformScaleComponent)
{
AZ_Warning("Transform component", false, "Failed to add non-uniform scale component.");
return AZ::Edit::PropertyRefreshLevels::None;
}
AzToolsFramework::EntityPropertyEditorRequestBus::Broadcast(
&AzToolsFramework::EntityPropertyEditorRequests::SetNewComponentId, nonUniformScaleComponentId);
return AZ::Edit::PropertyRefreshLevels::EntireTree;
}
void TransformComponent::Reflect(AZ::ReflectContext* context)
{
// reflect data for script, serialization, editing..
@@ -1211,6 +1275,7 @@ namespace AzToolsFramework
serializeContext->Class<Components::TransformComponent, EditorComponentBase>()->
Field("Parent Entity", &TransformComponent::m_parentEntityId)->
Field("Transform Data", &TransformComponent::m_editorTransform)->
Field("AddNonUniformScaleButton", &TransformComponent::m_addNonUniformScaleButton)->
Field("Cached World Transform", &TransformComponent::m_cachedWorldTransform)->
Field("Cached World Transform Parent", &TransformComponent::m_cachedWorldTransformParent)->
Field("Parent Activation Transform Mode", &TransformComponent::m_parentActivationTransformMode)->
@@ -1224,6 +1289,7 @@ namespace AzToolsFramework
{
ptrEdit->Class<TransformComponent>("Transform", "Controls the placement of the entity in the world in 3d")->
ClassElement(AZ::Edit::ClassElements::EditorData, "")->
Attribute(AZ::Edit::Attributes::FixedComponentListIndex, 0)->
Attribute(AZ::Edit::Attributes::Icon, "Icons/Components/Transform.svg")->
Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/Transform.png")->
Attribute(AZ::Edit::Attributes::AutoExpand, true)->
@@ -1234,6 +1300,10 @@ namespace AzToolsFramework
DataElement(AZ::Edit::UIHandlers::Default, &TransformComponent::m_editorTransform, "Values", "")->
Attribute(AZ::Edit::Attributes::ChangeNotify, &TransformComponent::TransformChanged)->
Attribute(AZ::Edit::Attributes::AutoExpand, true)->
DataElement(AZ::Edit::UIHandlers::Button, &TransformComponent::m_addNonUniformScaleButton, "", "")->
Attribute(AZ::Edit::Attributes::ButtonText, "Add non-uniform scale")->
Attribute(AZ::Edit::Attributes::ReadOnly, &TransformComponent::IsAddNonUniformScaleButtonReadOnly)->
Attribute(AZ::Edit::Attributes::ChangeNotify, &TransformComponent::OnAddNonUniformScaleButtonPressed)->
DataElement(AZ::Edit::UIHandlers::ComboBox, &TransformComponent::m_parentActivationTransformMode,
"Parent activation", "Configures relative transform behavior when parent activates.")->
EnumAttribute(AZ::TransformConfig::ParentActivationTransformMode::MaintainOriginalRelativeTransform, "Original relative transform")->
@@ -23,6 +23,7 @@
#include <AzToolsFramework/API/ComponentEntitySelectionBus.h>
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
#include <AzToolsFramework/Commands/SelectionCommand.h>
#include <AzToolsFramework/ToolsComponents/EditorNonUniformScaleComponent.h>
#include "EditorComponentBase.h"
#include "TransformComponentBus.h"
@@ -228,6 +229,10 @@ namespace AzToolsFramework
void CheckApplyCachedWorldTransform(const AZ::Transform& parentWorld);
AZ::Component* FindPresentOrPendingComponent(AZ::Uuid componentUuid);
bool IsAddNonUniformScaleButtonReadOnly();
AZ::Crc32 OnAddNonUniformScaleButtonPressed();
// Drives transform behavior when parent activates. See AZ::TransformConfig::ParentActivationTransformMode for details.
AZ::TransformConfig::ParentActivationTransformMode m_parentActivationTransformMode;
@@ -260,6 +265,10 @@ namespace AzToolsFramework
bool m_worldTransformDirty = true;
bool m_isStatic = false;
// This is a workaround for a bug which causes the button to appear with incorrect placement if a UI
// element is used rather than a data element.
bool m_addNonUniformScaleButton = false;
// Deprecated
AZ::InterpolationMode m_interpolatePosition;
AZ::InterpolationMode m_interpolateRotation;
@@ -63,6 +63,7 @@ AZ_POP_DISABLE_WARNING
#include <AzToolsFramework/ToolsComponents/EditorOnlyEntityComponentBus.h>
#include <AzToolsFramework/ToolsComponents/EditorOnlyEntityComponent.h>
#include <AzToolsFramework/ToolsComponents/EditorLayerComponent.h>
#include <AzToolsFramework/ToolsComponents/EditorNonUniformScaleComponent.h>
#include <AzToolsFramework/ToolsMessaging/EntityHighlightBus.h>
#include <AzToolsFramework/UI/ComponentPalette/ComponentPaletteUtil.hxx>
#include <AzToolsFramework/UI/ComponentPalette/ComponentPaletteWidget.hxx>
@@ -494,6 +495,11 @@ namespace AzToolsFramework
}
}
void EntityPropertyEditor::SetNewComponentId(AZ::ComponentId componentId)
{
m_newComponentId = componentId;
}
void EntityPropertyEditor::SetOverrideEntityIds(const AzToolsFramework::EntityIdSet& entities)
{
m_overrideSelectedEntityIds = entities;
@@ -1039,15 +1045,23 @@ namespace AzToolsFramework
sortedComponents.end(),
[=](const OrderedSortComponentEntry& component1, const OrderedSortComponentEntry& component2)
{
// Transform component must be first, always
// If component 1 is a transform component, it is sorted earlier
if (component1.m_component->RTTI_IsTypeOf(AZ::EditorTransformComponentTypeId))
AZStd::optional<int> fixedComponentListIndex1 = GetFixedComponentListIndex(component1.m_component);
AZStd::optional<int> fixedComponentListIndex2 = GetFixedComponentListIndex(component2.m_component);
// If both components have fixed list indices, sort based on those indices
if (fixedComponentListIndex1.has_value() && fixedComponentListIndex2.has_value())
{
return fixedComponentListIndex1.value() < fixedComponentListIndex2.value();
}
// If component 1 has a fixed list index, sort it first
if (fixedComponentListIndex1.has_value())
{
return true;
}
// If component 2 is a transform component, component 1 is never sorted earlier
if (component2.m_component->RTTI_IsTypeOf(AZ::EditorTransformComponentTypeId))
// If component 2 has a fixed list index, component 1 should not be sorted before it
if (fixedComponentListIndex2.has_value())
{
return false;
}
@@ -1128,10 +1142,7 @@ namespace AzToolsFramework
{
if (auto attributeData = azdynamic_cast<AZ::Edit::AttributeData<bool>*>(attribute))
{
if (!attributeData->Get(nullptr))
{
return false;
}
return attributeData->Get(nullptr);
}
}
}
@@ -1166,6 +1177,36 @@ namespace AzToolsFramework
return true;
}
AZStd::optional<int> EntityPropertyEditor::GetFixedComponentListIndex(const AZ::Component* component)
{
auto componentClassData = component ? GetComponentClassData(component) : nullptr;
if (componentClassData && componentClassData->m_editData)
{
if (auto editorDataElement = componentClassData->m_editData->FindElementData(AZ::Edit::ClassElements::EditorData))
{
if (auto attribute = editorDataElement->FindAttribute(AZ::Edit::Attributes::FixedComponentListIndex))
{
if (auto attributeData = azdynamic_cast<AZ::Edit::AttributeData<int>*>(attribute))
{
return { attributeData->Get(nullptr) };
}
}
}
}
return {};
}
bool EntityPropertyEditor::IsComponentDraggable(const AZ::Component* component)
{
return !GetFixedComponentListIndex(component).has_value();
}
bool EntityPropertyEditor::AreComponentsDraggable(const AZ::Entity::ComponentArrayType& components) const
{
return AZStd::all_of(
components.begin(), components.end(), [](AZ::Component* component) { return IsComponentDraggable(component); });
}
bool EntityPropertyEditor::AreComponentsCopyable(const AZ::Entity::ComponentArrayType& components) const
{
return AreComponentsCopyable(components, m_componentFilter);
@@ -3367,7 +3408,9 @@ namespace AzToolsFramework
sourceComponents.size() == m_selectedEntityIds.size() &&
targetComponents.size() == m_selectedEntityIds.size() &&
AreComponentsRemovable(sourceComponents) &&
AreComponentsRemovable(targetComponents);
AreComponentsRemovable(targetComponents) &&
AreComponentsDraggable(sourceComponents) &&
AreComponentsDraggable(targetComponents);
}
bool EntityPropertyEditor::IsMoveComponentsUpAllowed() const
@@ -3681,14 +3724,38 @@ namespace AzToolsFramework
void EntityPropertyEditor::ScrollToNewComponent()
{
//force new components to be visible, assuming they are added to the end of the list and layout
auto componentEditor = GetComponentEditorsFromIndex(m_componentEditorsUsed - 1);
// force new components to be visible
// if no component has been explicitly set at the most recently added,
// assume new components are added to the end of the list and layout
AZ::s32 newComponentIndex = m_componentEditorsUsed - 1;
// if there is a component id explicitly set as the most recently added, try to find it and make sure it is visible
if (m_newComponentId.has_value() && m_newComponentId.value() != AZ::InvalidComponentId)
{
AZ::ComponentId newComponentId = m_newComponentId.value();
for (AZ::s32 componentIndex = 0; componentIndex < m_componentEditorsUsed; ++componentIndex)
{
if (m_componentEditors[componentIndex])
{
for (const auto component : m_componentEditors[componentIndex]->GetComponents())
{
if (component->GetId() == newComponentId)
{
newComponentIndex = componentIndex;
}
}
}
}
}
auto componentEditor = GetComponentEditorsFromIndex(newComponentIndex);
if (componentEditor)
{
m_gui->m_componentList->ensureWidgetVisible(componentEditor);
}
m_shouldScrollToNewComponents = false;
m_shouldScrollToNewComponentsQueued = false;
m_newComponentId.reset();
}
void EntityPropertyEditor::QueueScrollToNewComponent()
@@ -4073,7 +4140,8 @@ namespace AzToolsFramework
{
if (!componentEditor ||
!componentEditor->isVisible() ||
!AreComponentsRemovable(componentEditor->GetComponents()))
!AreComponentsRemovable(componentEditor->GetComponents()) ||
!AreComponentsDraggable(componentEditor->GetComponents()))
{
return false;
}
@@ -4223,6 +4291,7 @@ namespace AzToolsFramework
while (targetComponentEditor
&& (targetComponentEditor->IsDragged()
|| !AreComponentsRemovable(targetComponentEditor->GetComponents())
|| !AreComponentsDraggable(targetComponentEditor->GetComponents())
|| (globalRect.center().y() > GetWidgetGlobalRect(targetComponentEditor).center().y())))
{
if (targetItr == m_componentEditors.end() || targetComponentEditor == m_componentEditors.back() || !targetComponentEditor->isVisible())
@@ -211,6 +211,7 @@ namespace AzToolsFramework
// EntityPropertEditorRequestBus
void GetSelectedAndPinnedEntities(EntityIdList& selectedEntityIds) override;
void GetSelectedEntities(EntityIdList& selectedEntityIds) override;
void SetNewComponentId(AZ::ComponentId componentId) override;
bool IsEntitySelected(const AZ::EntityId& id) const;
bool IsSingleEntitySelected(const AZ::EntityId& id) const;
@@ -237,6 +238,9 @@ namespace AzToolsFramework
static bool DoesComponentPassFilter(const AZ::Component* component, const ComponentFilter& filter);
static bool IsComponentRemovable(const AZ::Component* component);
bool AreComponentsRemovable(const AZ::Entity::ComponentArrayType& components) const;
static AZStd::optional<int> GetFixedComponentListIndex(const AZ::Component* component);
static bool IsComponentDraggable(const AZ::Component* component);
bool AreComponentsDraggable(const AZ::Entity::ComponentArrayType& components) const;
bool AreComponentsCopyable(const AZ::Entity::ComponentArrayType& components) const;
void AddMenuOptionsForComponents(QMenu& menu, const QPoint& position);
@@ -568,6 +572,9 @@ namespace AzToolsFramework
void ConnectToEntityBuses(const AZ::EntityId& entityId);
void DisconnectFromEntityBuses(const AZ::EntityId& entityId);
//! Stores a component id to be focused on next time the UI updates.
AZStd::optional<AZ::ComponentId> m_newComponentId;
private slots:
void OnPropertyRefreshRequired(); // refresh is needed for a property.
void UpdateContents();
@@ -178,6 +178,24 @@ namespace AzToolsFramework
~ViewportInteractionRequests() = default;
};
/// Interface to return only viewport specific settings (e.g. snapping).
class ViewportSettings
{
public:
virtual ~ViewportSettings() = default;
/// Return if grid snapping is enabled.
virtual bool GridSnappingEnabled() const = 0;
/// Return the grid snapping size.
virtual float GridSize() const = 0;
/// Does the grid currently want to be displayed.
virtual bool ShowGrid() const = 0;
/// Return if angle snapping is enabled.
virtual bool AngleSnappingEnabled() const = 0;
/// Return the angle snapping/step size.
virtual float AngleStep() const = 0;
};
/// Type to inherit to implement ViewportInteractionRequests.
using ViewportInteractionRequestBus = AZ::EBus<ViewportInteractionRequests, ViewportEBusTraits>;
@@ -244,6 +262,8 @@ namespace AzToolsFramework
/// from ViewportCursorScreenPosition. This method will always return the correct position to generate a mouse
/// position delta.
virtual AZStd::optional<AzFramework::ScreenPoint> PreviousViewportCursorScreenPosition() = 0;
/// Is mouse over viewport.
virtual bool IsMouseOver() const = 0;
protected:
~ViewportMouseCursorRequests() = default;
@@ -17,13 +17,13 @@
#include <AzToolsFramework/Application/ToolsApplication.h>
#include <AzToolsFramework/ViewportSelection/EditorInteractionSystemViewportSelectionRequestBus.h>
#include <AzToolsFramework/ToolsComponents/TransformComponent.h>
#include <AzToolsFramework/ToolsComponents/ScriptEditorComponent.h>
#include <AzToolsFramework/UI/PropertyEditor/EntityPropertyEditor.hxx>
#include <AzToolsFramework/API/EntityPropertyEditorRequestsBus.h>
#include <AzToolsFramework/ToolsComponents/EditorLockComponent.h>
#include <AzToolsFramework/ToolsComponents/EditorVisibilityComponent.h>
#include <AzToolsFramework/ViewportSelection/EditorDefaultSelection.h>
#include <AzCore/IO/Streamer/StreamerComponent.h>
#include <AzCore/Asset/AssetManagerComponent.h>
#include <AzCore/std/sort.h>
@@ -55,7 +55,7 @@ namespace UnitTest
TEST(EntityPropertyEditorTests, PrioritySort_NonTransformAsFirstItem_TransformMovesToTopRemainderUnchanged)
{
ComponentApplication app;
ToolsApplication app;
AZ::Entity::ComponentArrayType unorderedComponents;
AZ::Entity::ComponentArrayType orderedComponents;
@@ -68,12 +68,18 @@ namespace UnitTest
Entity* systemEntity = app.Create(desc, startupParams);
// Need to reflect the components so that edit attribute used for sorting, such as FixedComponentListIndex, get set.
app.RegisterComponentDescriptor(AzToolsFramework::Components::TransformComponent::CreateDescriptor());
app.RegisterComponentDescriptor(AzToolsFramework::Components::ScriptEditorComponent::CreateDescriptor());
app.RegisterComponentDescriptor(AZ::AssetManagerComponent::CreateDescriptor());
// Add more than 31 components, as we are testing the case where the sort fails when there are 32 or more items.
const int numFillerItems = 32;
for (int commentIndex = 0; commentIndex < numFillerItems; commentIndex++)
{
unorderedComponents.insert(unorderedComponents.begin(), systemEntity->CreateComponent(AZ::StreamerComponent::RTTI_Type()));
unorderedComponents.insert(unorderedComponents.begin(), systemEntity->CreateComponent(
AzToolsFramework::Components::ScriptEditorComponent::RTTI_Type()));
}
// Add a TransformComponent at the end which should be sorted to the beginning by the priority sort.