Merge branch 'main' into LY-113714

This commit is contained in:
sphrose
2021-05-10 11:08:22 +01:00
1039 changed files with 25403 additions and 39113 deletions
@@ -239,8 +239,13 @@ namespace AZ
return;
}
CheckReady();
m_initComplete = true;
// *After* setting initComplete to true, check to see if the assets are already ready.
// This check needs to wait until after setting initComplete because if they *are* ready, we want the final call to
// RemoveWaitingAsset to trigger the OnAssetContainerReady/Canceled event. If we call CheckReady() *before* setting
// initComplete, if all the assets are ready, the event will never get triggered.
CheckReady();
}
bool AssetContainer::IsReady() const
@@ -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>();
+38 -13
View File
@@ -142,27 +142,44 @@ namespace AZ
void SetBasis(const Vector3& basisX, const Vector3& basisY, const Vector3& basisZ);
//! @}
Matrix3x3 operator*(const Matrix3x3& rhs) const;
//! Calculates (this->GetTranspose() * rhs).
Matrix3x3 TransposedMultiply(const Matrix3x3& rhs) const;
//! Post-multiplies the matrix by a vector.
Vector3 operator*(const Vector3& rhs) const;
Matrix3x3 operator+(const Matrix3x3& rhs) const;
Matrix3x3 operator-(const Matrix3x3& rhs) const;
Matrix3x3 operator*(float multiplier) const;
Matrix3x3 operator/(float divisor) const;
Matrix3x3 operator-() const;
Matrix3x3& operator*=(const Matrix3x3& rhs);
//! Operator for matrix-matrix addition.
//! @{
[[nodiscard]] Matrix3x3 operator+(const Matrix3x3& rhs) const;
Matrix3x3& operator+=(const Matrix3x3& rhs);
//! @}
//! Operator for matrix-matrix substraction.
//! @{
[[nodiscard]] Matrix3x3 operator-(const Matrix3x3& rhs) const;
Matrix3x3& operator-=(const Matrix3x3& rhs);
//! @}
//! Operator for matrix-matrix multiplication.
//! @{
[[nodiscard]] Matrix3x3 operator*(const Matrix3x3& rhs) const;
Matrix3x3& operator*=(const Matrix3x3& rhs);
//! @}
//! Operator for multiplying all matrix's elements with a scalar
//! @{
[[nodiscard]] Matrix3x3 operator*(float multiplier) const;
Matrix3x3& operator*=(float multiplier);
//! @}
//! Operator for dividing all matrix's elements with a scalar
//! @{
[[nodiscard]] Matrix3x3 operator/(float divisor) const;
Matrix3x3& operator/=(float divisor);
//! @}
//! Operator for negating all matrix's elements
[[nodiscard]] Matrix3x3 operator-() const;
bool operator==(const Matrix3x3& rhs) const;
bool operator!=(const Matrix3x3& rhs) const;
@@ -187,7 +204,10 @@ namespace AZ
//! @}
//! Gets the scale part of the transformation, i.e. the length of the scale components.
Vector3 RetrieveScale() const;
[[nodiscard]] Vector3 RetrieveScale() const;
//! Gets the squared scale part of the transformation (the squared length of the basis vectors).
[[nodiscard]] Vector3 RetrieveScaleSq() const;
//! Gets the scale part of the transformation as in RetrieveScale, and also removes this scaling from the matrix.
Vector3 ExtractScale();
@@ -195,6 +215,9 @@ namespace AZ
//! Quick multiplication by a scale matrix, equivalent to m*=Matrix3x3::CreateScale(scale).
void MultiplyByScale(const Vector3& scale);
//! Returns a matrix with the reciprocal scale, keeping the same rotation and translation.
[[nodiscard]] Matrix3x3 GetReciprocalScaled() const;
//! Polar decomposition, M=U*H, U is orthogonal (unitary) and H is symmetric (hermitian).
//! This function returns the orthogonal part only
Matrix3x3 GetPolarDecomposition() const;
@@ -241,7 +264,9 @@ namespace AZ
//! Note that this is not the usual multiplication order for transformations.
Vector3& operator*=(Vector3& lhs, const Matrix3x3& rhs);
//! Pre-multiplies the matrix by a scalar.
Matrix3x3 operator*(float lhs, const Matrix3x3& rhs);
}
} // namespace AZ
#include <AzCore/Math/Matrix3x3.inl>
+84 -57
View File
@@ -392,14 +392,6 @@ namespace AZ
}
AZ_MATH_INLINE Matrix3x3 Matrix3x3::operator*(const Matrix3x3& rhs) const
{
Matrix3x3 result;
Simd::Vec3::Mat3x3Multiply(GetSimdValues(), rhs.GetSimdValues(), result.GetSimdValues());
return result;
}
AZ_MATH_INLINE Matrix3x3 Matrix3x3::TransposedMultiply(const Matrix3x3& rhs) const
{
Matrix3x3 result;
@@ -416,51 +408,12 @@ namespace AZ
AZ_MATH_INLINE Matrix3x3 Matrix3x3::operator+(const Matrix3x3& rhs) const
{
return Matrix3x3(Simd::Vec3::Add(m_rows[0].GetSimdValue(), rhs.m_rows[0].GetSimdValue())
, Simd::Vec3::Add(m_rows[1].GetSimdValue(), rhs.m_rows[1].GetSimdValue())
, Simd::Vec3::Add(m_rows[2].GetSimdValue(), rhs.m_rows[2].GetSimdValue()));
}
AZ_MATH_INLINE Matrix3x3 Matrix3x3::operator-(const Matrix3x3& rhs) const
{
return Matrix3x3(Simd::Vec3::Sub(m_rows[0].GetSimdValue(), rhs.m_rows[0].GetSimdValue())
, Simd::Vec3::Sub(m_rows[1].GetSimdValue(), rhs.m_rows[1].GetSimdValue())
, Simd::Vec3::Sub(m_rows[2].GetSimdValue(), rhs.m_rows[2].GetSimdValue()));
}
AZ_MATH_INLINE Matrix3x3 Matrix3x3::operator*(float multiplier) const
{
const Simd::Vec3::FloatType mulVec = Simd::Vec3::Splat(multiplier);
return Matrix3x3(Simd::Vec3::Mul(m_rows[0].GetSimdValue(), mulVec)
, Simd::Vec3::Mul(m_rows[1].GetSimdValue(), mulVec)
, Simd::Vec3::Mul(m_rows[2].GetSimdValue(), mulVec));
}
AZ_MATH_INLINE Matrix3x3 Matrix3x3::operator/(float divisor) const
{
const Simd::Vec3::FloatType divVec = Simd::Vec3::Splat(divisor);
return Matrix3x3(Simd::Vec3::Div(m_rows[0].GetSimdValue(), divVec)
, Simd::Vec3::Div(m_rows[1].GetSimdValue(), divVec)
, Simd::Vec3::Div(m_rows[2].GetSimdValue(), divVec));
}
AZ_MATH_INLINE Matrix3x3 Matrix3x3::operator-() const
{
const Simd::Vec3::FloatType zeroVec = Simd::Vec3::ZeroFloat();
return Matrix3x3(Simd::Vec3::Sub(zeroVec, m_rows[0].GetSimdValue())
, Simd::Vec3::Sub(zeroVec, m_rows[1].GetSimdValue())
, Simd::Vec3::Sub(zeroVec, m_rows[2].GetSimdValue()));
}
AZ_MATH_INLINE Matrix3x3& Matrix3x3::operator*=(const Matrix3x3& rhs)
{
*this = *this * rhs;
return *this;
return Matrix3x3
(
Simd::Vec3::Add(m_rows[0].GetSimdValue(), rhs.m_rows[0].GetSimdValue()),
Simd::Vec3::Add(m_rows[1].GetSimdValue(), rhs.m_rows[1].GetSimdValue()),
Simd::Vec3::Add(m_rows[2].GetSimdValue(), rhs.m_rows[2].GetSimdValue())
);
}
@@ -471,6 +424,17 @@ namespace AZ
}
AZ_MATH_INLINE Matrix3x3 Matrix3x3::operator-(const Matrix3x3& rhs) const
{
return Matrix3x3
(
Simd::Vec3::Sub(m_rows[0].GetSimdValue(), rhs.m_rows[0].GetSimdValue()),
Simd::Vec3::Sub(m_rows[1].GetSimdValue(), rhs.m_rows[1].GetSimdValue()),
Simd::Vec3::Sub(m_rows[2].GetSimdValue(), rhs.m_rows[2].GetSimdValue())
);
}
AZ_MATH_INLINE Matrix3x3& Matrix3x3::operator-=(const Matrix3x3& rhs)
{
*this = *this - rhs;
@@ -478,6 +442,33 @@ namespace AZ
}
AZ_MATH_INLINE Matrix3x3 Matrix3x3::operator*(const Matrix3x3& rhs) const
{
Matrix3x3 result;
Simd::Vec3::Mat3x3Multiply(GetSimdValues(), rhs.GetSimdValues(), result.GetSimdValues());
return result;
}
AZ_MATH_INLINE Matrix3x3& Matrix3x3::operator*=(const Matrix3x3& rhs)
{
*this = *this * rhs;
return *this;
}
AZ_MATH_INLINE Matrix3x3 Matrix3x3::operator*(float multiplier) const
{
const Simd::Vec3::FloatType mulVec = Simd::Vec3::Splat(multiplier);
return Matrix3x3
(
Simd::Vec3::Mul(m_rows[0].GetSimdValue(), mulVec),
Simd::Vec3::Mul(m_rows[1].GetSimdValue(), mulVec),
Simd::Vec3::Mul(m_rows[2].GetSimdValue(), mulVec)
);
}
AZ_MATH_INLINE Matrix3x3& Matrix3x3::operator*=(float multiplier)
{
*this = *this * multiplier;
@@ -485,6 +476,18 @@ namespace AZ
}
AZ_MATH_INLINE Matrix3x3 Matrix3x3::operator/(float divisor) const
{
const Simd::Vec3::FloatType divVec = Simd::Vec3::Splat(divisor);
return Matrix3x3
(
Simd::Vec3::Div(m_rows[0].GetSimdValue(), divVec),
Simd::Vec3::Div(m_rows[1].GetSimdValue(), divVec),
Simd::Vec3::Div(m_rows[2].GetSimdValue(), divVec)
);
}
AZ_MATH_INLINE Matrix3x3& Matrix3x3::operator/=(float divisor)
{
*this = *this / divisor;
@@ -492,6 +495,18 @@ namespace AZ
}
AZ_MATH_INLINE Matrix3x3 Matrix3x3::operator-() const
{
const Simd::Vec3::FloatType zeroVec = Simd::Vec3::ZeroFloat();
return Matrix3x3
(
Simd::Vec3::Sub(zeroVec, m_rows[0].GetSimdValue()),
Simd::Vec3::Sub(zeroVec, m_rows[1].GetSimdValue()),
Simd::Vec3::Sub(zeroVec, m_rows[2].GetSimdValue())
);
}
AZ_MATH_INLINE bool Matrix3x3::operator==(const Matrix3x3& rhs) const
{
return (Simd::Vec3::CmpAllEq(m_rows[0].GetSimdValue(), rhs.m_rows[0].GetSimdValue())
@@ -552,6 +567,12 @@ namespace AZ
}
AZ_MATH_INLINE Vector3 Matrix3x3::RetrieveScaleSq() const
{
return Vector3(GetBasisX().GetLengthSq(), GetBasisY().GetLengthSq(), GetBasisZ().GetLengthSq());
}
AZ_MATH_INLINE Vector3 Matrix3x3::ExtractScale()
{
const Vector3 x = GetBasisX();
@@ -584,6 +605,14 @@ namespace AZ
}
AZ_MATH_INLINE Matrix3x3 Matrix3x3::GetReciprocalScaled() const
{
Matrix3x3 result = *this;
result.MultiplyByScale(RetrieveScaleSq().GetReciprocal());
return result;
}
AZ_MATH_INLINE void Matrix3x3::GetPolarDecomposition(Matrix3x3* orthogonalOut, Matrix3x3* symmetricOut) const
{
*orthogonalOut = GetPolarDecomposition();
@@ -679,8 +708,6 @@ namespace AZ
AZ_MATH_INLINE Matrix3x3 operator*(float lhs, const Matrix3x3& rhs)
{
const Simd::Vec3::FloatType lhsVec = Simd::Vec3::Splat(lhs);
const Simd::Vec3::FloatType* rows = rhs.GetSimdValues();
return Matrix3x3(Simd::Vec3::Mul(lhsVec, rows[0]), Simd::Vec3::Mul(lhsVec, rows[1]), Simd::Vec3::Mul(lhsVec, rows[2]));
return rhs * lhs;
}
}
} // namespace AZ
+30 -11
View File
@@ -225,23 +225,38 @@ namespace AZ
//! Sets the three basis vectors and the translation.
void SetBasisAndTranslation(const Vector3& basisX, const Vector3& basisY, const Vector3& basisZ, const Vector3& translation);
//! Operator for matrix-matrix multiplication.
[[nodiscard]] Matrix3x4 operator*(const Matrix3x4& rhs) const;
//! Compound assignment operator for matrix-matrix multiplication.
Matrix3x4& operator*=(const Matrix3x4& rhs);
//! Operator for matrix-matrix addition.
//! @{
[[nodiscard]] Matrix3x4 operator+(const Matrix3x4& rhs) const;
//! Compound assignment operator for matrix-matrix addition.
Matrix3x4& operator+=(const Matrix3x4& rhs);
//! @}
//! Operator for matrix-matrix substraction.
//! @{
[[nodiscard]] Matrix3x4 operator-(const Matrix3x4& rhs) const;
Matrix3x4& operator-=(const Matrix3x4& rhs);
//! @}
//! Operator for matrix-matrix multiplication.
//! @{
[[nodiscard]] Matrix3x4 operator*(const Matrix3x4& rhs) const;
Matrix3x4& operator*=(const Matrix3x4& rhs);
//! @}
//! Operator for multiplying all matrix's elements with a scalar
[[nodiscard]] Matrix3x4 operator*(float scalar) const;
//! @{
[[nodiscard]] Matrix3x4 operator*(float multiplier) const;
Matrix3x4& operator*=(float multiplier);
//! @}
//! Compound assignment operator for multiplying all matrix's elements with a scalar
Matrix3x4& operator*=(float scalar);
//! Operator for dividing all matrix's elements with a scalar
//! @{
[[nodiscard]] Matrix3x4 operator/(float divisor) const;
Matrix3x4& operator/=(float divisor);
//! @}
//! Operator for negating all matrix's elements
[[nodiscard]] Matrix3x4 operator-() const;
//! Operator for transforming a Vector3.
[[nodiscard]] Vector3 operator*(const Vector3& rhs) const;
@@ -353,6 +368,10 @@ namespace AZ
Vector4 m_rows[RowCount];
};
//! Pre-multiplies the matrix by a scalar.
Matrix3x4 operator*(float lhs, const Matrix3x4& rhs);
} // namespace AZ
#include <AzCore/Math/Matrix3x4.inl>
+77 -22
View File
@@ -472,21 +472,6 @@ namespace AZ
}
AZ_MATH_INLINE Matrix3x4 Matrix3x4::operator*(const Matrix3x4& rhs) const
{
Matrix3x4 result;
Simd::Vec4::Mat3x4Multiply(GetSimdValues(), rhs.GetSimdValues(), result.GetSimdValues());
return result;
}
AZ_MATH_INLINE Matrix3x4& Matrix3x4::operator*=(const Matrix3x4& rhs)
{
*this = *this * rhs;
return *this;
}
AZ_MATH_INLINE Matrix3x4 Matrix3x4::operator+(const Matrix3x4& rhs) const
{
return Matrix3x4
@@ -505,25 +490,89 @@ namespace AZ
}
AZ_MATH_INLINE Matrix3x4 Matrix3x4::operator*(float scalar) const
AZ_MATH_INLINE Matrix3x4 Matrix3x4::operator-(const Matrix3x4& rhs) const
{
const Vector4 vector4Scalar(scalar);
return Matrix3x4
(
Simd::Vec4::Mul(m_rows[0].GetSimdValue(), vector4Scalar.GetSimdValue()),
Simd::Vec4::Mul(m_rows[1].GetSimdValue(), vector4Scalar.GetSimdValue()),
Simd::Vec4::Mul(m_rows[2].GetSimdValue(), vector4Scalar.GetSimdValue())
Simd::Vec4::Sub(m_rows[0].GetSimdValue(), rhs.m_rows[0].GetSimdValue()),
Simd::Vec4::Sub(m_rows[1].GetSimdValue(), rhs.m_rows[1].GetSimdValue()),
Simd::Vec4::Sub(m_rows[2].GetSimdValue(), rhs.m_rows[2].GetSimdValue())
);
}
AZ_MATH_INLINE Matrix3x4& Matrix3x4::operator*=(float scalar)
AZ_MATH_INLINE Matrix3x4& Matrix3x4::operator-=(const Matrix3x4& rhs)
{
*this = *this * scalar;
*this = *this - rhs;
return *this;
}
AZ_MATH_INLINE Matrix3x4 Matrix3x4::operator*(const Matrix3x4& rhs) const
{
Matrix3x4 result;
Simd::Vec4::Mat3x4Multiply(GetSimdValues(), rhs.GetSimdValues(), result.GetSimdValues());
return result;
}
AZ_MATH_INLINE Matrix3x4& Matrix3x4::operator*=(const Matrix3x4& rhs)
{
*this = *this * rhs;
return *this;
}
AZ_MATH_INLINE Matrix3x4 Matrix3x4::operator*(float multiplier) const
{
const Simd::Vec4::FloatType mulVec = Simd::Vec4::Splat(multiplier);
return Matrix3x4
(
Simd::Vec4::Mul(m_rows[0].GetSimdValue(), mulVec),
Simd::Vec4::Mul(m_rows[1].GetSimdValue(), mulVec),
Simd::Vec4::Mul(m_rows[2].GetSimdValue(), mulVec)
);
}
AZ_MATH_INLINE Matrix3x4& Matrix3x4::operator*=(float multiplier)
{
*this = *this * multiplier;
return *this;
}
AZ_MATH_INLINE Matrix3x4 Matrix3x4::operator/(float divisor) const
{
const Simd::Vec4::FloatType divVec = Simd::Vec4::Splat(divisor);
return Matrix3x4
(
Simd::Vec4::Div(m_rows[0].GetSimdValue(), divVec),
Simd::Vec4::Div(m_rows[1].GetSimdValue(), divVec),
Simd::Vec4::Div(m_rows[2].GetSimdValue(), divVec)
);
}
AZ_MATH_INLINE Matrix3x4& Matrix3x4::operator/=(float divisor)
{
*this = *this / divisor;
return *this;
}
AZ_MATH_INLINE Matrix3x4 Matrix3x4::operator-() const
{
const Simd::Vec4::FloatType zeroVec = Simd::Vec4::ZeroFloat();
return Matrix3x4
(
Simd::Vec4::Sub(zeroVec, m_rows[0].GetSimdValue()),
Simd::Vec4::Sub(zeroVec, m_rows[1].GetSimdValue()),
Simd::Vec4::Sub(zeroVec, m_rows[2].GetSimdValue())
);
}
AZ_MATH_INLINE Vector3 Matrix3x4::operator*(const Vector3& rhs) const
{
return Vector3
@@ -711,4 +760,10 @@ namespace AZ
{
return reinterpret_cast<Simd::Vec4::FloatType*>(m_rows);
}
AZ_MATH_INLINE Matrix3x4 operator*(float lhs, const Matrix3x4& rhs)
{
return rhs * lhs;
}
} // namespace AZ
+39 -5
View File
@@ -171,14 +171,38 @@ namespace AZ
void SetTranslation(const Vector3& v);
//! @}
Matrix4x4 operator+(const Matrix4x4& rhs) const;
//! Operator for matrix-matrix addition.
//! @{
[[nodiscard]] Matrix4x4 operator+(const Matrix4x4& rhs) const;
Matrix4x4& operator+=(const Matrix4x4& rhs);
//! @}
Matrix4x4 operator-(const Matrix4x4& rhs) const;
//! Operator for matrix-matrix substraction.
//! @{
[[nodiscard]] Matrix4x4 operator-(const Matrix4x4& rhs) const;
Matrix4x4& operator-=(const Matrix4x4& rhs);
//! @}
Matrix4x4 operator*(const Matrix4x4& rhs) const;
//! Operator for matrix-matrix multiplication.
//! @{
[[nodiscard]] Matrix4x4 operator*(const Matrix4x4& rhs) const;
Matrix4x4& operator*=(const Matrix4x4& rhs);
//! @}
//! Operator for multiplying all matrix's elements with a scalar
//! @{
[[nodiscard]] Matrix4x4 operator*(float multiplier) const;
Matrix4x4& operator*=(float multiplier);
//! @}
//! Operator for dividing all matrix's elements with a scalar
//! @{
[[nodiscard]] Matrix4x4 operator/(float divisor) const;
Matrix4x4& operator/=(float divisor);
//! @}
//! Operator for negating all matrix's elements
[[nodiscard]] Matrix4x4 operator-() const;
//! Post-multiplies the matrix by a vector.
//! Assumes that the w-component of the Vector3 is 1.0.
@@ -222,7 +246,10 @@ namespace AZ
//! @}
//! Gets the scale part of the transformation, i.e. the length of the scale components.
Vector3 RetrieveScale() const;
[[nodiscard]] Vector3 RetrieveScale() const;
//! Gets the squared scale part of the transformation (the squared length of the basis vectors).
[[nodiscard]] Vector3 RetrieveScaleSq() const;
//! Gets the scale part of the transformation as in RetrieveScale, and also removes this scaling from the matrix.
Vector3 ExtractScale();
@@ -230,6 +257,9 @@ namespace AZ
//! Quick multiplication by a scale matrix, equivalent to m*=Matrix4x4::CreateScale(scale).
void MultiplyByScale(const Vector3& scale);
//! Returns a matrix with the reciprocal scale, keeping the same rotation and translation.
[[nodiscard]] Matrix4x4 GetReciprocalScaled() const;
bool IsClose(const Matrix4x4& rhs, float tolerance = Constants::Tolerance) const;
bool operator==(const Matrix4x4& rhs) const;
@@ -270,6 +300,10 @@ namespace AZ
//! Pre-multiplies the matrix by a vector in-place.
//! Note that this is not the usual multiplication order for transformations.
Vector4& operator*=(Vector4& lhs, const Matrix4x4& rhs);
}
//! Pre-multiplies the matrix by a scalar.
Matrix4x4 operator*(float lhs, const Matrix4x4& rhs);
} // namespace AZ
#include <AzCore/Math/Matrix4x4.inl>
+92 -15
View File
@@ -480,20 +480,12 @@ namespace AZ
AZ_MATH_INLINE Matrix4x4 Matrix4x4::operator+(const Matrix4x4& rhs) const
{
return Matrix4x4
( Simd::Vec4::Add(m_rows[0].GetSimdValue(), rhs.m_rows[0].GetSimdValue())
, Simd::Vec4::Add(m_rows[1].GetSimdValue(), rhs.m_rows[1].GetSimdValue())
, Simd::Vec4::Add(m_rows[2].GetSimdValue(), rhs.m_rows[2].GetSimdValue())
, Simd::Vec4::Add(m_rows[3].GetSimdValue(), rhs.m_rows[3].GetSimdValue()));
}
AZ_MATH_INLINE Matrix4x4 Matrix4x4::operator-(const Matrix4x4& rhs) const
{
return Matrix4x4
( Simd::Vec4::Sub(m_rows[0].GetSimdValue(), rhs.m_rows[0].GetSimdValue())
, Simd::Vec4::Sub(m_rows[1].GetSimdValue(), rhs.m_rows[1].GetSimdValue())
, Simd::Vec4::Sub(m_rows[2].GetSimdValue(), rhs.m_rows[2].GetSimdValue())
, Simd::Vec4::Sub(m_rows[3].GetSimdValue(), rhs.m_rows[3].GetSimdValue()));
(
Simd::Vec4::Add(m_rows[0].GetSimdValue(), rhs.m_rows[0].GetSimdValue()),
Simd::Vec4::Add(m_rows[1].GetSimdValue(), rhs.m_rows[1].GetSimdValue()),
Simd::Vec4::Add(m_rows[2].GetSimdValue(), rhs.m_rows[2].GetSimdValue()),
Simd::Vec4::Add(m_rows[3].GetSimdValue(), rhs.m_rows[3].GetSimdValue())
);
}
AZ_MATH_INLINE Matrix4x4& Matrix4x4::operator+=(const Matrix4x4& rhs)
@@ -502,6 +494,18 @@ namespace AZ
return *this;
}
AZ_MATH_INLINE Matrix4x4 Matrix4x4::operator-(const Matrix4x4& rhs) const
{
return Matrix4x4
(
Simd::Vec4::Sub(m_rows[0].GetSimdValue(), rhs.m_rows[0].GetSimdValue()),
Simd::Vec4::Sub(m_rows[1].GetSimdValue(), rhs.m_rows[1].GetSimdValue()),
Simd::Vec4::Sub(m_rows[2].GetSimdValue(), rhs.m_rows[2].GetSimdValue()),
Simd::Vec4::Sub(m_rows[3].GetSimdValue(), rhs.m_rows[3].GetSimdValue())
);
}
AZ_MATH_INLINE Matrix4x4& Matrix4x4::operator-=(const Matrix4x4& rhs)
{
*this = *this - rhs;
@@ -523,6 +527,59 @@ namespace AZ
}
AZ_MATH_INLINE Matrix4x4 Matrix4x4::operator*(float multiplier) const
{
const Simd::Vec4::FloatType mulVec = Simd::Vec4::Splat(multiplier);
return Matrix4x4
(
Simd::Vec4::Mul(m_rows[0].GetSimdValue(), mulVec),
Simd::Vec4::Mul(m_rows[1].GetSimdValue(), mulVec),
Simd::Vec4::Mul(m_rows[2].GetSimdValue(), mulVec),
Simd::Vec4::Mul(m_rows[3].GetSimdValue(), mulVec)
);
}
AZ_MATH_INLINE Matrix4x4& Matrix4x4::operator*=(float multiplier)
{
*this = *this * multiplier;
return *this;
}
AZ_MATH_INLINE Matrix4x4 Matrix4x4::operator/(float divisor) const
{
const Simd::Vec4::FloatType divVec = Simd::Vec4::Splat(divisor);
return Matrix4x4
(
Simd::Vec4::Div(m_rows[0].GetSimdValue(), divVec),
Simd::Vec4::Div(m_rows[1].GetSimdValue(), divVec),
Simd::Vec4::Div(m_rows[2].GetSimdValue(), divVec),
Simd::Vec4::Div(m_rows[3].GetSimdValue(), divVec)
);
}
AZ_MATH_INLINE Matrix4x4& Matrix4x4::operator/=(float divisor)
{
*this = *this / divisor;
return *this;
}
AZ_MATH_INLINE Matrix4x4 Matrix4x4::operator-() const
{
const Simd::Vec4::FloatType zeroVec = Simd::Vec4::ZeroFloat();
return Matrix4x4
(
Simd::Vec4::Sub(zeroVec, m_rows[0].GetSimdValue()),
Simd::Vec4::Sub(zeroVec, m_rows[1].GetSimdValue()),
Simd::Vec4::Sub(zeroVec, m_rows[2].GetSimdValue()),
Simd::Vec4::Sub(zeroVec, m_rows[3].GetSimdValue())
);
}
AZ_MATH_INLINE Vector3 Matrix4x4::operator*(const Vector3& rhs) const
{
return Vector3(Simd::Vec4::Mat4x4TransformPoint3(GetSimdValues(), rhs.GetSimdValue()));
@@ -595,6 +652,12 @@ namespace AZ
}
AZ_MATH_INLINE Vector3 Matrix4x4::RetrieveScaleSq() const
{
return Vector3(GetBasisX().GetLengthSq(), GetBasisY().GetLengthSq(), GetBasisZ().GetLengthSq());
}
AZ_MATH_INLINE Vector3 Matrix4x4::ExtractScale()
{
Vector4 x = GetBasisX();
@@ -619,6 +682,14 @@ namespace AZ
}
AZ_MATH_INLINE Matrix4x4 Matrix4x4::GetReciprocalScaled() const
{
Matrix4x4 result = *this;
result.MultiplyByScale(RetrieveScaleSq().GetReciprocal());
return result;
}
AZ_MATH_INLINE bool Matrix4x4::IsClose(const Matrix4x4& rhs, float tolerance) const
{
const Simd::Vec4::FloatType vecTolerance = Simd::Vec4::Splat(tolerance);
@@ -702,4 +773,10 @@ namespace AZ
lhs = lhs * rhs;
return lhs;
}
}
AZ_MATH_INLINE Matrix4x4 operator*(float lhs, const Matrix4x4& rhs)
{
return rhs * lhs;
}
} // namespace AZ
@@ -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)
{
@@ -97,7 +110,8 @@ namespace AZ
return context.Report(Tasks::RetrieveInfo, Outcomes::Unknown,
AZStd::string::format("Failed to retrieve rtti information for %s.", classData->m_name));
}
AZ_Assert(classData->m_azRtti->GetTypeId() == typeId, "Type id mismatch during deserialization of a json file. (%s vs %s)");
AZ_Assert(classData->m_azRtti->GetTypeId() == typeId, "Type id mismatch during deserialization of a json file. (%s vs %s)",
classData->m_azRtti->GetTypeId().ToString<AZStd::string>().c_str(), typeId.ToString<AZStd::string>().c_str());
void** objectPtr = reinterpret_cast<void**>(object);
bool isNull = *objectPtr == nullptr;
@@ -512,27 +526,24 @@ namespace AZ
if (*object)
{
const AZ::Uuid& actualClassId = rtti.GetActualUuid(*object);
if (actualClassId != objectType)
const SerializeContext::ClassData* actualClassData = context.GetSerializeContext()->FindClassData(actualClassId);
if (!actualClassData)
{
const SerializeContext::ClassData* actualClassData = context.GetSerializeContext()->FindClassData(actualClassId);
if (!actualClassData)
{
status = context.Report(Tasks::RetrieveInfo, Outcomes::Unknown,
AZStd::string::format("Unable to find serialization information for type %s.", actualClassId.ToString<AZStd::string>().c_str()));
return ResolvePointerResult::FullyProcessed;
}
status = context.Report(Tasks::RetrieveInfo, Outcomes::Unknown,
AZStd::string::format("Unable to find serialization information for type %s.", actualClassId.ToString<AZStd::string>().c_str()));
return ResolvePointerResult::FullyProcessed;
}
if (actualClassData->m_factory)
{
actualClassData->m_factory->Destroy(*object);
*object = nullptr;
}
else
{
status = context.Report(Tasks::RetrieveInfo, Outcomes::Catastrophic,
"Unable to find the factory needed to clear out the default value.");
return ResolvePointerResult::FullyProcessed;
}
if (actualClassData->m_factory)
{
actualClassData->m_factory->Destroy(*object);
*object = nullptr;
}
else
{
status = context.Report(Tasks::RetrieveInfo, Outcomes::Catastrophic,
"Unable to find the factory needed to clear out the default value.");
return ResolvePointerResult::FullyProcessed;
}
}
status = ResultCode(Tasks::ReadField, Outcomes::Success);
@@ -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
@@ -38,6 +38,20 @@ namespace AZ
};
//! Core class to handle serialization to and from json documents.
//! The Json Serialization works by taking a default constructed object and then apply the information found in the JSON document
//! on top of that object. This allows the Json Serialization to avoid storing default values and helps guarantee that the final
//! object is in a valid state even if non-fatal issues are encountered.
//! Note on containers: Containers such as vector or map are always considered to be empty even if there's entries in the provided
//! default object. During deserialization entries will be appended to any existing values. A flag is provided to automatically
//! clear containers during deserialization.
//! Note on maps: If the key for map containers such as unordered_map can be interpret as a string the Json Serialization will use
//! a JSON Object to store the data in instead of an array with key/value objects.
//! Note on pointers: The Json Serialization assumes that are always constructed, so a default JSON value of "{}" is interpret as
//! creating a new default instance even if the default value is a null pointer. A JSON Null needs to be explicitly stored in
//! the JSON Document in order to default or explicitly set a pointer to null.
//! Note on pointer memory: Objects created/destroyed by the Json Serialization for pointers require that the AZ_CLASS_ALLOCATOR is
//! declared and the object is created using aznew or memory is allocated using azmalloc. Without these the application may
//! crash if the Json Serialization tries to create or destroy an object pointed to by a pointer.
class JsonSerialization final
{
public:
@@ -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);
@@ -23,13 +23,6 @@ namespace AZ
{
namespace JSR = JsonSerializationResult;
if (IsExplicitDefault(inputValue))
{
// Do nothing if the input is an explicit default.
return context.Report(JSR::Tasks::ReadField, JSR::Outcomes::DefaultsUsed,
"Default value for smart pointer requested so no change was made.");
}
const SerializeContext::ClassData* containerClass = context.GetSerializeContext()->FindClassData(outputValueTypeId);
if (!containerClass)
{
@@ -89,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;
}
}
@@ -100,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);
@@ -153,8 +146,7 @@ namespace AZ
if (defaultValue)
{
bool typesMatch = false;
auto defaultInputCallback = [&defaultValue, &inputPtrType, &typesMatch]
auto defaultInputCallback = [&defaultValue]
(void* elementPtr, const Uuid&, const SerializeContext::ClassData*, const SerializeContext::ClassElement*)
{
defaultValue = elementPtr;
@@ -163,13 +155,14 @@ namespace AZ
container->EnumElements(const_cast<void*>(defaultValue), defaultInputCallback);
}
JSR::ResultCode result = ContinueStoring(outputValue, inputValue, defaultValue, inputPtrType, context, Flags::ResolvePointer);
if (result.GetOutcome() == JSR::Outcomes::DefaultsUsed)
{
outputValue = GetExplicitDefault();
return context.Report(result, "Smart pointer used all defaults.");
}
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
@@ -330,4 +330,163 @@ namespace UnitTest
}
}
}
// The AssetManagerStreamerImmediateCompletionTests class adjusts the asset loading to force it to complete immediately,
// while still within the callstack for GetAsset(). This can be used to test various conditions in which the load thread
// completes more rapidly than expected, and can expose subtle race conditions.
// There are a few key things that this class does to make this work:
// - The file I/O streamer is mocked
// - The asset stream data is mocked to a 0-byte length for the asset so that the stream load will bypass the I/O streamer and
// just immediately return completion.
// - The number of JobManager threads is set to 0, forcing jobs to execute synchronously inline when they are started.
// With these changes, GetAssetInternal() will queue the stream, which will immediately call the callback that creates LoadAssetJob,
// which immediately executes in-place to process the asset due to the synchronous JobManager.
// Note that if we just created the asset in a Ready state, most of the asset loading code is completely bypassed, and so we
// wouldn't be able to test for race conditions in the AssetContainer.
//
// This class also unregisters the catalog and asset handler before shutting down the asset manager. This is done to catch
// any outstanding asset references that exist due to loads not completing and cleaning up successfully.
struct AssetManagerStreamerImmediateCompletionTests : public BaseAssetManagerTest,
public AZ::Data::AssetCatalogRequestBus::Handler,
public AZ::Data::AssetHandler,
public AZ::Data::AssetCatalog
{
static inline const AZ::Uuid TestAssetId{"{E970B177-5F45-44EB-A2C4-9F29D9A0B2A2}"};
static inline constexpr AZStd::string_view TestAssetPath = "test";
void SetUp() override
{
BaseAssetManagerTest::SetUp();
AssetManager::Descriptor desc;
AssetManager::Create(desc);
// Register the handler and catalog after creation, because we intend to destroy them before AssetManager destruction.
// The specific asset we load is irrelevant, so register EmptyAsset.
AZ::Data::AssetManager::Instance().RegisterHandler(this, AZ::AzTypeInfo<EmptyAsset>::Uuid());
AZ::Data::AssetManager::Instance().RegisterCatalog(this, AZ::AzTypeInfo<EmptyAsset>::Uuid());
// Intercept messages for finding assets by name so that we can mock out the asset we're loading.
AZ::Data::AssetCatalogRequestBus::Handler::BusConnect();
}
void TearDown() override
{
// Unregister before destroying AssetManager.
// This will catch any assets that got stuck in a loading state without getting cleaned up.
AZ::Data::AssetManager::Instance().UnregisterCatalog(this);
AZ::Data::AssetManager::Instance().UnregisterHandler(this);
AZ::Data::AssetCatalogRequestBus::Handler::BusDisconnect();
AssetManager::Destroy();
BaseAssetManagerTest::TearDown();
}
size_t GetNumJobManagerThreads() const override
{
// Return 0 threads so that the Job Manager executes jobs synchronously inline. This lets us finish a load while still
// in the callstack that initiates the load.
return 0;
}
// Create a mock streamer instead of a real one, since we don't really want to load an asset.
IO::IStreamer* CreateStreamer() override
{
m_mockStreamer = AZStd::make_unique<StreamerWrapper>();
return &(m_mockStreamer->m_mockStreamer);
}
void DestroyStreamer([[maybe_unused]] IO::IStreamer* streamer) override
{
m_mockStreamer = nullptr;
}
// AssetHandler implementation
// Minimalist mock to create a new EmptyAsset with the desired asset ID.
AZ::Data::AssetPtr CreateAsset(const AZ::Data::AssetId& id, [[maybe_unused]] const AZ::Data::AssetType& type) override
{
return new EmptyAsset(id);
}
void DestroyAsset(AZ::Data::AssetPtr ptr) override
{
delete ptr;
}
// The mocked-out Asset Catalog handles EmptyAsset types.
void GetHandledAssetTypes(AZStd::vector<AZ::Data::AssetType>& assetTypes) override
{
assetTypes.push_back(AZ::AzTypeInfo<EmptyAsset>::Uuid());
}
// This is a mocked-out load, so just immediately return completion without doing anything.
AZ::Data::AssetHandler::LoadResult LoadAssetData(
[[maybe_unused]] const AZ::Data::Asset<AZ::Data::AssetData>& asset,
[[maybe_unused]] AZStd::shared_ptr<AZ::Data::AssetDataStream> stream,
[[maybe_unused]] const AZ::Data::AssetFilterCB& assetLoadFilterCB)
{
return AZ::Data::AssetHandler::LoadResult::LoadComplete;
}
// AssetCatalogRequestBus implementation
// Minimalist mocks to provide our desired asset path or asset id
AZStd::string GetAssetPathById([[maybe_unused]] const AZ::Data::AssetId& id) override
{
return TestAssetPath;
}
AZ::Data::AssetId GetAssetIdByPath(
[[maybe_unused]] const char* path, [[maybe_unused]] const AZ::Data::AssetType& typeToRegister,
[[maybe_unused]] bool autoRegisterIfNotFound) override
{
return TestAssetId;
}
// Return the mocked-out information for our test asset
AZ::Data::AssetInfo GetAssetInfoById([[maybe_unused]] const AZ::Data::AssetId& id) override
{
AZ::Data::AssetInfo assetInfo;
assetInfo.m_assetId = TestAssetId;
assetInfo.m_assetType = AZ::AzTypeInfo<EmptyAsset>::Uuid();
assetInfo.m_relativePath = TestAssetPath;
return assetInfo;
}
// AssetCatalog implementation
// Set the mocked-out asset load to have a 0-byte length so that the load skips I/O and immediately returns success
AZ::Data::AssetStreamInfo GetStreamInfoForLoad(
[[maybe_unused]] const AZ::Data::AssetId& id, const AZ::Data::AssetType& type) override
{
EXPECT_TRUE(type == AZ::AzTypeInfo<EmptyAsset>::Uuid());
AZ::Data::AssetStreamInfo info;
info.m_dataOffset = 0;
info.m_streamName = TestAssetPath;
info.m_dataLen = 0;
info.m_streamFlags = AZ::IO::OpenMode::ModeRead;
return info;
}
AZStd::unique_ptr<StreamerWrapper> m_mockStreamer;
};
// This test will verify that even if the asset loading stream/job returns immediately, all of the loading
// code works successfully. The test here is fairly simple - it just loads the asset and verifies that it
// loaded successfully. The bulk of the test is really in the setup class above, where the load is forced
// to complete immediately. Also, the true failure condition is caught in the setup class too, which is
// the presence of any assets at the point that the asset handler is unregistered. If they're present, then
// the immediate load wasn't truly successful, as it left around extra references to the asset that haven't
// been cleaned up.
TEST_F(AssetManagerStreamerImmediateCompletionTests, LoadAssetWithImmediateJobCompletion_WorksSuccessfully)
{
AZ::Data::AssetLoadParameters loadParams;
auto testAsset =
AssetManager::Instance().GetAsset<EmptyAsset>(TestAssetId, AZ::Data::AssetLoadBehavior::Default, loadParams);
AZ::Data::AssetManager::Instance().DispatchEvents();
EXPECT_TRUE(testAsset.IsReady());
}
} // namespace UnitTest
@@ -24,6 +24,13 @@ namespace UnitTest
public:
AZ_CLASS_ALLOCATOR(EmptyAsset, AZ::SystemAllocator, 0);
AZ_RTTI(EmptyAsset, "{098E3F7F-13AC-414B-9B4E-49B5AD1BD7FE}", AZ::Data::AssetData);
EmptyAsset(
const AZ::Data::AssetId& assetId = AZ::Data::AssetId(),
AZ::Data::AssetData::AssetStatus status = AZ::Data::AssetData::AssetStatus::NotLoaded)
: AZ::Data::AssetData(assetId, status)
{
}
};
// EmptyAssetWithNoHandler: no data contained within, and no AssetHandler registered for this type
@@ -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>();
@@ -14,6 +14,7 @@
#include <AzCore/Math/Transform.h>
#include <AzCore/Math/Quaternion.h>
#include <AzCore/UnitTest/TestTypes.h>
#include <AZTestShared/Math/MathTestHelpers.h>
using namespace AZ;
@@ -251,19 +252,19 @@ namespace UnitTest
m2.SetRow(2, 13.0f, 14.0f, 15.0f);
Matrix3x3 m3 = m1 * m2;
AZ_TEST_ASSERT(m3.GetRow(0).IsClose(Vector3(66.0f, 72.0f, 78.0f)));
AZ_TEST_ASSERT(m3.GetRow(1).IsClose(Vector3(156.0f, 171.0f, 186.0f)));
AZ_TEST_ASSERT(m3.GetRow(2).IsClose(Vector3(246.0f, 270.0f, 294.0f)));
EXPECT_THAT(m3.GetRow(0), IsClose(Vector3(66.0f, 72.0f, 78.0f)));
EXPECT_THAT(m3.GetRow(1), IsClose(Vector3(156.0f, 171.0f, 186.0f)));
EXPECT_THAT(m3.GetRow(2), IsClose(Vector3(246.0f, 270.0f, 294.0f)));
Matrix3x3 m4 = m1;
m4 *= m2;
AZ_TEST_ASSERT(m4.GetRow(0).IsClose(Vector3(66.0f, 72.0f, 78.0f)));
AZ_TEST_ASSERT(m4.GetRow(1).IsClose(Vector3(156.0f, 171.0f, 186.0f)));
AZ_TEST_ASSERT(m4.GetRow(2).IsClose(Vector3(246.0f, 270.0f, 294.0f)));
EXPECT_THAT(m4.GetRow(0), IsClose(Vector3(66.0f, 72.0f, 78.0f)));
EXPECT_THAT(m4.GetRow(1), IsClose(Vector3(156.0f, 171.0f, 186.0f)));
EXPECT_THAT(m4.GetRow(2), IsClose(Vector3(246.0f, 270.0f, 294.0f)));
m3 = m1.TransposedMultiply(m2);
AZ_TEST_ASSERT(m3.GetRow(0).IsClose(Vector3(138.0f, 150.0f, 162.0f)));
AZ_TEST_ASSERT(m3.GetRow(1).IsClose(Vector3(168.0f, 183.0f, 198.0f)));
AZ_TEST_ASSERT(m3.GetRow(2).IsClose(Vector3(198.0f, 216.0f, 234.0f)));
EXPECT_THAT(m3.GetRow(0), IsClose(Vector3(138.0f, 150.0f, 162.0f)));
EXPECT_THAT(m3.GetRow(1), IsClose(Vector3(168.0f, 183.0f, 198.0f)));
EXPECT_THAT(m3.GetRow(2), IsClose(Vector3(198.0f, 216.0f, 234.0f)));
}
TEST(MATH_Matrix3x3, TestVectorMultiplication)
@@ -277,11 +278,11 @@ namespace UnitTest
m2.SetRow(1, 10.0f, 11.0f, 12.0f);
m2.SetRow(2, 13.0f, 14.0f, 15.0f);
AZ_TEST_ASSERT((m1 * Vector3(1.0f, 2.0f, 3.0f)).IsClose(Vector3(14.0f, 32.0f, 50.0f)));
EXPECT_THAT((m1 * Vector3(1.0f, 2.0f, 3.0f)), IsClose(Vector3(14.0f, 32.0f, 50.0f)));
Vector3 v1(1.0f, 2.0f, 3.0f);
AZ_TEST_ASSERT((v1 * m1).IsClose(Vector3(30.0f, 36.0f, 42.0f)));
EXPECT_THAT((v1 * m1), IsClose(Vector3(30.0f, 36.0f, 42.0f)));
v1 *= m1;
AZ_TEST_ASSERT(v1.IsClose(Vector3(30.0f, 36.0f, 42.0f)));
EXPECT_THAT(v1, IsClose(Vector3(30.0f, 36.0f, 42.0f)));
}
TEST(MATH_Matrix3x3, TestSum)
@@ -296,15 +297,15 @@ namespace UnitTest
m2.SetRow(2, 13.0f, 14.0f, 15.0f);
Matrix3x3 m3 = m1 + m2;
AZ_TEST_ASSERT(m3.GetRow(0).IsClose(Vector3(8.0f, 10.0f, 12.0f)));
AZ_TEST_ASSERT(m3.GetRow(1).IsClose(Vector3(14.0f, 16.0f, 18.0f)));
AZ_TEST_ASSERT(m3.GetRow(2).IsClose(Vector3(20.0f, 22.0f, 24.0f)));
EXPECT_THAT(m3.GetRow(0), IsClose(Vector3(8.0f, 10.0f, 12.0f)));
EXPECT_THAT(m3.GetRow(1), IsClose(Vector3(14.0f, 16.0f, 18.0f)));
EXPECT_THAT(m3.GetRow(2), IsClose(Vector3(20.0f, 22.0f, 24.0f)));
m3 = m1;
m3 += m2;
AZ_TEST_ASSERT(m3.GetRow(0).IsClose(Vector3(8.0f, 10.0f, 12.0f)));
AZ_TEST_ASSERT(m3.GetRow(1).IsClose(Vector3(14.0f, 16.0f, 18.0f)));
AZ_TEST_ASSERT(m3.GetRow(2).IsClose(Vector3(20.0f, 22.0f, 24.0f)));
EXPECT_THAT(m3.GetRow(0), IsClose(Vector3(8.0f, 10.0f, 12.0f)));
EXPECT_THAT(m3.GetRow(1), IsClose(Vector3(14.0f, 16.0f, 18.0f)));
EXPECT_THAT(m3.GetRow(2), IsClose(Vector3(20.0f, 22.0f, 24.0f)));
}
TEST(MATH_Matrix3x3, TestDifference)
@@ -319,14 +320,14 @@ namespace UnitTest
m2.SetRow(2, 13.0f, 14.0f, 15.0f);
Matrix3x3 m3 = m1 - m2;
AZ_TEST_ASSERT(m3.GetRow(0).IsClose(Vector3(-6.0f, -6.0f, -6.0f)));
AZ_TEST_ASSERT(m3.GetRow(1).IsClose(Vector3(-6.0f, -6.0f, -6.0f)));
AZ_TEST_ASSERT(m3.GetRow(2).IsClose(Vector3(-6.0f, -6.0f, -6.0f)));
EXPECT_THAT(m3.GetRow(0), IsClose(Vector3(-6.0f, -6.0f, -6.0f)));
EXPECT_THAT(m3.GetRow(1), IsClose(Vector3(-6.0f, -6.0f, -6.0f)));
EXPECT_THAT(m3.GetRow(2), IsClose(Vector3(-6.0f, -6.0f, -6.0f)));
m3 = m1;
m3 -= m2;
AZ_TEST_ASSERT(m3.GetRow(0).IsClose(Vector3(-6.0f, -6.0f, -6.0f)));
AZ_TEST_ASSERT(m3.GetRow(1).IsClose(Vector3(-6.0f, -6.0f, -6.0f)));
AZ_TEST_ASSERT(m3.GetRow(2).IsClose(Vector3(-6.0f, -6.0f, -6.0f)));
EXPECT_THAT(m3.GetRow(0), IsClose(Vector3(-6.0f, -6.0f, -6.0f)));
EXPECT_THAT(m3.GetRow(1), IsClose(Vector3(-6.0f, -6.0f, -6.0f)));
EXPECT_THAT(m3.GetRow(2), IsClose(Vector3(-6.0f, -6.0f, -6.0f)));
}
TEST(MATH_Matrix3x3, TestScalarMultiplication)
@@ -341,18 +342,18 @@ namespace UnitTest
m2.SetRow(2, 13.0f, 14.0f, 15.0f);
Matrix3x3 m3 = m1 * 2.0f;
AZ_TEST_ASSERT(m3.GetRow(0).IsClose(Vector3(2.0f, 4.0f, 6.0f)));
AZ_TEST_ASSERT(m3.GetRow(1).IsClose(Vector3(8.0f, 10.0f, 12.0f)));
AZ_TEST_ASSERT(m3.GetRow(2).IsClose(Vector3(14.0f, 16.0f, 18.0f)));
EXPECT_THAT(m3.GetRow(0), IsClose(Vector3(2.0f, 4.0f, 6.0f)));
EXPECT_THAT(m3.GetRow(1), IsClose(Vector3(8.0f, 10.0f, 12.0f)));
EXPECT_THAT(m3.GetRow(2), IsClose(Vector3(14.0f, 16.0f, 18.0f)));
m3 = m1;
m3 *= 2.0f;
AZ_TEST_ASSERT(m3.GetRow(0).IsClose(Vector3(2.0f, 4.0f, 6.0f)));
AZ_TEST_ASSERT(m3.GetRow(1).IsClose(Vector3(8.0f, 10.0f, 12.0f)));
AZ_TEST_ASSERT(m3.GetRow(2).IsClose(Vector3(14.0f, 16.0f, 18.0f)));
EXPECT_THAT(m3.GetRow(0), IsClose(Vector3(2.0f, 4.0f, 6.0f)));
EXPECT_THAT(m3.GetRow(1), IsClose(Vector3(8.0f, 10.0f, 12.0f)));
EXPECT_THAT(m3.GetRow(2), IsClose(Vector3(14.0f, 16.0f, 18.0f)));
m3 = 2.0f * m1;
AZ_TEST_ASSERT(m3.GetRow(0).IsClose(Vector3(2.0f, 4.0f, 6.0f)));
AZ_TEST_ASSERT(m3.GetRow(1).IsClose(Vector3(8.0f, 10.0f, 12.0f)));
AZ_TEST_ASSERT(m3.GetRow(2).IsClose(Vector3(14.0f, 16.0f, 18.0f)));
EXPECT_THAT(m3.GetRow(0), IsClose(Vector3(2.0f, 4.0f, 6.0f)));
EXPECT_THAT(m3.GetRow(1), IsClose(Vector3(8.0f, 10.0f, 12.0f)));
EXPECT_THAT(m3.GetRow(2), IsClose(Vector3(14.0f, 16.0f, 18.0f)));
}
TEST(MATH_Matrix3x3, TestScalarDivision)
@@ -367,18 +368,32 @@ namespace UnitTest
m2.SetRow(2, 13.0f, 14.0f, 15.0f);
Matrix3x3 m3 = m1 / 0.5f;
AZ_TEST_ASSERT(m3.GetRow(0).IsClose(Vector3(2.0f, 4.0f, 6.0f)));
AZ_TEST_ASSERT(m3.GetRow(1).IsClose(Vector3(8.0f, 10.0f, 12.0f)));
AZ_TEST_ASSERT(m3.GetRow(2).IsClose(Vector3(14.0f, 16.0f, 18.0f)));
EXPECT_THAT(m3.GetRow(0), IsClose(Vector3(2.0f, 4.0f, 6.0f)));
EXPECT_THAT(m3.GetRow(1), IsClose(Vector3(8.0f, 10.0f, 12.0f)));
EXPECT_THAT(m3.GetRow(2), IsClose(Vector3(14.0f, 16.0f, 18.0f)));
m3 = m1;
m3 /= 0.5f;
AZ_TEST_ASSERT(m3.GetRow(0).IsClose(Vector3(2.0f, 4.0f, 6.0f)));
AZ_TEST_ASSERT(m3.GetRow(1).IsClose(Vector3(8.0f, 10.0f, 12.0f)));
AZ_TEST_ASSERT(m3.GetRow(2).IsClose(Vector3(14.0f, 16.0f, 18.0f)));
m3 = -m1;
AZ_TEST_ASSERT(m3.GetRow(0).IsClose(Vector3(-1.0f, -2.0f, -3.0f)));
AZ_TEST_ASSERT(m3.GetRow(1).IsClose(Vector3(-4.0f, -5.0f, -6.0f)));
AZ_TEST_ASSERT(m3.GetRow(2).IsClose(Vector3(-7.0f, -8.0f, -9.0f)));
EXPECT_THAT(m3.GetRow(0), IsClose(Vector3(2.0f, 4.0f, 6.0f)));
EXPECT_THAT(m3.GetRow(1), IsClose(Vector3(8.0f, 10.0f, 12.0f)));
EXPECT_THAT(m3.GetRow(2), IsClose(Vector3(14.0f, 16.0f, 18.0f)));
}
TEST(MATH_Matrix3x3, TestNegation)
{
Matrix3x3 m1;
m1.SetRow(0, 1.0f, 2.0f, 3.0f);
m1.SetRow(1, 4.0f, 5.0f, 6.0f);
m1.SetRow(2, 7.0f, 8.0f, 9.0f);
EXPECT_THAT(-(-m1), IsClose(m1));
EXPECT_THAT(-Matrix3x3::CreateZero(), IsClose(Matrix3x3::CreateZero()));
Matrix3x3 m2 = -m1;
EXPECT_THAT(m2.GetRow(0), IsClose(Vector3(-1.0f, -2.0f, -3.0f)));
EXPECT_THAT(m2.GetRow(1), IsClose(Vector3(-4.0f, -5.0f, -6.0f)));
EXPECT_THAT(m2.GetRow(2), IsClose(Vector3(-7.0f, -8.0f, -9.0f)));
Matrix3x3 m3 = m1 + (-m1);
EXPECT_THAT(m3, IsClose(Matrix3x3::CreateZero()));
}
TEST(MATH_Matrix3x3, TestTranspose)
@@ -425,11 +440,33 @@ namespace UnitTest
TEST(MATH_Matrix3x3, TestScaleAccess)
{
Matrix3x3 m1 = Matrix3x3::CreateRotationX(DegToRad(40.0f)) * Matrix3x3::CreateScale(Vector3(2.0f, 3.0f, 4.0f));
AZ_TEST_ASSERT(m1.RetrieveScale().IsClose(Vector3(2.0f, 3.0f, 4.0f)));
AZ_TEST_ASSERT(m1.ExtractScale().IsClose(Vector3(2.0f, 3.0f, 4.0f)));
AZ_TEST_ASSERT(m1.RetrieveScale().IsClose(Vector3::CreateOne()));
EXPECT_THAT(m1.RetrieveScale(), IsClose(Vector3(2.0f, 3.0f, 4.0f)));
EXPECT_THAT(m1.ExtractScale(), IsClose(Vector3(2.0f, 3.0f, 4.0f)));
EXPECT_THAT(m1.RetrieveScale(), IsClose(Vector3::CreateOne()));
m1.MultiplyByScale(Vector3(3.0f, 4.0f, 5.0f));
AZ_TEST_ASSERT(m1.RetrieveScale().IsClose(Vector3(3.0f, 4.0f, 5.0f)));
EXPECT_THAT(m1.RetrieveScale(), IsClose(Vector3(3.0f, 4.0f, 5.0f)));
}
TEST(MATH_Matrix3x3, TestScaleSqAccess)
{
Matrix3x3 m1 = Matrix3x3::CreateRotationX(DegToRad(40.0f)) * Matrix3x3::CreateScale(Vector3(2.0f, 3.0f, 4.0f));
EXPECT_THAT(m1.RetrieveScaleSq(), IsClose(Vector3(4.0f, 9.0f, 16.0f)));
m1.ExtractScale();
EXPECT_THAT(m1.RetrieveScaleSq(), IsClose(Vector3::CreateOne()));
m1.MultiplyByScale(Vector3(3.0f, 4.0f, 5.0f));
EXPECT_THAT(m1.RetrieveScaleSq(), IsClose(Vector3(9.0f, 16.0f, 25.0f)));
}
TEST(MATH_Matrix3x3, TestReciprocalScaled)
{
Matrix3x3 orthogonalMatrix = Matrix3x3::CreateRotationX(DegToRad(40.0f));
EXPECT_THAT(orthogonalMatrix.GetReciprocalScaled(), IsClose(orthogonalMatrix));
const AZ::Vector3 scale(2.8f, 0.7f, 1.3f);
AZ::Matrix3x3 scaledMatrix = orthogonalMatrix;
scaledMatrix.MultiplyByScale(scale);
AZ::Matrix3x3 reciprocalScaledMatrix = orthogonalMatrix;
reciprocalScaledMatrix.MultiplyByScale(scale.GetReciprocal());
EXPECT_THAT(scaledMatrix.GetReciprocalScaled(), IsClose(reciprocalScaledMatrix));
}
TEST(MATH_Matrix3x3, TestPolarDecomposition)
@@ -467,53 +467,136 @@ namespace UnitTest
EXPECT_THAT(matrix.Multiply3x3(axisDirection), IsClose(forwardDirection));
}
TEST(MATH_Matrix3x4, MultiplyByMatrix3x4)
TEST(MATH_Matrix3x4, TestMatrixMultiplication)
{
const AZ::Matrix3x4 matrix1 = AZ::Matrix3x4::CreateFromValue(1.2f);
const AZ::Matrix3x4 matrix2 = AZ::Matrix3x4::CreateDiagonal(AZ::Vector3(1.3f, 1.5f, 0.4f));
const AZ::Matrix3x4 matrix3 = AZ::Matrix3x4::CreateFromQuaternionAndTranslation(
AZ::Quaternion(0.42f, 0.46f, -0.66f, 0.42f), AZ::Vector3(2.8f, -3.7f, 1.6f));
const AZ::Matrix3x4 matrix4 = AZ::Matrix3x4::CreateRotationX(-0.7f) * AZ::Matrix3x4::CreateScale(AZ::Vector3(0.6f, 1.3f, 0.7f));
AZ::Matrix3x4 matrix5 = matrix1;
matrix5 *= matrix4;
const AZ::Vector3 vector(1.9f, 2.3f, 0.2f);
EXPECT_TRUE((matrix1 * (matrix2 * matrix3)).IsClose((matrix1 * matrix2) * matrix3));
EXPECT_THAT((matrix3 * matrix4) * vector, IsClose(matrix3 * (matrix4 * vector)));
EXPECT_TRUE((matrix2 * AZ::Matrix3x4::Identity()).IsClose(matrix2));
EXPECT_TRUE((matrix3 * AZ::Matrix3x4::Identity()).IsClose(AZ::Matrix3x4::Identity() * matrix3));
EXPECT_TRUE(matrix5.IsClose(matrix1 * matrix4));
AZ::Matrix3x4 m1;
m1.SetRow(0, 1.0f, 2.0f, 3.0f, 4.0f);
m1.SetRow(1, 5.0f, 6.0f, 7.0f, 8.0f);
m1.SetRow(2, 9.0f, 10.0f, 11.0f, 12.0f);
AZ::Matrix3x4 m2;
m2.SetRow(0, 7.0f, 8.0f, 9.0f, 10.0f);
m2.SetRow(1, 11.0f, 12.0f, 13.0f, 14.0f);
m2.SetRow(2, 15.0f, 16.0f, 17.0f, 18.0f);
AZ::Matrix3x4 m3 = m1 * m2;
EXPECT_THAT(m3.GetRow(0), IsClose(AZ::Vector4(74.0f, 80.0f, 86.0f, 96.0f)));
EXPECT_THAT(m3.GetRow(1), IsClose(AZ::Vector4(206.0f, 224.0f, 242.0f, 268.0f)));
EXPECT_THAT(m3.GetRow(2), IsClose(AZ::Vector4(338.0f, 368.0f, 398.0f, 440.0f)));
AZ::Matrix3x4 m4 = m1;
m4 *= m2;
EXPECT_THAT(m4.GetRow(0), IsClose(AZ::Vector4(74.0f, 80.0f, 86.0f, 96.0f)));
EXPECT_THAT(m4.GetRow(1), IsClose(AZ::Vector4(206.0f, 224.0f, 242.0f, 268.0f)));
EXPECT_THAT(m4.GetRow(2), IsClose(AZ::Vector4(338.0f, 368.0f, 398.0f, 440.0f)));
}
TEST(MATH_Matrix3x4, AddMatrix3x4)
TEST(MATH_Matrix3x4, TestSum)
{
const AZ::Matrix3x4 matrix1 = AZ::Matrix3x4::CreateFromValue(1.2f);
const AZ::Matrix3x4 matrix2 = AZ::Matrix3x4::CreateDiagonal(AZ::Vector3(1.3f, 1.5f, 0.4f));
const AZ::Matrix3x4 matrix3 = AZ::Matrix3x4::CreateFromQuaternionAndTranslation(
AZ::Quaternion(0.42f, 0.46f, -0.66f, 0.42f), AZ::Vector3(2.8f, -3.7f, 1.6f));
const AZ::Matrix3x4 matrix4 = AZ::Matrix3x4::CreateRotationX(-0.7f) * AZ::Matrix3x4::CreateScale(AZ::Vector3(0.6f, 1.3f, 0.7f));
AZ::Matrix3x4 matrix5 = matrix1;
matrix5 += matrix4;
EXPECT_THAT(matrix1 + (matrix2 + matrix3), IsClose((matrix1 + matrix2) + matrix3));
EXPECT_THAT(matrix2 + AZ::Matrix3x4::CreateZero(), IsClose(matrix2));
EXPECT_THAT(matrix3 + AZ::Matrix3x4::CreateZero(), IsClose(AZ::Matrix3x4::CreateZero() + matrix3));
EXPECT_THAT(matrix3 + matrix3, IsClose(matrix3 * 2.0f));
EXPECT_THAT(matrix5, IsClose(matrix1 + matrix4));
AZ::Matrix3x4 m1;
m1.SetRow(0, 1.0f, 2.0f, 3.0f, 4.0f);
m1.SetRow(1, 5.0f, 6.0f, 7.0f, 8.0f);
m1.SetRow(2, 9.0f, 10.0f, 11.0f, 12.0f);
AZ::Matrix3x4 m2;
m2.SetRow(0, 7.0f, 8.0f, 9.0f, 10.0f);
m2.SetRow(1, 11.0f, 12.0f, 13.0f, 14.0f);
m2.SetRow(2, 15.0f, 16.0f, 17.0f, 18.0f);
AZ::Matrix3x4 m3 = m1 + m2;
EXPECT_THAT(m3.GetRow(0), IsClose(AZ::Vector4(8.0f, 10.0f, 12.0f, 14.0f)));
EXPECT_THAT(m3.GetRow(1), IsClose(AZ::Vector4(16.0f, 18.0f, 20.0f, 22.0f)));
EXPECT_THAT(m3.GetRow(2), IsClose(AZ::Vector4(24.0f, 26.0f, 28.0f, 30.0f)));
m3 = m1;
m3 += m2;
EXPECT_THAT(m3.GetRow(0), IsClose(AZ::Vector4(8.0f, 10.0f, 12.0f, 14.0f)));
EXPECT_THAT(m3.GetRow(1), IsClose(AZ::Vector4(16.0f, 18.0f, 20.0f, 22.0f)));
EXPECT_THAT(m3.GetRow(2), IsClose(AZ::Vector4(24.0f, 26.0f, 28.0f, 30.0f)));
}
TEST(MATH_Matrix3x4, MultiplyByScalar)
TEST(MATH_Matrix3x4, TestDifference)
{
const AZ::Vector4 row0(1.488f, 2.56f, 0.096f, 2.3f);
const AZ::Vector4 row1(0.384f, -1.92f, 0.428f, -1.6f);
const AZ::Vector4 row2(1.28f, -2.4f, -0.24f, 3.7f);
const float scalar = 3.2f;
const AZ::Vector4 row0Result = row0 * scalar;
const AZ::Vector4 row1Result = row1 * scalar;
const AZ::Vector4 row2Result = row2 * scalar;
AZ::Matrix3x4 matrix = AZ::Matrix3x4::CreateFromRows(row0, row1, row2);
EXPECT_THAT(matrix * 0.0f, IsClose(AZ::Matrix3x4::CreateZero()));
EXPECT_THAT(matrix * 1.0f, IsClose(matrix));
EXPECT_THAT(matrix * scalar, IsClose(AZ::Matrix3x4::CreateFromRows(row0Result, row1Result, row2Result)));
EXPECT_THAT(matrix * 2.0f, IsClose(matrix + matrix));
AZ::Matrix3x4 m1;
m1.SetRow(0, 1.0f, 2.0f, 3.0f, 4.0f);
m1.SetRow(1, 5.0f, 6.0f, 7.0f, 8.0f);
m1.SetRow(2, 9.0f, 10.0f, 11.0f, 12.0f);
AZ::Matrix3x4 m2;
m2.SetRow(0, 7.0f, 8.0f, 9.0f, 10.0f);
m2.SetRow(1, 11.0f, 12.0f, 13.0f, 14.0f);
m2.SetRow(2, 15.0f, 16.0f, 17.0f, 18.0f);
AZ::Matrix3x4 m3 = m1 - m2;
EXPECT_THAT(m3.GetRow(0), IsClose(AZ::Vector4(-6.0f, -6.0f, -6.0f, -6.0f)));
EXPECT_THAT(m3.GetRow(1), IsClose(AZ::Vector4(-6.0f, -6.0f, -6.0f, -6.0f)));
EXPECT_THAT(m3.GetRow(2), IsClose(AZ::Vector4(-6.0f, -6.0f, -6.0f, -6.0f)));
m3 = m1;
m3 -= m2;
EXPECT_THAT(m3.GetRow(0), IsClose(AZ::Vector4(-6.0f, -6.0f, -6.0f, -6.0f)));
EXPECT_THAT(m3.GetRow(1), IsClose(AZ::Vector4(-6.0f, -6.0f, -6.0f, -6.0f)));
EXPECT_THAT(m3.GetRow(2), IsClose(AZ::Vector4(-6.0f, -6.0f, -6.0f, -6.0f)));
}
TEST(MATH_Matrix3x4, TestScalarMultiplication)
{
AZ::Matrix3x4 m1;
m1.SetRow(0, 1.0f, 2.0f, 3.0f, 4.0f);
m1.SetRow(1, 5.0f, 6.0f, 7.0f, 8.0f);
m1.SetRow(2, 9.0f, 10.0f, 11.0f, 12.0f);
AZ::Matrix3x4 m2;
m2.SetRow(0, 7.0f, 8.0f, 9.0f, 10.0f);
m2.SetRow(1, 11.0f, 12.0f, 13.0f, 14.0f);
m2.SetRow(2, 15.0f, 16.0f, 17.0f, 18.0f);
AZ::Matrix3x4 m3 = m1 * 2.0f;
EXPECT_THAT(m3.GetRow(0), IsClose(AZ::Vector4(2.0f, 4.0f, 6.0f, 8.0f)));
EXPECT_THAT(m3.GetRow(1), IsClose(AZ::Vector4(10.0f, 12.0f, 14.0f, 16.0f)));
EXPECT_THAT(m3.GetRow(2), IsClose(AZ::Vector4(18.0f, 20.0f, 22.0f, 24.0f)));
m3 = m1;
m3 *= 2.0f;
EXPECT_THAT(m3.GetRow(0), IsClose(AZ::Vector4(2.0f, 4.0f, 6.0f, 8.0f)));
EXPECT_THAT(m3.GetRow(1), IsClose(AZ::Vector4(10.0f, 12.0f, 14.0f, 16.0f)));
EXPECT_THAT(m3.GetRow(2), IsClose(AZ::Vector4(18.0f, 20.0f, 22.0f, 24.0f)));
m3 = 2.0f * m1;
EXPECT_THAT(m3.GetRow(0), IsClose(AZ::Vector4(2.0f, 4.0f, 6.0f, 8.0f)));
EXPECT_THAT(m3.GetRow(1), IsClose(AZ::Vector4(10.0f, 12.0f, 14.0f, 16.0f)));
EXPECT_THAT(m3.GetRow(2), IsClose(AZ::Vector4(18.0f, 20.0f, 22.0f, 24.0f)));
}
TEST(MATH_Matrix3x4, TestScalarDivision)
{
AZ::Matrix3x4 m1;
m1.SetRow(0, 1.0f, 2.0f, 3.0f, 4.0f);
m1.SetRow(1, 5.0f, 6.0f, 7.0f, 8.0f);
m1.SetRow(2, 9.0f, 10.0f, 11.0f, 12.0f);
AZ::Matrix3x4 m2;
m2.SetRow(0, 7.0f, 8.0f, 9.0f, 10.0f);
m2.SetRow(1, 11.0f, 12.0f, 13.0f, 14.0f);
m2.SetRow(2, 15.0f, 16.0f, 17.0f, 18.0f);
AZ::Matrix3x4 m3 = m1 / 0.5f;
EXPECT_THAT(m3.GetRow(0), IsClose(AZ::Vector4(2.0f, 4.0f, 6.0f, 8.0f)));
EXPECT_THAT(m3.GetRow(1), IsClose(AZ::Vector4(10.0f, 12.0f, 14.0f, 16.0f)));
EXPECT_THAT(m3.GetRow(2), IsClose(AZ::Vector4(18.0f, 20.0f, 22.0f, 24.0f)));
m3 = m1;
m3 /= 0.5f;
EXPECT_THAT(m3.GetRow(0), IsClose(AZ::Vector4(2.0f, 4.0f, 6.0f, 8.0f)));
EXPECT_THAT(m3.GetRow(1), IsClose(AZ::Vector4(10.0f, 12.0f, 14.0f, 16.0f)));
EXPECT_THAT(m3.GetRow(2), IsClose(AZ::Vector4(18.0f, 20.0f, 22.0f, 24.0f)));
}
TEST(MATH_Matrix3x4, TestNegation)
{
AZ::Matrix3x4 m1;
m1.SetRow(0, 1.0f, 2.0f, 3.0f, 4.0f);
m1.SetRow(1, 5.0f, 6.0f, 7.0f, 8.0f);
m1.SetRow(2, 9.0f, 10.0f, 11.0f, 12.0f);
EXPECT_THAT(-(-m1), IsClose(m1));
EXPECT_THAT(-AZ::Matrix3x4::CreateZero(), IsClose(AZ::Matrix3x4::CreateZero()));
AZ::Matrix3x4 m2 = -m1;
EXPECT_THAT(m2.GetRow(0), IsClose(AZ::Vector4(-1.0f, -2.0f, -3.0f, -4.0f)));
EXPECT_THAT(m2.GetRow(1), IsClose(AZ::Vector4(-5.0f, -6.0f, -7.0f, -8.0f)));
EXPECT_THAT(m2.GetRow(2), IsClose(AZ::Vector4(-9.0f, -10.0f, -11.0f, -12.0f)));
AZ::Matrix3x4 m3 = m1 + (-m1);
EXPECT_THAT(m3, IsClose(AZ::Matrix3x4::CreateZero()));
}
TEST(MATH_Matrix3x4, MultiplyByVector3)
@@ -246,16 +246,16 @@ namespace UnitTest
m2.SetRow(2, 15.0f, 16.0f, 17.0f, 18.0f);
m2.SetRow(3, 19.0f, 20.0f, 21.0f, 22.0f);
Matrix4x4 m3 = m1 * m2;
AZ_TEST_ASSERT(m3.GetRow(0).IsClose(Vector4(150.0f, 160.0f, 170.0f, 180.0f)));
AZ_TEST_ASSERT(m3.GetRow(1).IsClose(Vector4(358.0f, 384.0f, 410.0f, 436.0f)));
AZ_TEST_ASSERT(m3.GetRow(2).IsClose(Vector4(566.0f, 608.0f, 650.0f, 692.0f)));
AZ_TEST_ASSERT(m3.GetRow(3).IsClose(Vector4(774.0f, 832.0f, 890.0f, 948.0f)));
EXPECT_THAT(m3.GetRow(0), IsClose(Vector4(150.0f, 160.0f, 170.0f, 180.0f)));
EXPECT_THAT(m3.GetRow(1), IsClose(Vector4(358.0f, 384.0f, 410.0f, 436.0f)));
EXPECT_THAT(m3.GetRow(2), IsClose(Vector4(566.0f, 608.0f, 650.0f, 692.0f)));
EXPECT_THAT(m3.GetRow(3), IsClose(Vector4(774.0f, 832.0f, 890.0f, 948.0f)));
Matrix4x4 m4 = m1;
m4 *= m2;
AZ_TEST_ASSERT(m4.GetRow(0).IsClose(Vector4(150.0f, 160.0f, 170.0f, 180.0f)));
AZ_TEST_ASSERT(m4.GetRow(1).IsClose(Vector4(358.0f, 384.0f, 410.0f, 436.0f)));
AZ_TEST_ASSERT(m4.GetRow(2).IsClose(Vector4(566.0f, 608.0f, 650.0f, 692.0f)));
AZ_TEST_ASSERT(m4.GetRow(3).IsClose(Vector4(774.0f, 832.0f, 890.0f, 948.0f)));
EXPECT_THAT(m4.GetRow(0), IsClose(Vector4(150.0f, 160.0f, 170.0f, 180.0f)));
EXPECT_THAT(m4.GetRow(1), IsClose(Vector4(358.0f, 384.0f, 410.0f, 436.0f)));
EXPECT_THAT(m4.GetRow(2), IsClose(Vector4(566.0f, 608.0f, 650.0f, 692.0f)));
EXPECT_THAT(m4.GetRow(3), IsClose(Vector4(774.0f, 832.0f, 890.0f, 948.0f)));
}
TEST(MATH_Matrix4x4, TestVectorMultiplication)
@@ -265,18 +265,148 @@ namespace UnitTest
m1.SetRow(1, 5.0f, 6.0f, 7.0f, 8.0f);
m1.SetRow(2, 9.0f, 10.0f, 11.0f, 12.0f);
m1.SetRow(3, 13.0f, 14.0f, 15.0f, 16.0f);
AZ_TEST_ASSERT((m1 * Vector3(1.0f, 2.0f, 3.0f)).IsClose(Vector3(18.0f, 46.0f, 74.0f)));
AZ_TEST_ASSERT((m1 * Vector4(1.0f, 2.0f, 3.0f, 4.0f)).IsClose(Vector4(30.0f, 70.0f, 110.0f, 150.0f)));
AZ_TEST_ASSERT(m1.TransposedMultiply3x3(Vector3(1.0f, 2.0f, 3.0f)).IsClose(Vector3(38.0f, 44.0f, 50.0f)));
AZ_TEST_ASSERT(m1.Multiply3x3(Vector3(1.0f, 2.0f, 3.0f)).IsClose(Vector3(14.0f, 38.0f, 62.0f)));
EXPECT_THAT((m1 * Vector3(1.0f, 2.0f, 3.0f)), IsClose(Vector3(18.0f, 46.0f, 74.0f)));
EXPECT_THAT((m1 * Vector4(1.0f, 2.0f, 3.0f, 4.0f)), IsClose(Vector4(30.0f, 70.0f, 110.0f, 150.0f)));
EXPECT_THAT(m1.TransposedMultiply3x3(Vector3(1.0f, 2.0f, 3.0f)), IsClose(Vector3(38.0f, 44.0f, 50.0f)));
EXPECT_THAT(m1.Multiply3x3(Vector3(1.0f, 2.0f, 3.0f)), IsClose(Vector3(14.0f, 38.0f, 62.0f)));
Vector3 v1(1.0f, 2.0f, 3.0f);
AZ_TEST_ASSERT((v1 * m1).IsClose(Vector3(51.0f, 58.0f, 65.0f)));
EXPECT_THAT((v1 * m1), IsClose(Vector3(51.0f, 58.0f, 65.0f)));
v1 *= m1;
AZ_TEST_ASSERT(v1.IsClose(Vector3(51.0f, 58.0f, 65.0f)));
EXPECT_THAT(v1, IsClose(Vector3(51.0f, 58.0f, 65.0f)));
Vector4 v2(1.0f, 2.0f, 3.0f, 4.0f);
AZ_TEST_ASSERT((v2 * m1).IsClose(Vector4(90.0f, 100.0f, 110.0f, 120.0f)));
EXPECT_THAT((v2 * m1), IsClose(Vector4(90.0f, 100.0f, 110.0f, 120.0f)));
v2 *= m1;
AZ_TEST_ASSERT(v2.IsClose(Vector4(90.0f, 100.0f, 110.0f, 120.0f)));
EXPECT_THAT(v2, IsClose(Vector4(90.0f, 100.0f, 110.0f, 120.0f)));
}
TEST(MATH_Matrix4x4, TestSum)
{
Matrix4x4 m1;
m1.SetRow(0, 1.0f, 2.0f, 3.0f, 4.0f);
m1.SetRow(1, 5.0f, 6.0f, 7.0f, 8.0f);
m1.SetRow(2, 9.0f, 10.0f, 11.0f, 12.0f);
m1.SetRow(3, 13.0f, 14.0f, 15.0f, 16.0f);
Matrix4x4 m2;
m2.SetRow(0, 7.0f, 8.0f, 9.0f, 10.0f);
m2.SetRow(1, 11.0f, 12.0f, 13.0f, 14.0f);
m2.SetRow(2, 15.0f, 16.0f, 17.0f, 18.0f);
m2.SetRow(3, 19.0f, 20.0f, 21.0f, 22.0f);
Matrix4x4 m3 = m1 + m2;
EXPECT_THAT(m3.GetRow(0), IsClose(Vector4(8.0f, 10.0f, 12.0f, 14.0f)));
EXPECT_THAT(m3.GetRow(1), IsClose(Vector4(16.0f, 18.0f, 20.0f, 22.0f)));
EXPECT_THAT(m3.GetRow(2), IsClose(Vector4(24.0f, 26.0f, 28.0f, 30.0f)));
EXPECT_THAT(m3.GetRow(3), IsClose(Vector4(32.0f, 34.0f, 36.0f, 38.0f)));
m3 = m1;
m3 += m2;
EXPECT_THAT(m3.GetRow(0), IsClose(Vector4(8.0f, 10.0f, 12.0f, 14.0f)));
EXPECT_THAT(m3.GetRow(1), IsClose(Vector4(16.0f, 18.0f, 20.0f, 22.0f)));
EXPECT_THAT(m3.GetRow(2), IsClose(Vector4(24.0f, 26.0f, 28.0f, 30.0f)));
EXPECT_THAT(m3.GetRow(3), IsClose(Vector4(32.0f, 34.0f, 36.0f, 38.0f)));
}
TEST(MATH_Matrix4x4, TestDifference)
{
Matrix4x4 m1;
m1.SetRow(0, 1.0f, 2.0f, 3.0f, 4.0f);
m1.SetRow(1, 5.0f, 6.0f, 7.0f, 8.0f);
m1.SetRow(2, 9.0f, 10.0f, 11.0f, 12.0f);
m1.SetRow(3, 13.0f, 14.0f, 15.0f, 16.0f);
Matrix4x4 m2;
m2.SetRow(0, 7.0f, 8.0f, 9.0f, 10.0f);
m2.SetRow(1, 11.0f, 12.0f, 13.0f, 14.0f);
m2.SetRow(2, 15.0f, 16.0f, 17.0f, 18.0f);
m2.SetRow(3, 19.0f, 20.0f, 21.0f, 22.0f);
Matrix4x4 m3 = m1 - m2;
EXPECT_THAT(m3.GetRow(0), IsClose(Vector4(-6.0f, -6.0f, -6.0f, -6.0f)));
EXPECT_THAT(m3.GetRow(1), IsClose(Vector4(-6.0f, -6.0f, -6.0f, -6.0f)));
EXPECT_THAT(m3.GetRow(2), IsClose(Vector4(-6.0f, -6.0f, -6.0f, -6.0f)));
EXPECT_THAT(m3.GetRow(3), IsClose(Vector4(-6.0f, -6.0f, -6.0f, -6.0f)));
m3 = m1;
m3 -= m2;
EXPECT_THAT(m3.GetRow(0), IsClose(Vector4(-6.0f, -6.0f, -6.0f, -6.0f)));
EXPECT_THAT(m3.GetRow(1), IsClose(Vector4(-6.0f, -6.0f, -6.0f, -6.0f)));
EXPECT_THAT(m3.GetRow(2), IsClose(Vector4(-6.0f, -6.0f, -6.0f, -6.0f)));
EXPECT_THAT(m3.GetRow(3), IsClose(Vector4(-6.0f, -6.0f, -6.0f, -6.0f)));
}
TEST(MATH_Matrix4x4, TestScalarMultiplication)
{
Matrix4x4 m1;
m1.SetRow(0, 1.0f, 2.0f, 3.0f, 4.0f);
m1.SetRow(1, 5.0f, 6.0f, 7.0f, 8.0f);
m1.SetRow(2, 9.0f, 10.0f, 11.0f, 12.0f);
m1.SetRow(3, 13.0f, 14.0f, 15.0f, 16.0f);
Matrix4x4 m2;
m2.SetRow(0, 7.0f, 8.0f, 9.0f, 10.0f);
m2.SetRow(1, 11.0f, 12.0f, 13.0f, 14.0f);
m2.SetRow(2, 15.0f, 16.0f, 17.0f, 18.0f);
m2.SetRow(3, 19.0f, 20.0f, 21.0f, 22.0f);
Matrix4x4 m3 = m1 * 2.0f;
EXPECT_THAT(m3.GetRow(0), IsClose(Vector4(2.0f, 4.0f, 6.0f, 8.0f)));
EXPECT_THAT(m3.GetRow(1), IsClose(Vector4(10.0f, 12.0f, 14.0f, 16.0f)));
EXPECT_THAT(m3.GetRow(2), IsClose(Vector4(18.0f, 20.0f, 22.0f, 24.0f)));
EXPECT_THAT(m3.GetRow(3), IsClose(Vector4(26.0f, 28.0f, 30.0f, 32.0f)));
m3 = m1;
m3 *= 2.0f;
EXPECT_THAT(m3.GetRow(0), IsClose(Vector4(2.0f, 4.0f, 6.0f, 8.0f)));
EXPECT_THAT(m3.GetRow(1), IsClose(Vector4(10.0f, 12.0f, 14.0f, 16.0f)));
EXPECT_THAT(m3.GetRow(2), IsClose(Vector4(18.0f, 20.0f, 22.0f, 24.0f)));
EXPECT_THAT(m3.GetRow(3), IsClose(Vector4(26.0f, 28.0f, 30.0f, 32.0f)));
m3 = 2.0f * m1;
EXPECT_THAT(m3.GetRow(0), IsClose(Vector4(2.0f, 4.0f, 6.0f, 8.0f)));
EXPECT_THAT(m3.GetRow(1), IsClose(Vector4(10.0f, 12.0f, 14.0f, 16.0f)));
EXPECT_THAT(m3.GetRow(2), IsClose(Vector4(18.0f, 20.0f, 22.0f, 24.0f)));
EXPECT_THAT(m3.GetRow(3), IsClose(Vector4(26.0f, 28.0f, 30.0f, 32.0f)));
}
TEST(MATH_Matrix4x4, TestScalarDivision)
{
Matrix4x4 m1;
m1.SetRow(0, 1.0f, 2.0f, 3.0f, 4.0f);
m1.SetRow(1, 5.0f, 6.0f, 7.0f, 8.0f);
m1.SetRow(2, 9.0f, 10.0f, 11.0f, 12.0f);
m1.SetRow(3, 13.0f, 14.0f, 15.0f, 16.0f);
Matrix4x4 m2;
m2.SetRow(0, 7.0f, 8.0f, 9.0f, 10.0f);
m2.SetRow(1, 11.0f, 12.0f, 13.0f, 14.0f);
m2.SetRow(2, 15.0f, 16.0f, 17.0f, 18.0f);
m2.SetRow(3, 19.0f, 20.0f, 21.0f, 22.0f);
Matrix4x4 m3 = m1 / 0.5f;
EXPECT_THAT(m3.GetRow(0), IsClose(Vector4(2.0f, 4.0f, 6.0f, 8.0f)));
EXPECT_THAT(m3.GetRow(1), IsClose(Vector4(10.0f, 12.0f, 14.0f, 16.0f)));
EXPECT_THAT(m3.GetRow(2), IsClose(Vector4(18.0f, 20.0f, 22.0f, 24.0f)));
EXPECT_THAT(m3.GetRow(3), IsClose(Vector4(26.0f, 28.0f, 30.0f, 32.0f)));
m3 = m1;
m3 /= 0.5f;
EXPECT_THAT(m3.GetRow(0), IsClose(Vector4(2.0f, 4.0f, 6.0f, 8.0f)));
EXPECT_THAT(m3.GetRow(1), IsClose(Vector4(10.0f, 12.0f, 14.0f, 16.0f)));
EXPECT_THAT(m3.GetRow(2), IsClose(Vector4(18.0f, 20.0f, 22.0f, 24.0f)));
EXPECT_THAT(m3.GetRow(3), IsClose(Vector4(26.0f, 28.0f, 30.0f, 32.0f)));
}
TEST(MATH_Matrix4x4, TestNegation)
{
Matrix4x4 m1;
m1.SetRow(0, 1.0f, 2.0f, 3.0f, 4.0f);
m1.SetRow(1, 5.0f, 6.0f, 7.0f, 8.0f);
m1.SetRow(2, 9.0f, 10.0f, 11.0f, 12.0f);
m1.SetRow(3, 13.0f, 14.0f, 15.0f, 16.0f);
EXPECT_THAT(-(-m1), IsClose(m1));
EXPECT_THAT(-Matrix4x4::CreateZero(), IsClose(Matrix4x4::CreateZero()));
Matrix4x4 m2 = -m1;
EXPECT_THAT(m2.GetRow(0), IsClose(Vector4(-1.0f, -2.0f, -3.0f, -4.0f)));
EXPECT_THAT(m2.GetRow(1), IsClose(Vector4(-5.0f, -6.0f, -7.0f, -8.0f)));
EXPECT_THAT(m2.GetRow(2), IsClose(Vector4(-9.0f, -10.0f, -11.0f, -12.0f)));
EXPECT_THAT(m2.GetRow(3), IsClose(Vector4(-13.0f, -14.0f, -15.0f, -16.0f)));
Matrix4x4 m3 = m1 + (-m1);
EXPECT_THAT(m3, IsClose(Matrix4x4::CreateZero()));
}
TEST(MATH_Matrix4x4, TestTranspose)
@@ -368,4 +498,36 @@ namespace UnitTest
m1.SetRow(3, 13.0f, 14.0f, 15.0f, 16.0f);
AZ_TEST_ASSERT(m1.GetDiagonal() == Vector4(1.0f, 6.0f, 11.0f, 16.0f));
}
TEST(MATH_Matrix4x4, TestScaleAccess)
{
Matrix4x4 m1 = Matrix4x4::CreateRotationX(DegToRad(40.0f)) * Matrix4x4::CreateScale(Vector3(2.0f, 3.0f, 4.0f));
EXPECT_THAT(m1.RetrieveScale(), IsClose(Vector3(2.0f, 3.0f, 4.0f)));
EXPECT_THAT(m1.ExtractScale(), IsClose(Vector3(2.0f, 3.0f, 4.0f)));
EXPECT_THAT(m1.RetrieveScale(), IsClose(Vector3::CreateOne()));
m1.MultiplyByScale(Vector3(3.0f, 4.0f, 5.0f));
EXPECT_THAT(m1.RetrieveScale(), IsClose(Vector3(3.0f, 4.0f, 5.0f)));
}
TEST(MATH_Matrix4x4, TestScaleSqAccess)
{
Matrix4x4 m1 = Matrix4x4::CreateRotationX(DegToRad(40.0f)) * Matrix4x4::CreateScale(Vector3(2.0f, 3.0f, 4.0f));
EXPECT_THAT(m1.RetrieveScaleSq(), IsClose(Vector3(4.0f, 9.0f, 16.0f)));
m1.ExtractScale();
EXPECT_THAT(m1.RetrieveScaleSq(), IsClose(Vector3::CreateOne()));
m1.MultiplyByScale(Vector3(3.0f, 4.0f, 5.0f));
EXPECT_THAT(m1.RetrieveScaleSq(), IsClose(Vector3(9.0f, 16.0f, 25.0f)));
}
TEST(MATH_Matrix4x4, TestReciprocalScaled)
{
Matrix4x4 orthogonalMatrix = Matrix4x4::CreateRotationX(DegToRad(40.0f));
EXPECT_THAT(orthogonalMatrix.GetReciprocalScaled(), IsClose(orthogonalMatrix));
const AZ::Vector3 scale(2.8f, 0.7f, 1.3f);
AZ::Matrix4x4 scaledMatrix = orthogonalMatrix;
scaledMatrix.MultiplyByScale(scale);
AZ::Matrix4x4 reciprocalScaledMatrix = orthogonalMatrix;
reciprocalScaledMatrix.MultiplyByScale(scale.GetReciprocal());
EXPECT_THAT(scaledMatrix.GetReciprocalScaled(), IsClose(reciprocalScaledMatrix));
}
}
@@ -110,7 +110,7 @@ namespace JsonSerializationTests
EXPECT_EQ(42, value);
}
TEST_F(BaseJsonSerializerTests, ContinueLoading_PointerInstance_ValueLoadedCorrectly)
TEST_F(BaseJsonSerializerTests, ContinueLoading_ToPointerInstance_ValueLoadedCorrectly)
{
using namespace AZ::JsonSerializationResult;
@@ -119,13 +119,62 @@ 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);
EXPECT_EQ(42, value);
}
TEST_F(BaseJsonSerializerTests, ContinueLoading_ToNullPointer_ValueLoadedCorrectly)
{
using namespace AZ::JsonSerializationResult;
rapidjson::Value json;
json.Set(42);
int* ptrValue = nullptr;
ResultCode result =
ContinueLoading(&ptrValue, azrtti_typeid<int>(), json, *m_jsonDeserializationContext, ContinuationFlags::ResolvePointer);
EXPECT_EQ(Processing::Completed, result.GetProcessing());
ASSERT_NE(nullptr, ptrValue);
EXPECT_EQ(42, *ptrValue);
azfree(ptrValue, AZ::SystemAllocator, sizeof(int), alignof(int));
}
TEST_F(BaseJsonSerializerTests, ContinueLoading_DefaultToNullPointer_ValueLoadedCorrectly)
{
using namespace AZ::JsonSerializationResult;
rapidjson::Value json(rapidjson::kObjectType);
int* ptrValue = nullptr;
ResultCode result =
ContinueLoading(&ptrValue, azrtti_typeid<int>(), json, *m_jsonDeserializationContext, ContinuationFlags::ResolvePointer);
EXPECT_EQ(Processing::Completed, result.GetProcessing());
ASSERT_NE(nullptr, ptrValue);
azfree(ptrValue, AZ::SystemAllocator, sizeof(int), alignof(int));
}
TEST_F(BaseJsonSerializerTests, ContinueLoading_NullDeletesObject_ValueLoadedCorrectly)
{
using namespace AZ::JsonSerializationResult;
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, ContinuationFlags::ResolvePointer);
EXPECT_EQ(Processing::Completed, result.GetProcessing());
ASSERT_EQ(nullptr, ptrValue);
}
//
// ContinueStoring
//
@@ -149,21 +198,82 @@ 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");
}
TEST_F(BaseJsonSerializerTests, ContinueStoring_StorePointerToFullDefaultedInstance_ValueStoredCorrectly)
{
using namespace AZ::JsonSerializationResult;
int value = 42;
int* ptrValue = &value;
int value2 = 42;
int* defaultPtrValue = &value2;
ResultCode result = ContinueStoring(
*m_jsonDocument, &ptrValue, &defaultPtrValue, azrtti_typeid<int>(), *m_jsonSerializationContext,
ContinuationFlags::ResolvePointer);
EXPECT_EQ(Processing::Completed, result.GetProcessing());
Expect_DocStrEq("{}");
}
TEST_F(BaseJsonSerializerTests, ContinueStoring_StorePointerToNullptr_ValueStoredCorrectly)
{
using namespace AZ::JsonSerializationResult;
int* ptrValue = nullptr;
ResultCode result = ContinueStoring(
*m_jsonDocument, &ptrValue, nullptr, azrtti_typeid<int>(), *m_jsonSerializationContext, ContinuationFlags::ResolvePointer);
EXPECT_EQ(Processing::Completed, result.GetProcessing());
Expect_DocStrEq("null");
}
TEST_F(BaseJsonSerializerTests, ContinueStoring_StorePointerToNullptrWithValueDefault_ValueStoredCorrectly)
{
using namespace AZ::JsonSerializationResult;
int* ptrValue = nullptr;
int value2 = 42;
int* defaultPtrValue = &value2;
ResultCode result = ContinueStoring(
*m_jsonDocument, &ptrValue, &defaultPtrValue, azrtti_typeid<int>(), *m_jsonSerializationContext,
ContinuationFlags::ResolvePointer);
EXPECT_EQ(Processing::Completed, result.GetProcessing());
Expect_DocStrEq("null");
}
TEST_F(BaseJsonSerializerTests, ContinueStoring_StorePointerToNullptrWithNullPtrDefault_NullPtrIsStored)
{
using namespace AZ::JsonSerializationResult;
int* ptrValue = nullptr;
int* defaultPtrValue = nullptr;
ResultCode result = ContinueStoring(
*m_jsonDocument, &ptrValue, &defaultPtrValue, azrtti_typeid<int>(), *m_jsonSerializationContext,
ContinuationFlags::ResolvePointer);
EXPECT_EQ(Processing::Completed, result.GetProcessing());
Expect_DocStrEq("null");
}
TEST_F(BaseJsonSerializerTests, ContinueStoring_ReplaceDefault_ValueStoredCorrectly)
{
using namespace AZ::JsonSerializationResult;
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");
@@ -177,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");
@@ -190,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,7 +32,7 @@ namespace JsonSerializationTests
return AZStd::make_shared<AZ::JsonSmartPointerSerializer>();
}
AZStd::shared_ptr<SmartPointer> CreateDefaultInstance() override
AZStd::shared_ptr<SmartPointer> CreateDefaultConstructedInstance() override
{
return AZStd::make_shared<SmartPointer>();
}
@@ -51,6 +51,13 @@ namespace JsonSerializationTests
using SmartPointer = T<SimpleClass>;
using Base = SmartPointerBaseTestDescription<SmartPointer>;
AZStd::shared_ptr<SmartPointer> CreateDefaultInstance() override
{
auto result = AZStd::make_shared<SmartPointer>();
*result = SmartPointer(aznew SimpleClass());
return result;
}
AZStd::shared_ptr<SmartPointer> CreateFullySetInstance() override
{
auto result = AZStd::make_shared<SmartPointer>();
@@ -106,21 +113,6 @@ namespace JsonSerializationTests
}
};
template<template<typename...> class T>
class SmartPointerSimpleClassWithInstanceTestDescription :
public SmartPointerSimpleClassTestDescription<T>
{
public:
using SmartPointer = typename SmartPointerSimpleClassTestDescription<T>::SmartPointer;
AZStd::shared_ptr<SmartPointer> CreateDefaultInstance() override
{
auto result = AZStd::make_shared<SmartPointer>();
*result = SmartPointer(aznew SimpleClass());
return result;
}
};
template<template<typename...> class T>
class SmartPointerSimpleDerivedClassTestDescription :
public SmartPointerBaseTestDescription<T<BaseClass>>
@@ -129,6 +121,13 @@ namespace JsonSerializationTests
using SmartPointer = T<BaseClass>;
using Base = SmartPointerBaseTestDescription<SmartPointer>;
AZStd::shared_ptr<SmartPointer> CreateDefaultInstance() override
{
auto result = AZStd::make_shared<SmartPointer>();
*result = SmartPointer(aznew BaseClass());
return result;
}
AZStd::shared_ptr<SmartPointer> CreateFullySetInstance() override
{
auto* instance = aznew SimpleInheritence();
@@ -234,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"(
@@ -272,6 +277,13 @@ namespace JsonSerializationTests
using SmartPointer = T<BaseClass2>;
using Base = SmartPointerBaseTestDescription<SmartPointer>;
AZStd::shared_ptr<SmartPointer> CreateDefaultInstance() override
{
auto result = AZStd::make_shared<SmartPointer>();
*result = SmartPointer(aznew BaseClass2());
return result;
}
AZStd::shared_ptr<SmartPointer> CreateFullySetInstance() override
{
auto* instance = aznew MultipleInheritence();
@@ -385,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"(
@@ -424,9 +442,6 @@ namespace JsonSerializationTests
SmartPointerSimpleClassTestDescription<AZStd::unique_ptr>,
SmartPointerSimpleClassTestDescription<AZStd::shared_ptr>,
SmartPointerSimpleClassTestDescription<AZStd::intrusive_ptr>,
SmartPointerSimpleClassWithInstanceTestDescription<AZStd::unique_ptr>,
SmartPointerSimpleClassWithInstanceTestDescription<AZStd::shared_ptr>,
SmartPointerSimpleClassWithInstanceTestDescription<AZStd::intrusive_ptr>,
// Simple derived class, include single inheritance.
SmartPointerSimpleDerivedClassTestDescription<AZStd::unique_ptr>,
SmartPointerSimpleDerivedClassTestDescription<AZStd::shared_ptr>,
@@ -551,6 +566,37 @@ namespace JsonSerializationTests
EXPECT_EQ(nullptr, *instance);
}
TEST_F(JsonSmartPointerSerializerTests, Load_DefaultInstanceToNullptr_ReturnsSuccess)
{
namespace JSR = AZ::JsonSerializationResult;
SmartPointer instance;
AZStd::shared_ptr<SmartPointer> compare = m_description.CreateDefaultInstance();
m_jsonDocument->SetObject();
JSR::ResultCode result =
m_serializer.Load(&instance, azrtti_typeid<SmartPointer>(), *m_jsonDocument, *m_jsonDeserializationContext);
EXPECT_EQ(JSR::Processing::Completed, result.GetProcessing());
EXPECT_NE(nullptr, instance);
EXPECT_TRUE(m_description.AreEqual(instance, *compare));
}
TEST_F(JsonSmartPointerSerializerTests, Load_DefaultObjectDoesNotUpdateInstance_ReturnsSuccess)
{
namespace JSR = AZ::JsonSerializationResult;
AZStd::shared_ptr<SmartPointer> instance = m_description.CreateFullySetInstance();
AZStd::shared_ptr<SmartPointer> compare = m_description.CreateFullySetInstance();
m_jsonDocument->SetObject();
JSR::ResultCode result =
m_serializer.Load(instance.get(), azrtti_typeid<SmartPointer>(), *m_jsonDocument, *m_jsonDeserializationContext);
EXPECT_EQ(JSR::Processing::Completed, result.GetProcessing());
EXPECT_TRUE(m_description.AreEqual(*instance, *compare));
}
TEST_F(JsonSmartPointerSerializerTests, Load_InstanceBeingReplacedWithDifferentType_ReturnsSuccess)
{
namespace JSR = AZ::JsonSerializationResult;
@@ -720,13 +766,66 @@ namespace JsonSerializationTests
namespace JSR = AZ::JsonSerializationResult;
AZStd::shared_ptr<SmartPointer> instance = m_description.CreateFullySetInstance();
SmartPointer nullPtr;
JSR::ResultCode result = m_serializer.Store(*m_jsonDocument, instance.get(), &nullPtr,
SmartPointer defaultInstance;
JSR::ResultCode result = m_serializer.Store(
*m_jsonDocument, instance.get(), &defaultInstance,
azrtti_typeid<SmartPointer>(), *m_jsonSerializationContext);
EXPECT_EQ(JSR::Outcomes::Success, result.GetOutcome());
}
TEST_F(JsonSmartPointerSerializerTests, Store_ValuePointerIsNullPtr_ReturnsSuccessAndStoresNull)
{
namespace JSR = AZ::JsonSerializationResult;
SmartPointer instance;
AZStd::shared_ptr<SmartPointer> defaultInstance = m_description.CreateFullySetInstance();
JSR::ResultCode result = m_serializer.Store(
*m_jsonDocument, &instance, defaultInstance.get(), azrtti_typeid<SmartPointer>(), *m_jsonSerializationContext);
EXPECT_EQ(JSR::Outcomes::Success, result.GetOutcome());
EXPECT_TRUE(m_jsonDocument->IsNull());
}
TEST_F(JsonSmartPointerSerializerTests, Store_ValueAndDefaultPointersAreNullPtr_ReturnsSuccessAndStoresNull)
{
namespace JSR = AZ::JsonSerializationResult;
SmartPointer instance;
SmartPointer defaultInstance;
JSR::ResultCode result =
m_serializer.Store(*m_jsonDocument, &instance, &defaultInstance, azrtti_typeid<SmartPointer>(), *m_jsonSerializationContext);
EXPECT_EQ(JSR::Outcomes::DefaultsUsed, result.GetOutcome());
EXPECT_TRUE(m_jsonDocument->IsNull());
}
TEST_F(JsonSmartPointerSerializerTests, Store_ValueAndDefaultPointersAreBothDefault_ReturnsSuccess)
{
namespace JSR = AZ::JsonSerializationResult;
AZStd::shared_ptr<SmartPointer> instance = m_description.CreateDefaultInstance();
AZStd::shared_ptr<SmartPointer> defaultInstance = m_description.CreateDefaultInstance();
JSR::ResultCode result =
m_serializer.Store(*m_jsonDocument, instance.get(), defaultInstance.get(), azrtti_typeid<SmartPointer>(), *m_jsonSerializationContext);
EXPECT_EQ(JSR::Outcomes::DefaultsUsed, result.GetOutcome());
Expect_ExplicitDefault(*m_jsonDocument);
}
TEST_F(JsonSmartPointerSerializerTests, Store_ValueHasDefaultValuesAndDefaultHasNullPointer_ReturnsSuccess)
{
namespace JSR = AZ::JsonSerializationResult;
AZStd::shared_ptr<SmartPointer> instance = m_description.CreateDefaultInstance();
SmartPointer defaultInstance;
JSR::ResultCode result = m_serializer.Store(
*m_jsonDocument, instance.get(), &defaultInstance, azrtti_typeid<SmartPointer>(), *m_jsonSerializationContext);
EXPECT_EQ(JSR::Outcomes::DefaultsUsed, result.GetOutcome());
Expect_ExplicitDefault(*m_jsonDocument);
}
TEST_F(JsonSmartPointerSerializerTests, Store_DefaultPointerIsOtherClass_CompletesButDoesNotReturnDefaults)
{
namespace JSR = AZ::JsonSerializationResult;
@@ -749,7 +848,7 @@ namespace JsonSerializationTests
EXPECT_EQ(JSR::Processing::Completed, result.GetProcessing());
}
TEST_F(JsonSmartPointerSerializerTests, Store_SaveAnClassThatIsNotReflected_ReturnsUnknown)
TEST_F(JsonSmartPointerSerializerTests, Store_ClassThatIsNotReflected_ReturnsUnknown)
{
namespace JSR = AZ::JsonSerializationResult;
@@ -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
@@ -1,26 +0,0 @@
/*
* 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/RTTI/RTTI.h>
namespace AzFramework
{
class AtomActiveInterface
{
public:
AZ_RTTI(AtomActiveInterface, "{4BB59C86-0848-485D-AB28-700540470B2B}");
AtomActiveInterface() = default;
virtual ~AtomActiveInterface() = default;
};
} // namespace AzFramework
@@ -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"));
@@ -34,6 +34,8 @@ class ITexture;
namespace AzFramework
{
inline constexpr AZ::s32 g_defaultSceneEntityDebugDisplayId = AZ_CRC_CE("MainViewportEntityDebugDisplayId"); // default id to draw to all viewports in the default scene
/// DebugDisplayRequests provides a debug draw api to be used by components and viewport features.
class DebugDisplayRequests
: public AZ::EBusTraits
@@ -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);
@@ -148,6 +148,12 @@ namespace AzFramework
//! Blocks until all operations made on the provided ticket before the barrier call have completed.
virtual void Barrier(EntitySpawnTicket& ticket, BarrierCallback completionCallback) = 0;
//! Register a handler for OnSpawned events.
virtual void AddOnSpawnedHandler(AZ::Event<AZ::Data::Asset<Spawnable>>::Handler& handler) = 0;
//! Register a handler for OnDespawned events.
virtual void AddOnDespawnedHandler(AZ::Event<AZ::Data::Asset<Spawnable>>::Handler& handler) = 0;
protected:
[[nodiscard]] virtual void* CreateTicket(AZ::Data::Asset<Spawnable>&& spawnable) = 0;
virtual void DestroyTicket(void* ticket) = 0;
@@ -114,6 +114,16 @@ namespace AzFramework
}
}
void SpawnableEntitiesManager::AddOnSpawnedHandler(AZ::Event<AZ::Data::Asset<Spawnable>>::Handler& handler)
{
handler.Connect(m_onSpawnedEvent);
}
void SpawnableEntitiesManager::AddOnDespawnedHandler(AZ::Event<AZ::Data::Asset<Spawnable>>::Handler& handler)
{
handler.Connect(m_onDespawnedEvent);
}
auto SpawnableEntitiesManager::ProcessQueue() -> CommandQueueStatus
{
AZStd::queue<Requests> pendingRequestQueue;
@@ -223,6 +233,8 @@ namespace AzFramework
ticket.m_spawnedEntities.begin() + spawnedEntitiesCount, ticket.m_spawnedEntities.end()));
}
m_onSpawnedEvent.Signal(ticket.m_spawnable);
ticket.m_currentTicketId++;
return true;
}
@@ -257,6 +269,8 @@ namespace AzFramework
ticket.m_spawnedEntities.begin() + spawnedEntitiesCount, ticket.m_spawnedEntities.end()));
}
m_onSpawnedEvent.Signal(ticket.m_spawnable);
ticket.m_currentTicketId++;
return true;
}
@@ -289,6 +303,8 @@ namespace AzFramework
request.m_completionCallback(*request.m_ticket);
}
m_onDespawnedEvent.Signal(ticket.m_spawnable);
ticket.m_currentTicketId++;
return true;
}
@@ -315,6 +331,8 @@ namespace AzFramework
&GameEntityContextRequestBus::Events::DestroyGameEntityAndDescendants, entity->GetId());
}
}
m_onDespawnedEvent.Signal(ticket.m_spawnable);
// Rebuild the list of entities.
ticket.m_spawnedEntities.clear();
@@ -350,6 +368,9 @@ namespace AzFramework
}
ticket.m_currentTicketId++;
m_onSpawnedEvent.Signal(ticket.m_spawnable);
return true;
}
else
@@ -60,6 +60,9 @@ namespace AzFramework
void Barrier(EntitySpawnTicket& spawnInfo, BarrierCallback completionCallback) override;
void AddOnSpawnedHandler(AZ::Event<AZ::Data::Asset<Spawnable>>::Handler& handler) override;
void AddOnDespawnedHandler(AZ::Event<AZ::Data::Asset<Spawnable>>::Handler& handler) override;
//
// The following function is thread safe but intended to be run from the main thread.
//
@@ -156,5 +159,8 @@ namespace AzFramework
AZStd::deque<Requests> m_delayedQueue; //!< Requests that were processed before, but couldn't be completed.
AZStd::queue<Requests> m_pendingRequestQueue;
AZStd::mutex m_pendingRequestQueueMutex;
AZ::Event<AZ::Data::Asset<Spawnable>> m_onSpawnedEvent;
AZ::Event<AZ::Data::Asset<Spawnable>> m_onDespawnedEvent;
};
} // namespace AzFramework
@@ -15,15 +15,146 @@
#include <AzCore/Console/IConsole.h>
#include <AzCore/Math/MathUtils.h>
#include <AzCore/Math/Plane.h>
#include <AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard.h>
#include <AzFramework/Input/Devices/Mouse/InputDeviceMouse.h>
#include <AzFramework/Windowing/WindowBus.h>
AZ_CVAR(
float, ed_newCameraSystemDefaultPlaneHeight, 34.0f, nullptr, AZ::ConsoleFunctorFlags::Null,
"The default height of the ground plane to do intersection tests against when orbiting");
namespace AzFramework
{
AZ_CVAR(
float, ed_cameraSystemDefaultPlaneHeight, 34.0f, nullptr, AZ::ConsoleFunctorFlags::Null,
"The default height of the ground plane to do intersection tests against when orbiting");
AZ_CVAR(float, ed_cameraSystemBoostMultiplier, 3.0f, nullptr, AZ::ConsoleFunctorFlags::Null, "");
AZ_CVAR(float, ed_cameraSystemTranslateSpeed, 10.0f, nullptr, AZ::ConsoleFunctorFlags::Null, "");
AZ_CVAR(float, ed_cameraSystemOrbitDollyScrollSpeed, 0.02f, nullptr, AZ::ConsoleFunctorFlags::Null, "");
AZ_CVAR(float, ed_cameraSystemOrbitDollyCursorSpeed, 0.01f, nullptr, AZ::ConsoleFunctorFlags::Null, "");
AZ_CVAR(float, ed_cameraSystemScrollTranslateSpeed, 0.02f, nullptr, AZ::ConsoleFunctorFlags::Null, "");
AZ_CVAR(float, ed_cameraSystemDefaultOrbitDistance, 60.0f, nullptr, AZ::ConsoleFunctorFlags::Null, "");
AZ_CVAR(float, ed_cameraSystemMaxOrbitDistance, 100.0f, nullptr, AZ::ConsoleFunctorFlags::Null, "");
AZ_CVAR(float, ed_cameraSystemLookSmoothness, 5.0f, nullptr, AZ::ConsoleFunctorFlags::Null, "");
AZ_CVAR(float, ed_cameraSystemTranslateSmoothness, 5.0f, nullptr, AZ::ConsoleFunctorFlags::Null, "");
AZ_CVAR(float, ed_cameraSystemRotateSpeed, 0.005f, nullptr, AZ::ConsoleFunctorFlags::Null, "");
AZ_CVAR(float, ed_cameraSystemPanSpeed, 0.01f, nullptr, AZ::ConsoleFunctorFlags::Null, "");
AZ_CVAR(bool, ed_cameraSystemPanInvertX, true, nullptr, AZ::ConsoleFunctorFlags::Null, "");
AZ_CVAR(bool, ed_cameraSystemPanInvertY, true, nullptr, AZ::ConsoleFunctorFlags::Null, "");
AZ_CVAR(
AZ::CVarFixedString, ed_cameraSystemTranslateForwardKey, "keyboard_key_alphanumeric_W", nullptr, AZ::ConsoleFunctorFlags::Null, "");
AZ_CVAR(
AZ::CVarFixedString, ed_cameraSystemTranslateBackwardKey, "keyboard_key_alphanumeric_S", nullptr, AZ::ConsoleFunctorFlags::Null,
"");
AZ_CVAR(
AZ::CVarFixedString, ed_cameraSystemTranslateLeftKey, "keyboard_key_alphanumeric_A", nullptr, AZ::ConsoleFunctorFlags::Null, "");
AZ_CVAR(
AZ::CVarFixedString, ed_cameraSystemTranslateRightKey, "keyboard_key_alphanumeric_D", nullptr, AZ::ConsoleFunctorFlags::Null, "");
AZ_CVAR(AZ::CVarFixedString, ed_cameraSystemTranslateUpKey, "keyboard_key_alphanumeric_E", nullptr, AZ::ConsoleFunctorFlags::Null, "");
AZ_CVAR(
AZ::CVarFixedString, ed_cameraSystemTranslateDownKey, "keyboard_key_alphanumeric_Q", nullptr, AZ::ConsoleFunctorFlags::Null, "");
AZ_CVAR(
AZ::CVarFixedString, ed_cameraSystemTranslateBoostKey, "keyboard_key_modifier_shift_l", nullptr, AZ::ConsoleFunctorFlags::Null, "");
AZ_CVAR(AZ::CVarFixedString, ed_cameraSystemOrbitKey, "keyboard_key_modifier_alt_l", nullptr, AZ::ConsoleFunctorFlags::Null, "");
AZ_CVAR(AZ::CVarFixedString, ed_cameraSystemFreeLookButton, "mouse_button_right", nullptr, AZ::ConsoleFunctorFlags::Null, "");
AZ_CVAR(AZ::CVarFixedString, ed_cameraSystemFreePanButton, "mouse_button_middle", nullptr, AZ::ConsoleFunctorFlags::Null, "");
AZ_CVAR(AZ::CVarFixedString, ed_cameraSystemOrbitLookButton, "mouse_button_left", nullptr, AZ::ConsoleFunctorFlags::Null, "");
AZ_CVAR(AZ::CVarFixedString, ed_cameraSystemOrbitDollyButton, "mouse_button_right", nullptr, AZ::ConsoleFunctorFlags::Null, "");
AZ_CVAR(AZ::CVarFixedString, ed_cameraSystemOrbitPanButton, "mouse_button_middle", nullptr, AZ::ConsoleFunctorFlags::Null, "");
static InputChannelId CameraTranslateForwardId;
static InputChannelId CameraTranslateBackwardId;
static InputChannelId CameraTranslateLeftId;
static InputChannelId CameraTranslateRightId;
static InputChannelId CameraTranslateDownId;
static InputChannelId CameraTranslateUpId;
static InputChannelId CameraTranslateBoostId;
static InputChannelId CameraOrbitId;
// externed elsewhere
InputChannelId CameraFreeLookButton;
InputChannelId CameraFreePanButton;
InputChannelId CameraOrbitLookButton;
InputChannelId CameraOrbitDollyButton;
InputChannelId CameraOrbitPanButton;
void ReloadCameraKeyBindings()
{
const AZ::CVarFixedString& forward = ed_cameraSystemTranslateForwardKey;
CameraTranslateForwardId = InputChannelId(forward.c_str());
const AZ::CVarFixedString& backward = ed_cameraSystemTranslateBackwardKey;
CameraTranslateBackwardId = InputChannelId(backward.c_str());
const AZ::CVarFixedString& left = ed_cameraSystemTranslateLeftKey;
CameraTranslateLeftId = InputChannelId(left.c_str());
const AZ::CVarFixedString& right = ed_cameraSystemTranslateRightKey;
CameraTranslateRightId = InputChannelId(right.c_str());
const AZ::CVarFixedString& down = ed_cameraSystemTranslateDownKey;
CameraTranslateDownId = InputChannelId(down.c_str());
const AZ::CVarFixedString& up = ed_cameraSystemTranslateUpKey;
CameraTranslateUpId = InputChannelId(up.c_str());
const AZ::CVarFixedString& boost = ed_cameraSystemTranslateBoostKey;
CameraTranslateBoostId = InputChannelId(boost.c_str());
const AZ::CVarFixedString& orbit = ed_cameraSystemOrbitKey;
CameraOrbitId = InputChannelId(orbit.c_str());
const AZ::CVarFixedString& freeLook = ed_cameraSystemFreeLookButton;
CameraFreeLookButton = InputChannelId(freeLook.c_str());
const AZ::CVarFixedString& freePan = ed_cameraSystemFreePanButton;
CameraFreePanButton = InputChannelId(freePan.c_str());
const AZ::CVarFixedString& orbitLook = ed_cameraSystemOrbitLookButton;
CameraOrbitLookButton = InputChannelId(orbitLook.c_str());
const AZ::CVarFixedString& orbitDolly = ed_cameraSystemOrbitDollyButton;
CameraOrbitDollyButton = InputChannelId(orbitDolly.c_str());
const AZ::CVarFixedString& orbitPan = ed_cameraSystemOrbitPanButton;
CameraOrbitPanButton = InputChannelId(orbitPan.c_str());
}
static void ReloadCameraKeyBindingsConsole(const AZ::ConsoleCommandContainer&)
{
ReloadCameraKeyBindings();
}
AZ_CONSOLEFREEFUNC(ReloadCameraKeyBindingsConsole, AZ::ConsoleFunctorFlags::Null, "Reload keybindings for the modern camera system");
// Based on paper by David Eberly - https://www.geometrictools.com/Documentation/EulerAngles.pdf
AZ::Vector3 EulerAngles(const AZ::Matrix3x3& orientation)
{
float x;
float y;
float z;
// 2.4 Factor as RzRyRx
if (orientation.GetElement(2, 0) < 1.0f)
{
if (orientation.GetElement(2, 0) > -1.0f)
{
x = std::atan2(orientation.GetElement(2, 1), orientation.GetElement(2, 2));
y = std::asin(-orientation.GetElement(2, 0));
z = std::atan2(orientation.GetElement(1, 0), orientation.GetElement(0, 0));
}
else
{
x = 0.0f;
y = AZ::Constants::Pi * 0.5f;
z = -std::atan2(-orientation.GetElement(2, 1), orientation.GetElement(1, 1));
}
}
else
{
x = 0.0f;
y = -AZ::Constants::Pi * 0.5f;
z = std::atan2(-orientation.GetElement(1, 2), orientation.GetElement(1, 1));
}
return {x, y, z};
}
void UpdateCameraFromTransform(Camera& camera, const AZ::Transform& transform)
{
const auto eulerAngles = AzFramework::EulerAngles(AZ::Matrix3x3::CreateFromTransform(transform));
camera.m_lookAt = transform.GetTranslation();
camera.m_pitch = eulerAngles.GetX();
camera.m_yaw = eulerAngles.GetZ();
}
bool CameraSystem::HandleEvents(const InputEvent& event)
{
if (const auto& cursor_motion = AZStd::get_if<CursorMotionEvent>(&event))
@@ -144,7 +275,7 @@ namespace AzFramework
{
if (const auto& input = AZStd::get_if<DiscreteInputEvent>(&event))
{
if (input->m_channelId == m_channelId)
if (input->m_channelId == m_rotateChannelId)
{
if (input->m_state == InputChannel::State::Began)
{
@@ -164,8 +295,8 @@ namespace AzFramework
{
Camera nextCamera = targetCamera;
nextCamera.m_pitch -= float(cursorDelta.m_y) * m_props.m_rotateSpeed;
nextCamera.m_yaw -= float(cursorDelta.m_x) * m_props.m_rotateSpeed;
nextCamera.m_pitch -= float(cursorDelta.m_y) * ed_cameraSystemRotateSpeed;
nextCamera.m_yaw -= float(cursorDelta.m_x) * ed_cameraSystemRotateSpeed;
const auto clampRotation = [](const float angle) { return std::fmod(angle + AZ::Constants::TwoPi, AZ::Constants::TwoPi); };
@@ -180,7 +311,7 @@ namespace AzFramework
{
if (const auto& input = AZStd::get_if<DiscreteInputEvent>(&event))
{
if (input->m_channelId == InputDeviceMouse::Button::Middle)
if (input->m_channelId == m_panChannelId)
{
if (input->m_state == InputChannel::State::Began)
{
@@ -202,49 +333,48 @@ namespace AzFramework
const auto panAxes = m_panAxesFn(nextCamera);
const auto deltaPanX = float(cursorDelta.m_x) * panAxes.m_horizontalAxis * m_props.m_panSpeed;
const auto deltaPanY = float(cursorDelta.m_y) * panAxes.m_verticalAxis * m_props.m_panSpeed;
const auto deltaPanX = float(cursorDelta.m_x) * panAxes.m_horizontalAxis * ed_cameraSystemPanSpeed;
const auto deltaPanY = float(cursorDelta.m_y) * panAxes.m_verticalAxis * ed_cameraSystemPanSpeed;
const auto inv = [](const bool invert) {
constexpr float Dir[] = {1.0f, -1.0f};
return Dir[static_cast<int>(invert)];
};
nextCamera.m_lookAt += deltaPanX * inv(m_props.m_panInvertX);
nextCamera.m_lookAt += deltaPanY * -inv(m_props.m_panInvertY);
nextCamera.m_lookAt += deltaPanX * inv(ed_cameraSystemPanInvertX);
nextCamera.m_lookAt += deltaPanY * -inv(ed_cameraSystemPanInvertY);
return nextCamera;
}
TranslateCameraInput::TranslationType TranslateCameraInput::translationFromKey(InputChannelId channelId)
{
// note: remove hard-coded InputDevice keys
if (channelId == InputDeviceKeyboard::Key::AlphanumericW)
if (channelId == CameraTranslateForwardId)
{
return TranslationType::Forward;
}
if (channelId == InputDeviceKeyboard::Key::AlphanumericS)
if (channelId == CameraTranslateBackwardId)
{
return TranslationType::Backward;
}
if (channelId == InputDeviceKeyboard::Key::AlphanumericA)
if (channelId == CameraTranslateLeftId)
{
return TranslationType::Left;
}
if (channelId == InputDeviceKeyboard::Key::AlphanumericD)
if (channelId == CameraTranslateRightId)
{
return TranslationType::Right;
}
if (channelId == InputDeviceKeyboard::Key::AlphanumericQ)
if (channelId == CameraTranslateDownId)
{
return TranslationType::Down;
}
if (channelId == InputDeviceKeyboard::Key::AlphanumericE)
if (channelId == CameraTranslateUpId)
{
return TranslationType::Up;
}
@@ -269,19 +399,19 @@ namespace AzFramework
BeginActivation();
}
if (input->m_channelId == InputDeviceKeyboard::Key::ModifierShiftL)
if (input->m_channelId == CameraTranslateBoostId)
{
m_boost = true;
}
}
else if (input->m_state == InputChannel::State::Ended)
{
m_translation ^= translationFromKey(input->m_channelId);
m_translation &= ~(translationFromKey(input->m_channelId));
if (m_translation == TranslationType::Nil)
{
EndActivation();
}
if (input->m_channelId == InputDeviceKeyboard::Key::ModifierShiftL)
if (input->m_channelId == CameraTranslateBoostId)
{
m_boost = false;
}
@@ -300,8 +430,8 @@ namespace AzFramework
const auto axisY = translationBasis.GetBasisY();
const auto axisZ = translationBasis.GetBasisZ();
const float speed = [boost = m_boost, props = m_props]() {
return props.m_translateSpeed * (boost ? props.m_boostMultiplier : 1.0f);
const float speed = [boost = m_boost]() {
return ed_cameraSystemTranslateSpeed * (boost ? ed_cameraSystemBoostMultiplier : 1.0f);
}();
if ((m_translation & TranslationType::Forward) == TranslationType::Forward)
@@ -352,7 +482,7 @@ namespace AzFramework
{
if (const auto* input = AZStd::get_if<DiscreteInputEvent>(&event))
{
if (input->m_channelId == InputDeviceKeyboard::Key::ModifierAltL)
if (input->m_channelId == CameraOrbitId)
{
if (input->m_state == InputChannel::State::Began)
{
@@ -379,18 +509,17 @@ namespace AzFramework
if (Beginning())
{
float hit_distance = 0.0f;
if (AZ::Plane::CreateFromNormalAndPoint(
AZ::Vector3::CreateAxisZ(), AZ::Vector3::CreateAxisZ(ed_newCameraSystemDefaultPlaneHeight))
if (AZ::Plane::CreateFromNormalAndPoint(AZ::Vector3::CreateAxisZ(), AZ::Vector3::CreateAxisZ(ed_cameraSystemDefaultPlaneHeight))
.CastRay(targetCamera.Translation(), targetCamera.Rotation().GetBasisY(), hit_distance))
{
hit_distance = AZStd::min(hit_distance, m_props.m_maxOrbitDistance);
hit_distance = AZStd::min<float>(hit_distance, ed_cameraSystemMaxOrbitDistance);
nextCamera.m_lookDist = -hit_distance;
nextCamera.m_lookAt = targetCamera.Translation() + targetCamera.Rotation().GetBasisY() * hit_distance;
}
else
{
nextCamera.m_lookDist = -m_props.m_defaultOrbitDistance;
nextCamera.m_lookAt = targetCamera.Translation() + targetCamera.Rotation().GetBasisY() * m_props.m_defaultOrbitDistance;
nextCamera.m_lookDist = -ed_cameraSystemMaxOrbitDistance;
nextCamera.m_lookAt = targetCamera.Translation() + targetCamera.Rotation().GetBasisY() * ed_cameraSystemMaxOrbitDistance;
}
}
@@ -423,7 +552,7 @@ namespace AzFramework
[[maybe_unused]] const float deltaTime)
{
Camera nextCamera = targetCamera;
nextCamera.m_lookDist = AZ::GetMin(nextCamera.m_lookDist + scrollDelta * m_props.m_dollySpeed, 0.0f);
nextCamera.m_lookDist = AZ::GetMin(nextCamera.m_lookDist + scrollDelta * ed_cameraSystemOrbitDollyScrollSpeed, 0.0f);
EndActivation();
return nextCamera;
}
@@ -432,7 +561,7 @@ namespace AzFramework
{
if (const auto& input = AZStd::get_if<DiscreteInputEvent>(&event))
{
if (input->m_channelId == InputDeviceMouse::Button::Right)
if (input->m_channelId == m_dollyChannelId)
{
if (input->m_state == InputChannel::State::Began)
{
@@ -451,7 +580,7 @@ namespace AzFramework
[[maybe_unused]] const float deltaTime)
{
Camera nextCamera = targetCamera;
nextCamera.m_lookDist = AZ::GetMin(nextCamera.m_lookDist + float(cursorDelta.m_y) * m_props.m_dollySpeed, 0.0f);
nextCamera.m_lookDist = AZ::GetMin(nextCamera.m_lookDist + float(cursorDelta.m_y) * ed_cameraSystemOrbitDollyCursorSpeed, 0.0f);
return nextCamera;
}
@@ -472,14 +601,14 @@ namespace AzFramework
const auto translation_basis = LookTranslation(nextCamera);
const auto axisY = translation_basis.GetBasisY();
nextCamera.m_lookAt += axisY * scrollDelta * m_props.m_translateSpeed;
nextCamera.m_lookAt += axisY * scrollDelta * ed_cameraSystemScrollTranslateSpeed;
EndActivation();
return nextCamera;
}
Camera SmoothCamera(const Camera& currentCamera, const Camera& targetCamera, const SmoothProps& props, const float deltaTime)
Camera SmoothCamera(const Camera& currentCamera, const Camera& targetCamera, const float deltaTime)
{
const auto clamp_rotation = [](const float angle) { return std::fmod(angle + AZ::Constants::TwoPi, AZ::Constants::TwoPi); };
@@ -500,11 +629,11 @@ namespace AzFramework
Camera camera;
// note: the math for the lerp smoothing implementation for camera rotation and translation was inspired by this excellent
// article by Scott Lembcke: https://www.gamasutra.com/blogs/ScottLembcke/20180404/316046/Improved_Lerp_Smoothing.php
const float lookRate = std::exp2(props.m_lookSmoothness);
const float lookRate = std::exp2(ed_cameraSystemLookSmoothness);
const float lookT = std::exp2(-lookRate * deltaTime);
camera.m_pitch = AZ::Lerp(targetCamera.m_pitch, currentCamera.m_pitch, lookT);
camera.m_yaw = AZ::Lerp(targetYaw, currentYaw, lookT);
const float moveRate = std::exp2(props.m_moveSmoothness);
const float moveRate = std::exp2(ed_cameraSystemTranslateSmoothness);
const float moveT = std::exp2(-moveRate * deltaTime);
camera.m_lookDist = AZ::Lerp(targetCamera.m_lookDist, currentCamera.m_lookDist, moveT);
camera.m_lookAt = targetCamera.m_lookAt.Lerp(currentCamera.m_lookAt, moveT);
@@ -521,20 +650,19 @@ namespace AzFramework
return button == inputChannelId;
});
if (inputChannelId == InputDeviceMouse::SystemCursorPosition)
if (inputChannelId == InputDeviceMouse::Movement::X || inputChannelId == InputDeviceMouse::Movement::Y)
{
AZ::Vector2 systemCursorPositionNormalized = AZ::Vector2::CreateZero();
InputSystemCursorRequestBus::EventResult(
systemCursorPositionNormalized, inputDeviceId, &InputSystemCursorRequestBus::Events::GetSystemCursorPositionNormalized);
const auto* position = inputChannel.GetCustomData<AzFramework::InputChannel::PositionData2D>();
AZ_Assert(position, "Expected PositionData2D but found nullptr");
return CursorMotionEvent{ScreenPoint(
systemCursorPositionNormalized.GetX() * windowSize.m_width, systemCursorPositionNormalized.GetY() * windowSize.m_height)};
position->m_normalizedPosition.GetX() * windowSize.m_width, position->m_normalizedPosition.GetY() * windowSize.m_height)};
}
else if (inputChannelId == InputDeviceMouse::Movement::Z)
{
return ScrollEvent{inputChannel.GetValue()};
}
else if ((InputDeviceMouse::IsMouseDevice(inputDeviceId) && wasMouseButton) || InputDeviceKeyboard::IsKeyboardDevice(inputDeviceId))
else if (wasMouseButton || InputDeviceKeyboard::IsKeyboardDevice(inputDeviceId))
{
return DiscreteInputEvent{inputChannelId, inputChannel.GetState()};
}
@@ -12,36 +12,21 @@
#pragma once
#include <AzCore/EBus/EBus.h>
#include <AzCore/Math/Matrix3x3.h>
#include <AzCore/Math/Transform.h>
#include <AzCore/std/containers/variant.h>
#include <AzCore/std/optional.h>
#include <AzFramework/Input/Channels/InputChannel.h>
#include <AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard.h>
#include <AzFramework/Input/Devices/Mouse/InputDeviceMouse.h>
#include <AzFramework/Viewport/ScreenGeometry.h>
#include <AzFramework/Viewport/ViewportId.h>
namespace AzFramework
{
struct WindowSize;
//! Update camera key bindings that can be overridden with AZ console vars (invoke from console to update)
void ReloadCameraKeyBindings();
// to be moved
class ModernViewportCameraControllerRequests : public AZ::EBusTraits
{
public:
using BusIdType = AzFramework::ViewportId; ///< ViewportId - used to address requests to this EBus.
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::ById;
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
virtual void SetTargetCameraTransform(const AZ::Transform& transform) = 0;
protected:
~ModernViewportCameraControllerRequests() = default;
};
using ModernViewportCameraControllerRequestBus = AZ::EBus<ModernViewportCameraControllerRequests>;
//! Return Euler angles (pitch, roll, yaw) for the incoming orientation.
AZ::Vector3 EulerAngles(const AZ::Matrix3x3& orientation);
struct Camera
{
@@ -83,6 +68,8 @@ namespace AzFramework
return Transform().GetTranslation();
}
void UpdateCameraFromTransform(Camera& camera, const AZ::Transform& transform);
struct CursorMotionEvent
{
ScreenPoint m_position;
@@ -177,13 +164,7 @@ namespace AzFramework
Activation m_activation = Activation::Idle;
};
struct SmoothProps
{
float m_lookSmoothness = 5.0f;
float m_moveSmoothness = 5.0f;
};
Camera SmoothCamera(const Camera& currentCamera, const Camera& targetCamera, const SmoothProps& props, float deltaTime);
Camera SmoothCamera(const Camera& currentCamera, const Camera& targetCamera, float deltaTime);
class Cameras
{
@@ -215,19 +196,16 @@ namespace AzFramework
class RotateCameraInput : public CameraInput
{
public:
explicit RotateCameraInput(const InputChannelId channelId)
: m_channelId(channelId)
explicit RotateCameraInput(const InputChannelId rotateChannelId)
: m_rotateChannelId(rotateChannelId)
{
}
void HandleEvents(const InputEvent& event) override;
Camera StepCamera(const Camera& targetCamera, const ScreenVector& cursorDelta, float scrollDelta, float deltaTime) override;
InputChannelId m_channelId;
struct Props
{
float m_rotateSpeed = 0.005f;
} m_props;
private:
InputChannelId m_rotateChannelId;
};
struct PanAxes
@@ -260,22 +238,17 @@ namespace AzFramework
class PanCameraInput : public CameraInput
{
public:
explicit PanCameraInput(PanAxesFn panAxesFn)
PanCameraInput(const InputChannelId panChannelId, PanAxesFn panAxesFn)
: m_panAxesFn(AZStd::move(panAxesFn))
, m_panChannelId(panChannelId)
{
}
void HandleEvents(const InputEvent& event) override;
Camera StepCamera(const Camera& targetCamera, const ScreenVector& cursorDelta, float scrollDelta, float deltaTime) override;
struct Props
{
float m_panSpeed = 0.01f;
bool m_panInvertX = true;
bool m_panInvertY = true;
} m_props;
private:
PanAxesFn m_panAxesFn;
InputChannelId m_panChannelId;
};
using TranslationAxesFn = AZStd::function<AZ::Matrix3x3(const Camera& camera)>;
@@ -316,12 +289,6 @@ namespace AzFramework
Camera StepCamera(const Camera& targetCamera, const ScreenVector& cursorDelta, float scrollDelta, float deltaTime) override;
void ResetImpl() override;
struct Props
{
float m_translateSpeed = 10.0f;
float m_boostMultiplier = 3.0f;
} m_props;
private:
enum class TranslationType
{
@@ -372,6 +339,11 @@ namespace AzFramework
return lhs;
}
friend TranslationType operator~(const TranslationType lhs)
{
return static_cast<TranslationType>(~static_cast<std::underlying_type_t<TranslationType>>(lhs));
}
static TranslationType translationFromKey(InputChannelId channelId);
TranslationType m_translation = TranslationType::Nil;
@@ -384,23 +356,19 @@ namespace AzFramework
public:
void HandleEvents(const InputEvent& event) override;
Camera StepCamera(const Camera& targetCamera, const ScreenVector& cursorDelta, float scrollDelta, float deltaTime) override;
struct Props
{
float m_dollySpeed = 0.02f;
} m_props;
};
class OrbitDollyCursorMoveCameraInput : public CameraInput
{
public:
explicit OrbitDollyCursorMoveCameraInput(const InputChannelId dollyChannelId)
: m_dollyChannelId(dollyChannelId) {}
void HandleEvents(const InputEvent& event) override;
Camera StepCamera(const Camera& targetCamera, const ScreenVector& cursorDelta, float scrollDelta, float deltaTime) override;
struct Props
{
float m_dollySpeed = 0.1f;
} m_props;
private:
InputChannelId m_dollyChannelId;
};
class ScrollTranslationCameraInput : public CameraInput
@@ -408,11 +376,6 @@ namespace AzFramework
public:
void HandleEvents(const InputEvent& event) override;
Camera StepCamera(const Camera& targetCamera, const ScreenVector& cursorDelta, float scrollDelta, float deltaTime) override;
struct Props
{
float m_translateSpeed = 0.02f;
} m_props;
};
class OrbitCameraInput : public CameraInput
@@ -426,13 +389,10 @@ namespace AzFramework
}
Cameras m_orbitCameras;
struct Props
{
float m_defaultOrbitDistance = 60.0f;
float m_maxOrbitDistance = 100.0f;
} m_props;
};
struct WindowSize;
//! Map from a generic InputChannel event to a camera specific InputEvent.
InputEvent BuildInputEvent(const InputChannel& inputChannel, const WindowSize& windowSize);
} // namespace AzFramework
@@ -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>
@@ -14,7 +14,6 @@ set(FILES
AzFrameworkModule.h
AzFrameworkModule.cpp
API/ApplicationAPI.h
API/AtomActiveInterface.h
Application/Application.cpp
Application/Application.h
Archive/Archive.cpp
@@ -214,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
@@ -224,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
@@ -266,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
)
@@ -38,8 +38,9 @@ namespace AzManipulatorTestFramework
void SetGridSize(float size) override;
void SetAngularStep(float step) override;
int GetViewportId() const override;
AZStd::optional<AZ::Vector3> ViewportScreenToWorld(const QPoint& screenPosition, float depth) override;
AZStd::optional<AzToolsFramework::ViewportInteraction::ProjectedViewportRay> ViewportScreenToWorldRay(const QPoint& screenPosition) override;
AZStd::optional<AZ::Vector3> ViewportScreenToWorld(const AzFramework::ScreenPoint& screenPosition, float depth) override;
AZStd::optional<AzToolsFramework::ViewportInteraction::ProjectedViewportRay> ViewportScreenToWorldRay(
const AzFramework::ScreenPoint& screenPosition) override;
private:
// ViewportInteractionRequestBus ...
bool GridSnappingEnabled();
@@ -47,7 +48,7 @@ namespace AzManipulatorTestFramework
bool ShowGrid();
bool AngleSnappingEnabled();
float AngleStep();
QPoint ViewportWorldToScreen(const AZ::Vector3& worldPosition);
AzFramework::ScreenPoint ViewportWorldToScreen(const AZ::Vector3& worldPosition);
private:
AZStd::unique_ptr<NullDebugDisplayRequests> m_nullDebugDisplayRequests;
const int m_viewportId = 1234; // Arbitrary viewport id for manipulator tests
@@ -66,10 +66,9 @@ namespace AzManipulatorTestFramework
return m_angularStep;
}
QPoint ViewportInteraction::ViewportWorldToScreen(const AZ::Vector3& worldPosition)
AzFramework::ScreenPoint ViewportInteraction::ViewportWorldToScreen(const AZ::Vector3& worldPosition)
{
auto pos = AzFramework::WorldToScreen(worldPosition, m_cameraState);
return QPoint(pos.m_x, pos.m_y);
return AzFramework::WorldToScreen(worldPosition, m_cameraState);
}
void ViewportInteraction::SetCameraState(const AzFramework::CameraState& cameraState)
@@ -117,12 +116,14 @@ namespace AzManipulatorTestFramework
return m_viewportId;
}
AZStd::optional<AZ::Vector3> ViewportInteraction::ViewportScreenToWorld([[maybe_unused]]const QPoint& screenPosition, [[maybe_unused]]float depth)
AZStd::optional<AZ::Vector3> ViewportInteraction::ViewportScreenToWorld(
[[maybe_unused]] const AzFramework::ScreenPoint& screenPosition, [[maybe_unused]] float depth)
{
return {};
}
AZStd::optional<AzToolsFramework::ViewportInteraction::ProjectedViewportRay> ViewportInteraction::ViewportScreenToWorldRay([[maybe_unused]]const QPoint& screenPosition)
AZStd::optional<AzToolsFramework::ViewportInteraction::ProjectedViewportRay> ViewportInteraction::ViewportScreenToWorldRay(
[[maybe_unused]] const AzFramework::ScreenPoint& screenPosition)
{
return {};
}
@@ -16,7 +16,6 @@
#include <AzCore/std/string/string.h>
#include <AzCore/std/containers/fixed_vector.h>
#include <AzFramework/API/AtomActiveInterface.h>
#include <AzCore/Interface/Interface.h>
namespace AzQtComponents
@@ -13,6 +13,12 @@
#include <AzCore/EBus/EBus.h>
#include <AzCore/Interface/Interface.h>
#include <AzCore/std/string/string.h>
namespace AZ
{
struct BehaviorParameter;
}
namespace AzToolsFramework
{
@@ -40,6 +46,8 @@ namespace AzToolsFramework
};
using GlobalFunctionCollection = AZStd::vector<GlobalFunction>;
virtual void GetGlobalFunctionList(GlobalFunctionCollection& globalFunctionCollection) const = 0;
virtual AZStd::string FetchPythonTypeName(const AZ::BehaviorParameter& param) = 0;
};
//! Interface to signal the phases for the Python virtual machine
@@ -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>;
@@ -117,8 +117,6 @@ namespace AzToolsFramework
{
EditorEntityModel::EditorEntityModel()
{
AzFramework::ApplicationRequests::Bus::BroadcastResult(m_isPrefabEnabled, &AzFramework::ApplicationRequests::IsPrefabSystemEnabled);
EntityCompositionNotificationBus::Handler::BusConnect();
EditorOnlyEntityComponentNotificationBus::Handler::BusConnect();
EditorEntityRuntimeActivationChangeNotificationBus::Handler::BusConnect();
@@ -565,7 +563,7 @@ namespace AzToolsFramework
{
//retrieve or add an entity entry to the table
//the entry must exist, even if not connected, so children and other data can be assigned
[[maybe_unused]] auto [it, inserted] = m_entityInfoTable.try_emplace(entityId, m_isPrefabEnabled);
[[maybe_unused]] auto [it, inserted] = m_entityInfoTable.try_emplace(entityId);
auto& entityInfo = it->second;
//the entity id defaults to invalid and must be set to match the requested id
@@ -882,11 +880,6 @@ namespace AzToolsFramework
}
}
EditorEntityModel::EditorEntityModelEntry::EditorEntityModelEntry(bool isPrefabEnabled)
: m_isPrefabEnabled(isPrefabEnabled)
{
}
EditorEntityModel::EditorEntityModelEntry::~EditorEntityModelEntry()
{
Disconnect();
@@ -1213,29 +1206,15 @@ namespace AzToolsFramework
auto childItr = m_childIndexCache.find(childId);
if (childItr != m_childIndexCache.end())
{
if (m_isPrefabEnabled)
{
// Take the last entry and move it into the removed spot instead of deleting the entry and having to move all
// following entries one step down.
AZ::EntityId backEntity = m_children.back();
m_children[childItr->second] = backEntity;
// Update cached index for the moved id to the new index.
m_childIndexCache[backEntity] = childItr->second;
// Now remove the deleted id from the children and cache.
m_childIndexCache.erase(childId);
m_children.erase(m_children.end() - 1);
}
else
{
m_children.erase(m_children.begin() + childItr->second);
// rebuild index cache for faster lookup
m_childIndexCache.clear();
for (auto childIdToCache : m_children)
{
m_childIndexCache[childIdToCache] = static_cast<AZ::u64>(m_childIndexCache.size());
}
}
// Take the last entry and move it into the removed spot instead of deleting the entry and having to move all
// following entries one step down.
AZ::EntityId backEntity = m_children.back();
m_children[childItr->second] = backEntity;
// Update cached index for the moved id to the new index.
m_childIndexCache[backEntity] = childItr->second;
// Now remove the deleted id from the children and cache.
m_childIndexCache.erase(childId);
m_children.erase(m_children.end() - 1);
}
}
@@ -171,7 +171,6 @@ namespace AzToolsFramework
, public PropertyEditorEntityChangeNotificationBus::Handler
{
public:
explicit EditorEntityModelEntry(bool isPrefabEnabled);
~EditorEntityModelEntry();
// Separately connect to EditorEntityInfoRequestBus and refresh Entity
@@ -336,7 +335,6 @@ namespace AzToolsFramework
bool m_visible = true;
bool m_locked = false;
bool m_connected = false;
bool m_isPrefabEnabled = false;
AZStd::string m_name;
AZStd::string m_sliceAssetName;
AZStd::unordered_map<AZ::EntityId, AZ::u64> m_childIndexCache;
@@ -375,6 +373,5 @@ namespace AzToolsFramework
AZ::EntityId m_postInstantiateBeforeEntity;
AZ::EntityId m_postInstantiateSliceParent;
bool m_gotInstantiateSliceDetails = false;
bool m_isPrefabEnabled = false;
};
}
@@ -127,8 +127,7 @@ namespace AzToolsFramework
ViewportInteraction::MainEditorViewportInteractionRequestBus::EventResult(
worldSurfacePosition, viewportId,
&ViewportInteraction::MainEditorViewportInteractionRequestBus::Events::PickTerrain,
ViewportInteraction::QPointFromScreenPoint(
mouseInteraction.m_mouseInteraction.m_mousePick.m_screenCoordinates));
mouseInteraction.m_mouseInteraction.m_mousePick.m_screenCoordinates);
AZ::Transform worldFromLocal;
AZ::TransformBus::EventResult(worldFromLocal, GetEntityId(), &AZ::TransformBus::Events::GetWorldTM);
@@ -402,10 +401,10 @@ namespace AzToolsFramework
vertexIndex, localVertex);
const AZ::Vector3 worldVertex = worldFromLocal.TransformPoint(AZ::AdaptVertexOut<Vertex>(localVertex));
const QPoint screenPosition = GetScreenPosition(viewportId, worldVertex);
const AzFramework::ScreenPoint screenPosition = GetScreenPosition(viewportId, worldVertex);
// check if a vertex is inside the box select region
if (editorBoxSelect.BoxRegion()->contains(screenPosition))
if (editorBoxSelect.BoxRegion()->contains(ViewportInteraction::QPointFromScreenPoint(screenPosition)))
{
// see if vertexIndex is in active selection
auto vertexIt = AZStd::find(
@@ -103,8 +103,7 @@ namespace AzToolsFramework
ViewportInteraction::MainEditorViewportInteractionRequestBus::EventResult(
worldSurfacePosition, interaction.m_interactionId.m_viewportId,
&ViewportInteraction::MainEditorViewportInteractionRequestBus::Events::PickTerrain,
ViewportInteraction::QPointFromScreenPoint(
interaction.m_mousePick.m_screenCoordinates));
interaction.m_mousePick.m_screenCoordinates);
m_startInternal = CalculateManipulationDataStart(
worldFromLocalUniformScale, worldSurfacePosition, GetLocalPosition(),
@@ -129,8 +128,7 @@ namespace AzToolsFramework
ViewportInteraction::MainEditorViewportInteractionRequestBus::EventResult(
worldSurfacePosition, interaction.m_interactionId.m_viewportId,
&ViewportInteraction::MainEditorViewportInteractionRequestBus::Events::PickTerrain,
ViewportInteraction::QPointFromScreenPoint(
interaction.m_mousePick.m_screenCoordinates));
interaction.m_mousePick.m_screenCoordinates);
const GridSnapParameters gridSnapParams = GridSnapSettings(interaction.m_interactionId.m_viewportId);
@@ -150,8 +148,7 @@ namespace AzToolsFramework
ViewportInteraction::MainEditorViewportInteractionRequestBus::EventResult(
worldSurfacePosition, interaction.m_interactionId.m_viewportId,
&ViewportInteraction::MainEditorViewportInteractionRequestBus::Events::PickTerrain,
ViewportInteraction::QPointFromScreenPoint(
interaction.m_mousePick.m_screenCoordinates));
interaction.m_mousePick.m_screenCoordinates);
const GridSnapParameters gridSnapParams = GridSnapSettings(interaction.m_interactionId.m_viewportId);
@@ -96,9 +96,7 @@ namespace AzToolsFramework
// target templates of the other instances.
for (auto& nestedInstance : instances)
{
PrefabUndoHelpers::RemoveLink(
nestedInstance->GetTemplateId(), commonRootEntityOwningInstance->get().GetTemplateId(),
nestedInstance->GetInstanceAlias(), nestedInstance->GetLinkId(), undoBatch.GetUndoBatch());
RemoveLink(nestedInstance, commonRootEntityOwningInstance->get().GetTemplateId(), undoBatch.GetUndoBatch());
}
PrefabUndoHelpers::UpdatePrefabInstance(
@@ -184,6 +182,22 @@ namespace AzToolsFramework
instanceToParentUnder = prefabEditorEntityOwnershipInterface->GetRootPrefabInstance();
parent = instanceToParentUnder->get().GetContainerEntityId();
}
//Detect whether this instantiation would produce a cyclical dependency
auto relativePath = m_prefabLoaderInterface->GetRelativePathToProject(filePath);
Prefab::TemplateId templateId = m_prefabSystemComponentInterface->GetTemplateIdFromFilePath(relativePath);
// If the template isn't currently loaded, there's no way for it to be in the hierarchy so we just skip the check.
if (templateId != Prefab::InvalidTemplateId && IsPrefabInInstanceAncestorHierarchy(templateId, instanceToParentUnder->get()))
{
return AZ::Failure(
AZStd::string::format(
"Instantiate Prefab operation aborted - Cyclical dependency detected\n(%s depends on %s).",
relativePath.Native().c_str(),
instanceToParentUnder->get().GetTemplateSourcePath().Native().c_str()
)
);
}
{
// Initialize Undo Batch object
@@ -194,7 +208,7 @@ namespace AzToolsFramework
instanceToParentUnderDomBeforeCreate, instanceToParentUnder->get());
// Instantiate the Prefab
auto instanceToCreate = prefabEditorEntityOwnershipInterface->InstantiatePrefab(filePath, instanceToParentUnder);
auto instanceToCreate = prefabEditorEntityOwnershipInterface->InstantiatePrefab(relativePath, instanceToParentUnder);
if (!instanceToCreate)
{
@@ -238,17 +252,29 @@ namespace AzToolsFramework
// Retrieve the owning instance of the common root entity, which will be our new instance's parent instance.
commonRootEntityOwningInstance = GetOwnerInstanceByEntityId(commonRootEntityId);
if (!commonRootEntityOwningInstance)
{
AZ_Assert(
false,
"Failed to create prefab : Couldn't get a valid owning instance for the common root entity of the enities provided");
return AZ::Failure(AZStd::string(
"Failed to create prefab : Couldn't get a valid owning instance for the common root entity of the enities provided"));
}
AZ_Assert(
commonRootEntityOwningInstance.has_value(),
"Failed to create prefab : Couldn't get a valid owning instance for the common root entity of the enities provided");
return AZ::Success();
}
bool PrefabPublicHandler::IsPrefabInInstanceAncestorHierarchy(TemplateId prefabTemplateId, InstanceOptionalConstReference instance)
{
InstanceOptionalConstReference currentInstance = instance;
while (currentInstance.has_value())
{
if (currentInstance->get().GetTemplateId() == prefabTemplateId)
{
return true;
}
currentInstance = currentInstance->get().GetParentInstance();
}
return false;
}
void PrefabPublicHandler::CreateLink(
const EntityList& topLevelEntities, Instance& sourceInstance, TemplateId targetTemplateId,
UndoSystem::URSequencePoint* undoBatch, AZ::EntityId commonRootEntityId)
@@ -287,6 +313,34 @@ namespace AzToolsFramework
m_prefabUndoCache.Store(containerEntityId, AZStd::move(containerEntityDomAfter));
}
void PrefabPublicHandler::RemoveLink(
AZStd::unique_ptr<Instance>& sourceInstance, TemplateId targetTemplateId, UndoSystem::URSequencePoint* undoBatch)
{
LinkReference nestedInstanceLink = m_prefabSystemComponentInterface->FindLink(sourceInstance->GetLinkId());
AZ_Assert(
nestedInstanceLink.has_value(),
"A valid link was not found for one of the instances provided as input for the CreatePrefab operation.");
PrefabDomReference nestedInstanceLinkDom = nestedInstanceLink->get().GetLinkDom();
AZ_Assert(
nestedInstanceLinkDom.has_value(),
"A valid DOM was not found for the link corresponding to one of the instances provided as input for the "
"CreatePrefab operation.");
PrefabDomValueReference nestedInstanceLinkPatches =
PrefabDomUtils::FindPrefabDomValue(nestedInstanceLinkDom->get(), PrefabDomUtils::PatchesName);
AZ_Assert(
nestedInstanceLinkPatches.has_value(),
"A valid DOM for patches was not found for the link corresponding to one of the instances provided as input for the "
"CreatePrefab operation.");
PrefabDom patchesCopyForUndoSupport;
patchesCopyForUndoSupport.CopyFrom(nestedInstanceLinkPatches->get(), patchesCopyForUndoSupport.GetAllocator());
PrefabUndoHelpers::RemoveLink(
sourceInstance->GetTemplateId(), targetTemplateId, sourceInstance->GetInstanceAlias(), sourceInstance->GetLinkId(),
patchesCopyForUndoSupport, undoBatch);
}
PrefabOperationResult PrefabPublicHandler::SavePrefab(AZ::IO::Path filePath)
{
auto templateId = m_prefabSystemComponentInterface->GetTemplateIdFromFilePath(filePath.c_str());
@@ -82,6 +82,16 @@ namespace AzToolsFramework
const EntityList& topLevelEntities, Instance& sourceInstance, TemplateId targetTemplateId,
UndoSystem::URSequencePoint* undoBatch, AZ::EntityId commonRootEntityId);
/**
* Removes the link between template of the sourceInstance and the template corresponding to targetTemplateId.
*
* \param sourceInstance The instance corresponding to the source template of the link to be removed.
* \param targetTemplateId The id of the target template of the link to be removed.
* \param undoBatch The undo batch to set as parent for this remove link action.
*/
void RemoveLink(
AZStd::unique_ptr<Instance>& sourceInstance, TemplateId targetTemplateId, UndoSystem::URSequencePoint* undoBatch);
/**
* Given a list of entityIds, finds the prefab instance that owns the common root entity of the entityIds.
*
@@ -96,6 +106,14 @@ namespace AzToolsFramework
const AZStd::vector<AZ::EntityId>& entityIds, EntityList& inputEntityList, EntityList& topLevelEntities,
AZ::EntityId& commonRootEntityId, InstanceOptionalReference& commonRootEntityOwningInstance);
/* Detects whether an instance of prefabTemplateId is present in the hierarchy of ancestors of instance.
*
* \param prefabTemplateId The template id to test for
* \param instance The instance whose ancestor hierarchy prefabTemplateId will be tested against.
* \return true if an instance of the template of id prefabTemplateId could be found in the ancestor hierarchy of instance, false otherwise.
*/
bool IsPrefabInInstanceAncestorHierarchy(TemplateId prefabTemplateId, InstanceOptionalConstReference instance);
static Instance* GetParentInstance(Instance* instance);
static Instance* GetAncestorOfInstanceThatIsChildOfRoot(const Instance* ancestor, Instance* descendant);
static void GenerateContainerEntityTransform(const EntityList& topLevelEntities, AZ::Vector3& translation, AZ::Quaternion& rotation);
@@ -663,8 +663,9 @@ namespace AzToolsFramework
newLink.SetSourceTemplateId(linkSourceId);
newLink.SetInstanceName(instanceAlias.c_str());
newLink.GetLinkDom().SetObject();
newLink.GetLinkDom().AddMember(rapidjson::StringRef(PrefabDomUtils::SourceName),
rapidjson::StringRef(sourceTemplate.GetFilePath().c_str()), newLink.GetLinkDom().GetAllocator());
newLink.GetLinkDom().AddMember(
rapidjson::StringRef(PrefabDomUtils::SourceName), rapidjson::StringRef(sourceTemplate.GetFilePath().c_str()),
newLink.GetLinkDom().GetAllocator());
if (linkPatch && linkPatch->get().IsArray() && !(linkPatch->get().Empty()))
{
@@ -720,6 +721,8 @@ namespace AzToolsFramework
TemplateId PrefabSystemComponent::GetTemplateIdFromFilePath(AZ::IO::PathView filePath) const
{
AZ_Assert(!filePath.IsAbsolute(), "Prefab - GetTemplateIdFromFilePath was passed an absolute path. Prefabs use paths relative to the project folder.");
auto found = m_templateFilePathToIdMap.find(filePath);
if (found != m_templateFilePathToIdMap.end())
{
@@ -113,7 +113,7 @@ namespace AzToolsFramework
, m_sourceId(InvalidTemplateId)
, m_instanceAlias("")
, m_linkId(InvalidLinkId)
, m_linkDom(PrefabDom())
, m_linkPatches(PrefabDom())
, m_linkStatus(LinkStatus::LINKSTATUS)
{
m_prefabSystemComponentInterface = AZ::Interface<PrefabSystemComponentInterface>::Get();
@@ -124,7 +124,7 @@ namespace AzToolsFramework
const TemplateId& targetId,
const TemplateId& sourceId,
const InstanceAlias& instanceAlias,
PrefabDomReference linkDom,
PrefabDomReference linkPatches,
const LinkId linkId)
{
m_targetId = targetId;
@@ -132,9 +132,9 @@ namespace AzToolsFramework
m_instanceAlias = instanceAlias;
m_linkId = linkId;
if (linkDom.has_value())
if (linkPatches.has_value())
{
m_linkDom = AZStd::move(linkDom->get());
m_linkPatches = AZStd::move(linkPatches->get());
}
//if linkId is invalid, set as ADD
@@ -193,7 +193,7 @@ namespace AzToolsFramework
void PrefabUndoInstanceLink::AddLink()
{
m_linkId = m_prefabSystemComponentInterface->CreateLink(m_targetId, m_sourceId, m_instanceAlias, m_linkDom, m_linkId);
m_linkId = m_prefabSystemComponentInterface->CreateLink(m_targetId, m_sourceId, m_instanceAlias, m_linkPatches, m_linkId);
}
void PrefabUndoInstanceLink::RemoveLink()
@@ -101,7 +101,7 @@ namespace AzToolsFramework
const TemplateId& targetId,
const TemplateId& sourceId,
const InstanceAlias& instanceAlias,
PrefabDomReference linkDom = PrefabDomReference(),
PrefabDomReference linkPatches = PrefabDomReference(),
const LinkId linkId = InvalidLinkId);
void Undo() override;
@@ -120,7 +120,7 @@ namespace AzToolsFramework
InstanceAlias m_instanceAlias;
LinkId m_linkId;
PrefabDom m_linkDom; //data for delete/update
PrefabDom m_linkPatches; //data for delete/update
LinkStatus m_linkStatus;
PrefabSystemComponentInterface* m_prefabSystemComponentInterface = nullptr;
@@ -46,13 +46,11 @@ namespace AzToolsFramework
}
void RemoveLink(
TemplateId sourceTemplateId, TemplateId targetTemplateId, const InstanceAlias& instanceAlias,
LinkId linkId, UndoSystem::URSequencePoint* undoBatch)
TemplateId sourceTemplateId, TemplateId targetTemplateId, const InstanceAlias& instanceAlias, LinkId linkId,
PrefabDomReference linkPatches, UndoSystem::URSequencePoint* undoBatch)
{
auto linkRemoveUndo = aznew PrefabUndoInstanceLink("Remove Link");
PrefabDom emptyLinkDom;
linkRemoveUndo->Capture(
targetTemplateId, sourceTemplateId, instanceAlias, emptyLinkDom, linkId);
linkRemoveUndo->Capture(targetTemplateId, sourceTemplateId, instanceAlias, linkPatches, linkId);
linkRemoveUndo->SetParent(undoBatch);
linkRemoveUndo->Redo();
}
@@ -25,8 +25,8 @@ namespace AzToolsFramework
TemplateId sourceTemplateId, TemplateId targetTemplateId, PrefabDomReference patch,
const InstanceAlias& instanceAlias, UndoSystem::URSequencePoint* undoBatch);
void RemoveLink(
TemplateId sourceTemplateId, TemplateId targetTemplateId, const InstanceAlias& instanceAlias,
LinkId linkId, UndoSystem::URSequencePoint* undoBatch);
TemplateId sourceTemplateId, TemplateId targetTemplateId, const InstanceAlias& instanceAlias, LinkId linkId,
PrefabDomReference linkPatches, UndoSystem::URSequencePoint* undoBatch);
}
} // namespace Prefab
} // namespace AzToolsFramework
@@ -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();
@@ -21,8 +21,6 @@
#include <AzToolsFramework/Entity/EditorEntityContextBus.h>
#include <AzToolsFramework/Viewport/ViewportTypes.h>
class QPoint; // LYN-2315 in-progress, remove this
namespace AzFramework
{
struct ScreenPoint;
@@ -167,19 +165,37 @@ namespace AzToolsFramework
/// Return the angle snapping/step size.
virtual float AngleStep() = 0;
/// Transform a point in world space to screen space coordinates.
virtual QPoint ViewportWorldToScreen(const AZ::Vector3& worldPosition) = 0;
virtual AzFramework::ScreenPoint ViewportWorldToScreen(const AZ::Vector3& worldPosition) = 0;
/// Transform a point in screen space coordinates to a vector in world space based on clip space depth.
/// Depth specifies a relative camera depth to project in the range of [0.f, 1.f].
/// Returns the world space position if successful.
virtual AZStd::optional<AZ::Vector3> ViewportScreenToWorld(const QPoint& screenPosition, float depth) = 0;
virtual AZStd::optional<AZ::Vector3> ViewportScreenToWorld(const AzFramework::ScreenPoint& screenPosition, float depth) = 0;
/// Casts a point in screen space to a ray in world space originating from the viewport camera frustum's near plane.
/// Returns a ray containing the ray's origin and a direction normal, if successful.
virtual AZStd::optional<ProjectedViewportRay> ViewportScreenToWorldRay(const QPoint& screenPosition) = 0;
virtual AZStd::optional<ProjectedViewportRay> ViewportScreenToWorldRay(const AzFramework::ScreenPoint& screenPosition) = 0;
protected:
~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>;
@@ -207,9 +223,9 @@ namespace AzToolsFramework
public:
/// Given a point in screen space, return the picked entity (if any).
/// Picked EntityId will be returned, InvalidEntityId will be returned on failure.
virtual AZ::EntityId PickEntity(const QPoint& point) = 0;
virtual AZ::EntityId PickEntity(const AzFramework::ScreenPoint& point) = 0;
/// Given a point in screen space, return the terrain position in world space.
virtual AZ::Vector3 PickTerrain(const QPoint& point) = 0;
virtual AZ::Vector3 PickTerrain(const AzFramework::ScreenPoint& point) = 0;
/// Return the terrain height given a world position in 2d (xy plane).
virtual float TerrainHeight(const AZ::Vector2& position) = 0;
/// Given the current view frustum (viewport) return all visible entities.
@@ -246,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;
@@ -19,8 +19,6 @@ namespace AzToolsFramework
{
namespace ViewportInteraction
{
const AZ::s32 g_mainViewportEntityDebugDisplayId = AZ_CRC("MainViewportEntityDebugDisplayId", 0x58ae7fe8);
void ViewportInteractionReflect(AZ::ReflectContext* context)
{
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
@@ -256,9 +256,5 @@ namespace AzToolsFramework
/// Reflect all viewport related types.
void ViewportInteractionReflect(AZ::ReflectContext* context);
/// The Id the main DebugDisplayRequestBus will be connected on.
extern const AZ::s32 g_mainViewportEntityDebugDisplayId;
} // namespace ViewportInteraction
} // namespace AzToolsFramework
@@ -141,16 +141,16 @@ namespace AzToolsFramework
const AZ::Vector3& entityPosition = m_entityDataCache->GetVisibleEntityPosition(entityCacheIndex);
// selecting based on 2d icon - should only do it when visible and not selected
const QPoint screenPosition = GetScreenPosition(viewportId, entityPosition);
const AzFramework::ScreenPoint screenPosition = GetScreenPosition(viewportId, entityPosition);
const float distSqFromCamera = cameraState.m_position.GetDistanceSq(entityPosition);
const auto iconRange = static_cast<float>(GetIconScale(distSqFromCamera) * s_iconSize * 0.5f);
const auto screenCoords = mouseInteraction.m_mouseInteraction.m_mousePick.m_screenCoordinates;
if ( screenCoords.m_x >= screenPosition.x() - iconRange
&& screenCoords.m_x <= screenPosition.x() + iconRange
&& screenCoords.m_y >= screenPosition.y() - iconRange
&& screenCoords.m_y <= screenPosition.y() + iconRange)
if ( screenCoords.m_x >= screenPosition.m_x - iconRange
&& screenCoords.m_x <= screenPosition.m_x + iconRange
&& screenCoords.m_y >= screenPosition.m_y - iconRange
&& screenCoords.m_y <= screenPosition.m_y + iconRange)
{
entityIdUnderCursor = entityId;
break;
@@ -53,11 +53,11 @@ namespace AzToolsFramework
return AZ::GetMax(projectedCameraDistance, cameraState.m_nearClip) / apparentDistance;
}
QPoint GetScreenPosition(const int viewportId, const AZ::Vector3& worldTranslation)
AzFramework::ScreenPoint GetScreenPosition(const int viewportId, const AZ::Vector3& worldTranslation)
{
AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzToolsFramework);
QPoint screenPosition = QPoint();
auto screenPosition = AzFramework::ScreenPoint(0, 0);
ViewportInteraction::ViewportInteractionRequestBus::EventResult(
screenPosition, viewportId,
&ViewportInteraction::ViewportInteractionRequestBus::Events::ViewportWorldToScreen,
@@ -45,7 +45,7 @@ namespace AzToolsFramework
const AZ::Vector3& worldPosition, const AzFramework::CameraState& cameraState);
/// Map from world space to screen space.
QPoint GetScreenPosition(int viewportId, const AZ::Vector3& worldTranslation);
AzFramework::ScreenPoint GetScreenPosition(int viewportId, const AZ::Vector3& worldTranslation);
/// Given a mouse interaction, determine if the pick ray from its position
/// in screen space intersected an aabb in world space.
@@ -316,14 +316,14 @@ namespace AzToolsFramework
template<typename EntitySelectFuncType, typename EntityIdContainer, typename Compare>
static void BoxSelectAddRemoveToEntitySelection(
const AZStd::optional<QRect>& boxSelect, const QPoint& screenPosition, const AZ::EntityId visibleEntityId,
const AZStd::optional<QRect>& boxSelect, const AzFramework::ScreenPoint& screenPosition, const AZ::EntityId visibleEntityId,
const EntityIdContainer& incomingEntityIds, EntityIdContainer& outgoingEntityIds,
EditorTransformComponentSelection& entityTransformComponentSelection,
EntitySelectFuncType selectFunc1, EntitySelectFuncType selectFunc2, Compare outgoingCheck)
{
AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzToolsFramework);
if (boxSelect->contains(screenPosition))
if (boxSelect->contains(ViewportInteraction::QPointFromScreenPoint(screenPosition)))
{
const auto entityIt = incomingEntityIds.find(visibleEntityId);
@@ -389,7 +389,7 @@ namespace AzToolsFramework
const AZ::EntityId entityId = entityDataCache.GetVisibleEntityId(entityCacheIndex);
const AZ::Vector3& entityPosition = entityDataCache.GetVisibleEntityPosition(entityCacheIndex);
const QPoint screenPosition = GetScreenPosition(viewportId, entityPosition);
const AzFramework::ScreenPoint screenPosition = GetScreenPosition(viewportId, entityPosition);
if (currentKeyboardModifiers.Ctrl())
{
@@ -927,7 +927,7 @@ namespace AzToolsFramework
ViewportInteraction::MainEditorViewportInteractionRequestBus::EventResult(
worldSurfacePosition, viewportId,
&ViewportInteraction::MainEditorViewportInteractionRequestBus::Events::PickTerrain,
ViewportInteraction::QPointFromScreenPoint(mouseInteraction.m_mousePick.m_screenCoordinates));
mouseInteraction.m_mousePick.m_screenCoordinates);
// convert to local space - snap if enabled
const GridSnapParameters gridSnapParams = GridSnapSettings(viewportId);
@@ -78,7 +78,7 @@ namespace Benchmark
}
BENCHMARK_REGISTER_F(BM_PrefabUpdateInstances, UpdateInstances_SingeEntityInstances)
->RangeMultiplier(10)
->Range(100, 1000)
->Range(100, 10000)
->Unit(benchmark::kMillisecond)
->Complexity();
@@ -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.