Initial commit

This commit is contained in:
alexpete
2021-03-05 11:26:34 -08:00
commit a10351f38d
27091 changed files with 5521199 additions and 0 deletions
@@ -0,0 +1,388 @@
/*
* 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/Aabb.h>
#include <AzCore/Math/Obb.h>
#include <AzCore/Math/Vector3.h>
#include <AzCore/Math/Transform.h>
#include <AzCore/UnitTest/TestTypes.h>
using namespace AZ;
namespace UnitTest
{
TEST(MATH_Aabb, TestCreateNull)
{
Aabb aabb = Aabb::CreateNull();
EXPECT_FALSE(aabb.IsValid());
EXPECT_TRUE(aabb.GetMin().IsGreaterThan(aabb.GetMax()));
}
TEST(MATH_Aabb, TestCreateFromPoint)
{
Aabb aabb = Aabb::CreateFromPoint(Vector3(0.0f));
EXPECT_TRUE(aabb.IsValid());
EXPECT_TRUE(aabb.IsFinite());
EXPECT_TRUE(aabb.GetMin().IsClose(aabb.GetMax()));
}
TEST(MATH_Aabb, TestCreateFromMinMax)
{
Aabb aabb = Aabb::CreateFromMinMax(Vector3(0.0f), Vector3(1.0f));
EXPECT_TRUE(aabb.IsValid());
EXPECT_TRUE(aabb.GetMin().IsClose(Vector3(0.0f)));
EXPECT_TRUE(aabb.GetMax().IsClose(Vector3(1.0f)));
}
TEST(MATH_Aabb, TestCreateCenterHalfExtents)
{
Aabb aabb = Aabb::CreateCenterHalfExtents(Vector3(1.0f), Vector3(1.0f));
EXPECT_TRUE(aabb.IsValid());
EXPECT_TRUE(aabb.GetMin().IsClose(Vector3(0.0f)));
EXPECT_TRUE(aabb.GetMax().IsClose(Vector3(2.0f)));
EXPECT_TRUE(aabb.GetCenter().IsClose(Vector3(1.0f)));
}
TEST(MATH_Aabb, TestCreateCenterRadius)
{
Aabb aabb = Aabb::CreateCenterRadius(Vector3(4.0f), 2.0f);
EXPECT_TRUE(aabb.GetMin().IsClose(Vector3(2.0f)));
EXPECT_TRUE(aabb.GetMax().IsClose(Vector3(6.0f)));
EXPECT_TRUE(aabb.GetExtents().IsClose(Vector3(4.0f)));
}
TEST(MATH_Aabb, TestCreatePoints)
{
const int numPoints = 3;
Vector3 points[numPoints] =
{
Vector3(10.0f, 0.0f, -10.0f),
Vector3(-10.0f, 10.0f, 0.0f),
Vector3(0.0f, -10.0f, 10.0f)
};
Aabb aabb = Aabb::CreatePoints(points, numPoints);
EXPECT_TRUE(aabb.GetMin().IsClose(Vector3(-10.0f)));
EXPECT_TRUE(aabb.GetMax().IsClose(Vector3(10.0f)));
}
TEST(MATH_Aabb, TestCreateFromObb)
{
const Vector3 position(1.0f, 1.0f, 1.0f);
const Quaternion rotation = Quaternion::CreateRotationZ(Constants::QuarterPi);
const Vector3 halfLengths = Vector3::CreateOne();
const Obb obb = Obb::CreateFromPositionRotationAndHalfLengths(position, rotation, halfLengths);
Aabb aabb = Aabb::CreateFromObb(obb);
EXPECT_TRUE(aabb.GetMin().IsClose(Vector3(-0.414f, -0.414f, 0.0f)));
EXPECT_TRUE(aabb.GetMax().IsClose(Vector3(2.414f, 2.414f, 2.0f)));
EXPECT_TRUE(aabb.GetCenter().IsClose(Vector3(1.0f, 1.0f, 1.0f)));
}
TEST(MATH_Aabb, TestSetters)
{
Aabb aabb = Aabb::CreateFromMinMax(Vector3(1.0f), Vector3(4.0f));
EXPECT_TRUE(aabb.GetMin().IsClose(Vector3(1.0f)));
EXPECT_TRUE(aabb.GetMax().IsClose(Vector3(4.0f)));
aabb.SetMin(Vector3(0.0f));
EXPECT_TRUE(aabb.GetMin().IsClose(Vector3(0.0f)));
EXPECT_TRUE(aabb.GetCenter().IsClose(Vector3(2.0f)));
aabb.SetMax(Vector3(6.0f));
EXPECT_TRUE(aabb.GetMax().IsClose(Vector3(6.0f)));
EXPECT_TRUE(aabb.GetCenter().IsClose(Vector3(3.0f)));
}
TEST(MATH_Aabb, TestGetExtents)
{
Aabb aabb = Aabb::CreateFromMinMax(Vector3(0.0f), Vector3(1.0f, 2.0f, 3.0f));
EXPECT_TRUE(AZ::IsClose(aabb.GetXExtent(), 1.0f));
EXPECT_TRUE(AZ::IsClose(aabb.GetYExtent(), 2.0f));
EXPECT_TRUE(AZ::IsClose(aabb.GetZExtent(), 3.0f));
}
TEST(MATH_Aabb, TestSupport)
{
Aabb aabb = Aabb::CreateFromMinMax(Vector3(0.0f), Vector3(1.0f));
EXPECT_TRUE(aabb.GetSupport(Vector3( 0.5774, 0.5774, 0.5774)).IsClose(Vector3(0.0f, 0.0f, 0.0f)));
EXPECT_TRUE(aabb.GetSupport(Vector3( 0.5774, 0.5774, -0.5774)).IsClose(Vector3(0.0f, 0.0f, 1.0f)));
EXPECT_TRUE(aabb.GetSupport(Vector3( 0.5774, -0.5774, 0.5774)).IsClose(Vector3(0.0f, 1.0f, 0.0f)));
EXPECT_TRUE(aabb.GetSupport(Vector3( 0.5774, -0.5774, -0.5774)).IsClose(Vector3(0.0f, 1.0f, 1.0f)));
EXPECT_TRUE(aabb.GetSupport(Vector3(-0.5774, 0.5774, 0.5774)).IsClose(Vector3(1.0f, 0.0f, 0.0f)));
EXPECT_TRUE(aabb.GetSupport(Vector3(-0.5774, 0.5774, -0.5774)).IsClose(Vector3(1.0f, 0.0f, 1.0f)));
EXPECT_TRUE(aabb.GetSupport(Vector3(-0.5774, -0.5774, 0.5774)).IsClose(Vector3(1.0f, 1.0f, 0.0f)));
EXPECT_TRUE(aabb.GetSupport(Vector3(-0.5774, -0.5774, -0.5774)).IsClose(Vector3(1.0f, 1.0f, 1.0f)));
}
TEST(MATH_Aabb, TestGetAsSphere)
{
Aabb aabb = Aabb::CreateFromMinMax(Vector3(-2.0f, 0.0f, 0.0f), Vector3(2.0f, 0.0f, 0.0f));
Vector3 center;
float radius;
aabb.GetAsSphere(center, radius);
EXPECT_TRUE(center.IsClose(Vector3(0.0f)));
EXPECT_TRUE(AZ::IsClose(radius, 2.0f));
}
TEST(MATH_Aabb, TestContains)
{
Aabb aabb = Aabb::CreateFromMinMax(Vector3(-2.0f), Vector3(2.0f));
Aabb aabb2 = Aabb::CreateFromMinMax(Vector3(1.0f), Vector3(2.0f));
//Contains(Vector3)
EXPECT_TRUE(aabb.Contains(Vector3(1.0f)));
//Contains(Aabb)
EXPECT_TRUE(aabb.Contains(aabb2));
EXPECT_FALSE(aabb2.Contains(aabb));
}
TEST(MATH_Aabb, TestOverlaps)
{
Aabb aabb = Aabb::CreateFromMinMax(Vector3(0.0f), Vector3(2.0f));
Aabb aabb2 = Aabb::CreateFromMinMax(Vector3(1.0f), Vector3(3.0f));
EXPECT_TRUE(aabb.Overlaps(aabb2));
aabb2.Set(Vector3(5.0f), Vector3(6.0f));
EXPECT_FALSE(aabb.Overlaps(aabb2));
}
TEST(MATH_Aabb, TestDisjoint)
{
Aabb aabb = Aabb::CreateFromMinMax(Vector3(-2.0f), Vector3(2.0f));
Aabb aabb2 = Aabb::CreateFromMinMax(Vector3(1.0f), Vector3(2.0f));
Aabb aabb3 = Aabb::CreateFromMinMax(Vector3(3.0f), Vector3(4.0f));
//Disjoint(Aabb)
EXPECT_FALSE(aabb.Disjoint(aabb2));
EXPECT_FALSE(aabb2.Disjoint(aabb));
EXPECT_TRUE(aabb3.Disjoint(aabb));
EXPECT_TRUE(aabb3.Disjoint(aabb2));
EXPECT_TRUE(aabb.Disjoint(aabb3));
EXPECT_TRUE(aabb2.Disjoint(aabb3));
}
TEST(MATH_Aabb, TestExpand)
{
Aabb aabb = Aabb::CreateFromMinMax(Vector3(0.0f), Vector3(2.0f));
aabb.Expand(Vector3(1.0f));
EXPECT_TRUE(aabb.GetCenter().IsClose(Vector3(1.0f)));
EXPECT_TRUE(aabb.GetMin().IsClose(Vector3(-1.0f)));
EXPECT_TRUE(aabb.GetMax().IsClose(Vector3(3.0f)));
}
TEST(MATH_Aabb, TestGetExpanded)
{
Aabb aabb = Aabb::CreateFromMinMax(Vector3(-1.0f), Vector3(1.0f));
aabb = aabb.GetExpanded(Vector3(9.0f));
EXPECT_TRUE(aabb.GetMin().IsClose(Vector3(-10.0f)));
EXPECT_TRUE(aabb.GetMax().IsClose(Vector3(10.0f)));
}
TEST(MATH_Aabb, TestAddPoint)
{
Aabb aabb = Aabb::CreateFromMinMax(Vector3(-1.0f), Vector3(1.0f));
aabb.AddPoint(Vector3(1.0f));
EXPECT_TRUE(aabb.GetMin().IsClose(Vector3(-1.0f)));
EXPECT_TRUE(aabb.GetMax().IsClose(Vector3(1.0f)));
aabb.AddPoint(Vector3(2.0f));
EXPECT_TRUE(aabb.GetMin().IsClose(Vector3(-1.0f)));
EXPECT_TRUE(aabb.GetMax().IsClose(Vector3(2.0f)));
}
TEST(MATH_Aabb, TestAddAabb)
{
Aabb aabb = Aabb::CreateFromMinMax(Vector3(0.0f), Vector3(2.0f));
Aabb aabb2 = Aabb::CreateFromMinMax(Vector3(-2.0f), Vector3(0.0f));
aabb.AddAabb(aabb2);
EXPECT_TRUE(aabb.GetMin().IsClose(Vector3(-2.0f)));
EXPECT_TRUE(aabb.GetMax().IsClose(Vector3(2.0f)));
}
TEST(MATH_Aabb, TestGetDistance)
{
Aabb aabb = Aabb::CreateFromMinMax(Vector3(-1.0f), Vector3(1.0f));
EXPECT_TRUE(AZ::IsClose(aabb.GetDistance(Vector3(2.0f, 0.0f, 0.0f)), 1.0f));
// make sure a point inside the box returns zero, even if that point isn't the center.
EXPECT_TRUE(AZ::IsClose(aabb.GetDistance(Vector3(0.5f, 0.0f, 0.0f)), 0.0f));
}
TEST(MATH_Aabb, TestGetDistanceSq)
{
Aabb aabb = Aabb::CreateFromMinMax(Vector3(-1.0f), Vector3(1.0f));
EXPECT_TRUE(AZ::IsClose(aabb.GetDistanceSq(Vector3(0.0f, 3.0f, 0.0f)), 4.0f));
// make sure a point inside the box returns zero, even if that point isn't the center.
EXPECT_TRUE(AZ::IsClose(aabb.GetDistanceSq(Vector3(0.0f, 0.5f, 0.0f)), 0.0f));
}
TEST(MATH_Aabb, TestGetMaxDistance)
{
Aabb aabb = Aabb::CreateFromMinMax(Vector3(-1.0f), Vector3(1.0f));
// The max distance for all of the following should be the square root of (4^2 + 3^2 + 2^2)
EXPECT_TRUE(AZ::IsClose(aabb.GetMaxDistance(Vector3(3.0f, 2.0f, 1.0f)), sqrtf(16.0f + 9.0f + 4.0f)));
EXPECT_TRUE(AZ::IsClose(aabb.GetMaxDistance(Vector3(3.0f, 2.0f, -1.0f)), sqrtf(16.0f + 9.0f + 4.0f)));
EXPECT_TRUE(AZ::IsClose(aabb.GetMaxDistance(Vector3(3.0f, -2.0f, 1.0f)), sqrtf(16.0f + 9.0f + 4.0f)));
EXPECT_TRUE(AZ::IsClose(aabb.GetMaxDistance(Vector3(3.0f, -2.0f, -1.0f)), sqrtf(16.0f + 9.0f + 4.0f)));
EXPECT_TRUE(AZ::IsClose(aabb.GetMaxDistance(Vector3(-3.0f, 2.0f, 1.0f)), sqrtf(16.0f + 9.0f + 4.0f)));
EXPECT_TRUE(AZ::IsClose(aabb.GetMaxDistance(Vector3(-3.0f, 2.0f, -1.0f)), sqrtf(16.0f + 9.0f + 4.0f)));
EXPECT_TRUE(AZ::IsClose(aabb.GetMaxDistance(Vector3(-3.0f, -2.0f, 1.0f)), sqrtf(16.0f + 9.0f + 4.0f)));
EXPECT_TRUE(AZ::IsClose(aabb.GetMaxDistance(Vector3(-3.0f, -2.0f, -1.0f)), sqrtf(16.0f + 9.0f + 4.0f)));
// make sure points inside the box return a correct max distance as well - sqrt of (1.5^2 + 1.5^2 + 1.5^2)
EXPECT_TRUE(AZ::IsClose(aabb.GetMaxDistance(Vector3(0.5f, 0.5f, 0.5f)), sqrtf(2.25f + 2.25f + 2.25f)));
EXPECT_TRUE(AZ::IsClose(aabb.GetMaxDistance(Vector3(0.5f, 0.5f, -0.5f)), sqrtf(2.25f + 2.25f + 2.25f)));
EXPECT_TRUE(AZ::IsClose(aabb.GetMaxDistance(Vector3(0.5f, -0.5f, 0.5f)), sqrtf(2.25f + 2.25f + 2.25f)));
EXPECT_TRUE(AZ::IsClose(aabb.GetMaxDistance(Vector3(0.5f, -0.5f, -0.5f)), sqrtf(2.25f + 2.25f + 2.25f)));
EXPECT_TRUE(AZ::IsClose(aabb.GetMaxDistance(Vector3(-0.5f, 0.5f, 0.5f)), sqrtf(2.25f + 2.25f + 2.25f)));
EXPECT_TRUE(AZ::IsClose(aabb.GetMaxDistance(Vector3(-0.5f, 0.5f, -0.5f)), sqrtf(2.25f + 2.25f + 2.25f)));
EXPECT_TRUE(AZ::IsClose(aabb.GetMaxDistance(Vector3(-0.5f, -0.5f, 0.5f)), sqrtf(2.25f + 2.25f + 2.25f)));
EXPECT_TRUE(AZ::IsClose(aabb.GetMaxDistance(Vector3(-0.5f, -0.5f, -0.5f)), sqrtf(2.25f + 2.25f + 2.25f)));
// make sure the center returns our minimal max distance (1^2 + 1^2 + 1^2)
EXPECT_TRUE(AZ::IsClose(aabb.GetMaxDistance(Vector3(0.0f, 0.0f, 0.0f)), sqrtf(1.0f + 1.0f + 1.0f)));
}
TEST(MATH_Aabb, TestGetMaxDistanceSq)
{
Aabb aabb = Aabb::CreateFromMinMax(Vector3(-1.0f), Vector3(1.0f));
// The max distance for all of the following should be (4^2 + 3^2 + 2^2)
EXPECT_TRUE(AZ::IsClose(aabb.GetMaxDistanceSq(Vector3(3.0f, 2.0f, 1.0f)), 16.0f + 9.0f + 4.0f));
EXPECT_TRUE(AZ::IsClose(aabb.GetMaxDistanceSq(Vector3(3.0f, 2.0f, -1.0f)), 16.0f + 9.0f + 4.0f));
EXPECT_TRUE(AZ::IsClose(aabb.GetMaxDistanceSq(Vector3(3.0f, -2.0f, 1.0f)), 16.0f + 9.0f + 4.0f));
EXPECT_TRUE(AZ::IsClose(aabb.GetMaxDistanceSq(Vector3(3.0f, -2.0f, -1.0f)), 16.0f + 9.0f + 4.0f));
EXPECT_TRUE(AZ::IsClose(aabb.GetMaxDistanceSq(Vector3(-3.0f, 2.0f, 1.0f)), 16.0f + 9.0f + 4.0f));
EXPECT_TRUE(AZ::IsClose(aabb.GetMaxDistanceSq(Vector3(-3.0f, 2.0f, -1.0f)), 16.0f + 9.0f + 4.0f));
EXPECT_TRUE(AZ::IsClose(aabb.GetMaxDistanceSq(Vector3(-3.0f, -2.0f, 1.0f)), 16.0f + 9.0f + 4.0f));
EXPECT_TRUE(AZ::IsClose(aabb.GetMaxDistanceSq(Vector3(-3.0f, -2.0f, -1.0f)), 16.0f + 9.0f + 4.0f));
// make sure points inside the box return a correct max distance as well: (1.5^2 + 1.5^2 + 1.5^2)
EXPECT_TRUE(AZ::IsClose(aabb.GetMaxDistanceSq(Vector3(0.5f, 0.5f, 0.5f)), 2.25f + 2.25f + 2.25f));
EXPECT_TRUE(AZ::IsClose(aabb.GetMaxDistanceSq(Vector3(0.5f, 0.5f, -0.5f)), 2.25f + 2.25f + 2.25f));
EXPECT_TRUE(AZ::IsClose(aabb.GetMaxDistanceSq(Vector3(0.5f, -0.5f, 0.5f)), 2.25f + 2.25f + 2.25f));
EXPECT_TRUE(AZ::IsClose(aabb.GetMaxDistanceSq(Vector3(0.5f, -0.5f, -0.5f)), 2.25f + 2.25f + 2.25f));
EXPECT_TRUE(AZ::IsClose(aabb.GetMaxDistanceSq(Vector3(-0.5f, 0.5f, 0.5f)), 2.25f + 2.25f + 2.25f));
EXPECT_TRUE(AZ::IsClose(aabb.GetMaxDistanceSq(Vector3(-0.5f, 0.5f, -0.5f)), 2.25f + 2.25f + 2.25f));
EXPECT_TRUE(AZ::IsClose(aabb.GetMaxDistanceSq(Vector3(-0.5f, -0.5f, 0.5f)), 2.25f + 2.25f + 2.25f));
EXPECT_TRUE(AZ::IsClose(aabb.GetMaxDistanceSq(Vector3(-0.5f, -0.5f, -0.5f)), 2.25f + 2.25f + 2.25f));
// make sure the center returns our minimal max distance (1^2 + 1^2 + 1^2)
EXPECT_TRUE(AZ::IsClose(aabb.GetMaxDistanceSq(Vector3(0.0f, 0.0f, 0.0f)), 1.0f + 1.0f + 1.0f));
}
TEST(MATH_Aabb, TestGetClamped)
{
Aabb aabb = Aabb::CreateFromMinMax(Vector3(0.0f), Vector3(2.0f));
Aabb aabb2 = Aabb::CreateFromMinMax(Vector3(1.0f), Vector3(4.0f));
aabb = aabb.GetClamped(aabb2);
EXPECT_TRUE(aabb.GetMin().IsClose(Vector3(1.0f)));
EXPECT_TRUE(aabb.GetMax().IsClose(Vector3(2.0f)));
}
TEST(MATH_Aabb, TestClamp)
{
Aabb aabb = Aabb::CreateFromMinMax(Vector3(0.0f), Vector3(2.0f));
Aabb aabb2 = Aabb::CreateFromMinMax(Vector3(-2.0f), Vector3(1.0f));
aabb.Clamp(aabb2);
EXPECT_TRUE(aabb.GetMin().IsClose(Vector3(0.0f)));
EXPECT_TRUE(aabb.GetMax().IsClose(Vector3(1.0f)));
}
TEST(MATH_Aabb, TestSetNull)
{
Aabb aabb = Aabb::CreateFromMinMax(Vector3(0.0f), Vector3(1.0f));
aabb.SetNull();
EXPECT_FALSE(aabb.IsValid());
}
TEST(MATH_Aabb, TestTranslate)
{
Aabb aabb = Aabb::CreateFromMinMax(Vector3(-1.0f), Vector3(1.0f));
aabb.Translate(Vector3(2.0f));
EXPECT_TRUE(aabb.GetMin().IsClose(Vector3(1.0f)));
EXPECT_TRUE(aabb.GetMax().IsClose(Vector3(3.0f)));
}
TEST(MATH_Aabb, TestGetTranslated)
{
Aabb aabb = Aabb::CreateFromMinMax(Vector3(1.0f), Vector3(3.0f));
aabb = aabb.GetTranslated(Vector3(-2.0f));
EXPECT_TRUE(aabb.GetMin().IsClose(Vector3(-1.0f)));
EXPECT_TRUE(aabb.GetMax().IsClose(Vector3(1.0f)));
}
TEST(MATH_Aabb, TestGetSurfaceArea)
{
Aabb aabb = Aabb::CreateFromMinMax(Vector3(0.0f), Vector3(1.0f));
EXPECT_NEAR(aabb.GetSurfaceArea(), 6.0f, 0.002f);
}
TEST(MATH_Aabb, TestIsClose)
{
const Aabb aabb1 = Aabb::CreateFromMinMax(Vector3(0.0f), Vector3(1.0f));
const Aabb aabb2 = Aabb::CreateFromMinMax(Vector3(0.1f), Vector3(0.9f));
const Aabb aabb3 = Aabb::CreateFromMinMax(Vector3(0.3f), Vector3(0.7f));
EXPECT_TRUE(aabb1.IsClose(aabb2, 0.2f));
EXPECT_FALSE(aabb1.IsClose(aabb3, 0.2f));
EXPECT_TRUE(aabb2.IsClose(aabb3, 0.3f));
}
TEST(MATH_Aabb, TestIsFinite)
{
Aabb aabb = Aabb::CreateNull();
const float infinity = std::numeric_limits<float>::infinity();
const Vector3 infiniteV3 = Vector3(infinity);
aabb.Set(Vector3(0), infiniteV3);
// A bounding box is only invalid if the min is greater than the max.
// A bounding box with an infinite min or max is valid as long as this is true.
EXPECT_TRUE(aabb.IsValid());
EXPECT_FALSE(aabb.IsFinite());
}
// Check if both aabb transform functions give the same result
TEST(MATH_AabbTransform, CompareTest)
{
// create aabb
Vector3 min(-100.0f, 50.0f, 0.0f);
Vector3 max(120.0f, 300.0f, 50.0f);
Aabb aabb = Aabb::CreateFromMinMax(min, max);
// make the transformation matrix
Transform tm = Transform::CreateRotationX(1.0f);
tm.SetTranslation(100.0f, 0.0f, -50.0f);
// transform
Obb obb = aabb.GetTransformedObb(tm);
Aabb transAabb = aabb.GetTransformedAabb(tm);
Aabb transAabb2 = Aabb::CreateFromObb(obb);
aabb.ApplyTransform(tm);
// compare the transformations
EXPECT_TRUE(transAabb.GetMin().IsClose(transAabb2.GetMin()));
EXPECT_TRUE(transAabb.GetMax().IsClose(transAabb2.GetMax()));
EXPECT_TRUE(aabb.GetMin().IsClose(transAabb.GetMin()));
EXPECT_TRUE(aabb.GetMax().IsClose(transAabb.GetMax()));
}
}
@@ -0,0 +1,536 @@
/*
* 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/Color.h>
#include <AzCore/Math/Vector3.h>
#include <AzCore/Math/Vector4.h>
#include <AzCore/UnitTest/TestTypes.h>
using namespace AZ;
namespace UnitTest
{
TEST(MATH_Color, Construction)
{
// Default constructor
Color colorDefault;
colorDefault.SetR(1.0f);
EXPECT_NEAR(colorDefault.GetR(), 1.0f, Constants::Tolerance);
// Vector4 constructor
const Vector4 vectorColor(0.5f, 0.6f, 0.7f, 1.0f);
const Color colorFromVector(vectorColor);
EXPECT_TRUE(colorFromVector.GetAsVector4().IsClose(vectorColor));
// Single float constructor
const Color colorFromFloat(1.0f);
EXPECT_TRUE(colorFromFloat.GetAsVector4().IsClose(Vector4(1.0f, 1.0f, 1.0f, 1.0f)));
// Four individual floats constructor
const Color colorFromFloats(0.5f, 0.6f, 0.7f, 1.0f);
EXPECT_TRUE(colorFromFloats.GetAsVector4().IsClose(vectorColor));
// Four individual uint8s constructor
const Color colorFronUints((u8)0x7F, (u8)0x9F, (u8)0xBF, (u8)0xFF);
EXPECT_EQ(colorFronUints.ToU32(), 0xFFBF9F7F);
}
TEST(MATH_Color, StaticConstruction)
{
// Zero color
const Color colorZero = Color::CreateZero();
EXPECT_TRUE(colorZero.GetAsVector4().IsClose(Vector4(0.0f, 0.0f, 0.0f, 0.0f)));
// OneColor
const Color colorOne = Color::CreateOne();
EXPECT_TRUE(colorOne.GetAsVector4().IsClose(Vector4(1.0f, 1.0f, 1.0f, 1.0f)));
// From Float4
const float float4Color[4] = { 0.3f, 0.4f, 0.5f, 0.8f };
const Color colorFrom4Floats = Color::CreateFromFloat4(float4Color);
EXPECT_TRUE(colorFrom4Floats.GetAsVector4().IsClose(Vector4(0.3f, 0.4f, 0.5f, 0.8f)));
// From Vector3
const Vector3 vector3Color(0.6f, 0.7f, 0.8f);
const Color colorFromVector3 = Color::CreateFromVector3(vector3Color);
EXPECT_TRUE(colorFromVector3.GetAsVector4().IsClose(Vector4(0.6f, 0.7f, 0.8f, 1.0f)));
// From Vector 3 and float
const Color colorFromVector3AndFloat = Color::CreateFromVector3AndFloat(vector3Color, 0.5f);
EXPECT_TRUE(colorFromVector3AndFloat.GetAsVector4().IsClose(Vector4(0.6f, 0.7f, 0.8f, 0.5f)));
}
TEST(MATH_Color, Store)
{
// Store color values to float array.
float dest[4];
const Color colorOne = Color::CreateOne();
colorOne.StoreToFloat4(dest);
EXPECT_NEAR(dest[0], 1.0f, Constants::Tolerance);
EXPECT_NEAR(dest[1], 1.0f, Constants::Tolerance);
EXPECT_NEAR(dest[2], 1.0f, Constants::Tolerance);
EXPECT_NEAR(dest[3], 1.0f, Constants::Tolerance);
}
TEST(MATH_Color, ComponentAccess)
{
Color testColor;
// Float Get / Set
testColor.SetR(0.4f);
EXPECT_NEAR(testColor.GetR(), 0.4f, Constants::Tolerance);
testColor.SetG(0.3f);
EXPECT_NEAR(testColor.GetG(), 0.3f, Constants::Tolerance);
testColor.SetB(0.2f);
EXPECT_NEAR(testColor.GetB(), 0.2f, Constants::Tolerance);
testColor.SetA(0.1f);
EXPECT_NEAR(testColor.GetA(), 0.1f, Constants::Tolerance);
// u8 Get / Set
testColor.SetR8((u8)0x12);
EXPECT_EQ(testColor.GetR8(), 0x12);
testColor.SetG8((u8)0x34);
EXPECT_EQ(testColor.GetG8(), 0x34);
testColor.SetB8((u8)0x56);
EXPECT_EQ(testColor.GetB8(), 0x56);
testColor.SetA8((u8)0x78);
EXPECT_EQ(testColor.GetA8(), 0x78);
// Index-based element access
testColor = Color(0.3f, 0.4f, 0.5f, 0.7f);
EXPECT_NEAR(testColor.GetElement(0), 0.3f, Constants::Tolerance);
EXPECT_NEAR(testColor.GetElement(1), 0.4f, Constants::Tolerance);
EXPECT_NEAR(testColor.GetElement(2), 0.5f, Constants::Tolerance);
EXPECT_NEAR(testColor.GetElement(3), 0.7f, Constants::Tolerance);
testColor.SetElement(0, 0.7f);
EXPECT_NEAR(testColor.GetR(), 0.7f, Constants::Tolerance);
testColor.SetElement(1, 0.5f);
EXPECT_NEAR(testColor.GetG(), 0.5f, Constants::Tolerance);
testColor.SetElement(2, 0.4f);
EXPECT_NEAR(testColor.GetB(), 0.4f, Constants::Tolerance);
testColor.SetElement(3, 0.3f);
EXPECT_NEAR(testColor.GetA(), 0.3f, Constants::Tolerance);
}
TEST(MATH_Color, SettersGetters)
{
// Vector3 getter
const Color vectorColor = Color(0.3f, 0.4f, 0.5f, 0.7f);
EXPECT_TRUE(vectorColor.GetAsVector3().IsClose(Vector3(0.3f, 0.4f, 0.5f)));
// Vector 4 getter
EXPECT_TRUE(vectorColor.GetAsVector4().IsClose(Vector4(0.3f, 0.4f, 0.5f, 0.7f)));
// Set from single float
Color singleValue;
singleValue.Set(0.75f);
EXPECT_TRUE(singleValue.GetAsVector4().IsClose(Vector4(0.75f, 0.75f, 0.75f, 0.75f)));
// Set from 4 floats
Color separateValues;
separateValues.Set(0.23f, 0.45f, 0.67f, 0.89f);
EXPECT_TRUE(separateValues.GetAsVector4().IsClose(Vector4(0.23f, 0.45f, 0.67f, 0.89f)));
// Set from a float[4]
float floatArray[4] = { 0.87f, 0.65f, 0.43f, 0.21f };
Color floatArrayColor;
floatArrayColor.Set(floatArray);
EXPECT_TRUE(floatArrayColor.GetAsVector4(). IsClose(Vector4(0.87f, 0.65f, 0.43f, 0.21f)));
// Set from Vector3, Alpha should be set to 1.0f
Color vector3Color;
vector3Color.Set(Vector3(0.2f, 0.4f, 0.6f));
EXPECT_TRUE(vector3Color.GetAsVector4().IsClose(Vector4(0.2f, 0.4f, 0.6f, 1.0f)));
// Set from Vector3 +_ alpha
Color vector4Color;
vector4Color.Set(Vector3(0.1f, 0.3f, 0.5f), 0.7f);
EXPECT_TRUE(vector4Color.GetAsVector4().IsClose(Vector4(0.1f, 0.3f, 0.5f, 0.7f)));
// Oddly lacking a Set() from Vector4...
}
TEST(MATH_Color, HueSaturationValue)
{
Color fromHSV(0.0f, 0.0f, 0.0f, 1.0f);
// Check first sexant (0-60 degrees) with 0 hue, full saturation and value = red.
fromHSV.SetFromHSVRadians(0.0f, 1.0f, 1.0f);
EXPECT_TRUE(fromHSV.IsClose(Color(1.0f, 0.0f, 0.0f, 1.0f)));
// Check the second sexant (60-120 degrees)
fromHSV.SetFromHSVRadians(AZ::DegToRad(72.0f), 1.0f, 1.0f);
EXPECT_TRUE(fromHSV.IsClose(Color(0.8f, 1.0f, 0.0f, 1.0f)));
// Check the third sexant (120-180 degrees)
fromHSV.SetFromHSVRadians(AZ::DegToRad(144.0f), 1.0f, 1.0f);
EXPECT_TRUE(fromHSV.IsClose(Color(0.0f, 1.0f, 0.4f, 1.0f)));
// Check the fourth sexant (180-240 degrees)
fromHSV.SetFromHSVRadians(AZ::DegToRad(216.0f), 1.0f, 1.0f);
EXPECT_TRUE(fromHSV.IsClose(Color(0.0f, 0.4f, 1.0f, 1.0f)));
// Check the fifth sexant (240-300 degrees)
fromHSV.SetFromHSVRadians(AZ::DegToRad(252.0f), 1.0f, 1.0f);
EXPECT_TRUE(fromHSV.IsClose(Color(0.2f, 0.0f, 1.0f, 1.0f)));
// Check the sixth sexant (300-360 degrees)
fromHSV.SetFromHSVRadians(AZ::DegToRad(324.0f), 1.0f, 1.0f);
EXPECT_TRUE(fromHSV.IsClose(Color(1.0f, 0.0f, 0.6f, 1.0f)));
// Check the upper limit of the hue
fromHSV.SetFromHSVRadians(AZ::Constants::TwoPi, 1.0f, 1.0f);
EXPECT_TRUE(fromHSV.IsClose(Color(1.0f, 0.0f, 0.0f, 1.0f)));
// Check that zero saturation causes RGB to all be value.
fromHSV.SetFromHSVRadians(AZ::DegToRad(90.0f), 0.0f, 0.75f);
EXPECT_TRUE(fromHSV.IsClose(Color(0.75f, 0.75f, 0.75f, 1.0f)));
// Check that zero value causes the color to be black.
fromHSV.SetFromHSVRadians(AZ::DegToRad(180.0f), 1.0f, 0.0f);
EXPECT_TRUE(fromHSV.IsClose(Color(0.0f, 0.0f, 0.0f, 1.0f)));
// Check a non-zero, non-one saturation
fromHSV.SetFromHSVRadians(AZ::DegToRad(252.0f), 0.5f, 1.0f);
EXPECT_TRUE(fromHSV.IsClose(Color(0.6f, 0.5f, 1.0f, 1.0f)));
// Check a non-zero, non-one value
fromHSV.SetFromHSVRadians(AZ::DegToRad(216.0f), 1.0f, 0.5f);
EXPECT_TRUE(fromHSV.IsClose(Color(0.0f, 0.2f, 0.5f, 1.0f)));
// Check a non-zero, non-one value and saturation
fromHSV.SetFromHSVRadians(AZ::DegToRad(144.0f), 0.25f, 0.75f);
EXPECT_TRUE(fromHSV.IsClose(Color(143.44f / 255.0f, 191.25f / 255.0f, 162.56f / 255.0f, 1.0f)));
// Check that negative hue is handled correctly (only fractional value, +1 to be positive)
fromHSV.SetFromHSVRadians(AZ::DegToRad(-396.0f), 1.0f, 1.0f);
EXPECT_TRUE(fromHSV.IsClose(Color(1.0f, 0.0f, 0.6f, 1.0f)));
// Check that negative saturation is clamped to 0
fromHSV.SetFromHSVRadians(AZ::DegToRad(324.0f), -1.0f, 1.0f);
EXPECT_TRUE(fromHSV.IsClose(Color(1.0f, 1.0f, 1.0f, 1.0f)));
// Check that negative value is clamped to 0
fromHSV.SetFromHSVRadians(AZ::Constants::Pi, 1.0f, -1.0f);
EXPECT_TRUE(fromHSV.IsClose(Color(0.0f, 0.0f, 0.0f, 1.0f)));
// Check that > 1 saturation is clamped to 1
fromHSV.SetFromHSVRadians(AZ::DegToRad(324.0f), 2.0f, 1.0f);
EXPECT_TRUE(fromHSV.IsClose(Color(1.0f, 0.0f, 0.6f, 1.0f)));
// Check that > 1 value is clamped to 1
fromHSV.SetFromHSVRadians(AZ::DegToRad(324.0f), 1.0f, 2.0f);
EXPECT_TRUE(fromHSV.IsClose(Color(1.0f, 0.0f, 0.6f, 1.0f)));
// Check a large hue.
fromHSV.SetFromHSVRadians(AZ::DegToRad(3744.0f), 1.0f, 1.0f);
EXPECT_TRUE(fromHSV.IsClose(Color(0.0f, 1.0f, 0.4f, 1.0f)));
}
TEST(MATH_Color, EqualityComparisons)
{
// Equality within tolerance
Color color1(0.1f, 0.2f, 0.3f, 0.4f);
Color color2(0.1f, 0.2f, 0.3f, 0.4f);
Color color3(0.12f, 0.22f, 0.32f, 0.42f);
EXPECT_TRUE(color1.IsClose(color2));
EXPECT_FALSE(color1.IsClose(color3));
EXPECT_TRUE(color1.IsClose(color3, 0.03f));
EXPECT_FALSE(color1.IsClose(color3, 0.01f));
// Zero check within tolerance
Color zeroColor = Color::CreateZero();
EXPECT_TRUE(zeroColor.IsZero());
Color almostZeroColor(0.001f, 0.001f, 0.001f, 0.001f);
EXPECT_FALSE(almostZeroColor.IsZero());
EXPECT_TRUE(almostZeroColor.IsZero(0.01f));
// Strict equality
EXPECT_TRUE(color1 == color2);
EXPECT_FALSE(color1 == color3);
// Strict inequality
EXPECT_FALSE(color1 != color2);
EXPECT_TRUE(color1 != color3);
}
TEST(MATH_Color, LessThanComparisons)
{
Color color1(0.3f, 0.6f, 0.8f, 1.0f);
Color color2(0.2f, 0.5f, 0.7f, 1.0f);
Color color3(0.2f, 0.5f, 0.7f, 0.9f);
Color color4(0.8f, 0.4f, 0.3f, 0.8f);
// color 1 and two have an equal component so should not be strictly less than each other
EXPECT_FALSE(color1.IsLessThan(color2));
EXPECT_FALSE(color2.IsLessThan(color1));
// color 3 should be strictly less than color 1, but not color 2
EXPECT_TRUE(color3.IsLessThan(color1));
EXPECT_FALSE(color3.IsLessThan(color2));
// color 4 has values higher and lower than other colors so it should always fail
EXPECT_FALSE(color4.IsLessThan(color1));
EXPECT_FALSE(color4.IsLessThan(color2));
EXPECT_FALSE(color4.IsLessThan(color3));
EXPECT_FALSE(color1.IsLessThan(color4));
EXPECT_FALSE(color2.IsLessThan(color4));
EXPECT_FALSE(color3.IsLessThan(color4));
// color 1 and two have an equal component but otherwise color 2 is less than color 1
EXPECT_FALSE(color1.IsLessEqualThan(color2));
EXPECT_TRUE(color2.IsLessEqualThan(color1));
// color 3 should be less than or equal to both color 1 and color 2
EXPECT_TRUE(color3.IsLessEqualThan(color1));
EXPECT_TRUE(color3.IsLessEqualThan(color2));
// color 4 has values higher and lower than other colors so it should always fail
EXPECT_FALSE(color4.IsLessEqualThan(color1));
EXPECT_FALSE(color4.IsLessEqualThan(color2));
EXPECT_FALSE(color4.IsLessEqualThan(color3));
EXPECT_FALSE(color1.IsLessEqualThan(color4));
EXPECT_FALSE(color2.IsLessEqualThan(color4));
EXPECT_FALSE(color3.IsLessEqualThan(color4));
}
TEST(MATH_Color, GreaterThanComparisons)
{
Color color1(0.3f, 0.6f, 0.8f, 1.0f);
Color color2(0.2f, 0.5f, 0.7f, 1.0f);
Color color3(0.2f, 0.5f, 0.7f, 0.9f);
Color color4(0.8f, 0.4f, 0.3f, 0.8f);
// color 1 and two have an equal component so should not be strictly greater than each other
EXPECT_FALSE(color1.IsGreaterThan(color2));
EXPECT_FALSE(color2.IsGreaterThan(color1));
// color 1 should be strictly greater than color 3, but color 2 shouldn't
EXPECT_TRUE(color1.IsGreaterThan(color3));
EXPECT_FALSE(color2.IsGreaterThan(color3));
// color 4 has values higher and lower than other colors so it should always fail
EXPECT_FALSE(color4.IsGreaterThan(color1));
EXPECT_FALSE(color4.IsGreaterThan(color2));
EXPECT_FALSE(color4.IsGreaterThan(color3));
EXPECT_FALSE(color1.IsGreaterThan(color4));
EXPECT_FALSE(color2.IsGreaterThan(color4));
EXPECT_FALSE(color3.IsGreaterThan(color4));
// color 1 and two have an equal component but otherwise color 2 is less than color 1
EXPECT_TRUE(color1.IsGreaterEqualThan(color2));
EXPECT_FALSE(color2.IsGreaterEqualThan(color1));
// color 1 and 2 should both be greater than or equal to color 3
EXPECT_TRUE(color1.IsGreaterEqualThan(color3));
EXPECT_TRUE(color2.IsGreaterEqualThan(color3));
// color 4 has values higher and lower than other colors so it should always fail
EXPECT_FALSE(color4.IsGreaterEqualThan(color1));
EXPECT_FALSE(color4.IsGreaterEqualThan(color2));
EXPECT_FALSE(color4.IsGreaterEqualThan(color3));
EXPECT_FALSE(color1.IsGreaterEqualThan(color4));
EXPECT_FALSE(color2.IsGreaterEqualThan(color4));
EXPECT_FALSE(color3.IsGreaterEqualThan(color4));
}
TEST(MATH_Color, VectorConversions)
{
Vector3 vec3FromColor(Color(0.4f, 0.6f, 0.8f, 1.0f));
EXPECT_TRUE(vec3FromColor.IsClose(Vector3(0.4f, 0.6f, 0.8f)));
Vector4 vec4FromColor(Color(0.4f, 0.6f, 0.8f, 1.0f));
EXPECT_TRUE(vec4FromColor.IsClose(Vector4(0.4f, 0.6f, 0.8f, 1.0f)));
Color ColorfromVec3;
ColorfromVec3 = Vector3(0.3f, 0.4f, 0.5f);
EXPECT_TRUE(ColorfromVec3.GetAsVector4().IsClose(Vector4(0.3f, 0.4f, 0.5f, 1.0f)));
}
TEST(MATH_Color, LinearToGamma)
{
// Very dark values (these are converted differently)
Color reallyDarkLinear(0.001234f, 0.000123f, 0.001010f, 0.5f);
Color reallyDarkGamma = reallyDarkLinear.LinearToGamma();
Color reallyDarkGammaCheck(0.01594328f, 0.00158916f, 0.0130492f, 0.5f);
EXPECT_TRUE(reallyDarkGamma.IsClose(reallyDarkGammaCheck, 0.00001f));
// Normal values
Color normalLinear(0.123456f, 0.345678f, 0.567890f, 0.8f);
Color normalGamma = normalLinear.LinearToGamma();
Color normalGammaCheck(0.386281658744f, 0.622691988496f, 0.77841737783f, 0.8f);
EXPECT_TRUE(normalGamma.IsClose(normalGammaCheck, 0.00001f));
// Bright values
Color brightLinear(1.234567f, 3.456789f, 5.678901f, 1.0f);
Color brightGamma = brightLinear.LinearToGamma();
Color brightGammaCheck(1.09681722689f, 1.71388455271f, 2.12035054203f, 1.0f);
EXPECT_TRUE(brightGamma.IsClose(brightGammaCheck, 0.00001f));
// Zero should stay the same
Color zeroColor = Color::CreateZero();
Color zeroColorGamma = zeroColor.LinearToGamma();
EXPECT_TRUE(zeroColorGamma.IsClose(zeroColor, 0.00001f));
// One should stay the same
Color oneColor = Color::CreateOne();
Color oneColorGamma = oneColor.LinearToGamma();
EXPECT_TRUE(oneColorGamma.IsClose(oneColor, 0.00001f));
}
TEST(MATH_Color, GammaToLinear)
{
// Very dark values (these are converted differently)
Color reallyDarkGamma(0.001234f, 0.000123f, 0.001010f, 0.5f);
Color reallyDarkLinear = reallyDarkGamma.GammaToLinear();
Color reallyDarkLinearCheck(0.0000955108359133f, 0.00000952012383901f, 0.000078173374613f, 0.5f);
EXPECT_TRUE(reallyDarkLinear.IsClose(reallyDarkLinearCheck, 0.00001f));
// Normal values
Color normalGamma(0.123456f, 0.345678f, 0.567890f, 0.8f);
Color normalLinear = normalGamma.GammaToLinear();
Color normalLinearCheck(0.0140562303977f, 0.097927189487f, 0.282345816828f, 0.8f);
EXPECT_TRUE(normalLinear.IsClose(normalLinearCheck, 0.00001f));
// Bright values
Color brightGamma(1.234567f, 3.456789f, 5.678901f, 1.0f);
Color brightLinear = brightGamma.GammaToLinear();
Color brightLinearCheck(1.61904710087f, 17.9251290437f, 58.1399365547f, 1.0f);
EXPECT_TRUE(brightLinear.IsClose(brightLinearCheck, 0.00001f));
// Zero should stay the same
Color zeroColor = Color::CreateZero();
Color zeroColorLinear = zeroColor.GammaToLinear();
EXPECT_TRUE(zeroColorLinear.IsClose(zeroColor, 0.00001f));
// One should stay the same
Color oneColor = Color::CreateOne();
Color oneColorLinear = oneColor.GammaToLinear();
EXPECT_TRUE(oneColorLinear.IsClose(oneColor, 0.00001f));
}
TEST(MATH_Color, UintConversions)
{
// Convert to u32, floats expected to floor.
Color colorToU32(0.23f, 0.55f, 0.88f, 1.0f);
EXPECT_EQ(colorToU32.ToU32(), 0xFFE08C3A);
// Convert from u32
Color colorFromU32;
colorFromU32.FromU32(0xFFE08C3A);
EXPECT_TRUE(colorFromU32.IsClose(Color(0.22745098039f, 0.549019607843f, 0.87843137254f, 1.0f), 0.00001f));
// Convert to u32 and change to gamma space at the same time.
Color colorToU32Gamma(0.23f, 0.55f, 0.88f, 0.5f);
EXPECT_EQ(colorToU32Gamma.ToU32LinearToGamma(), 0x7FF1C383);
// Convert from u32 and change to linear space at the same time.
Color colorFromU32Gamma;
colorFromU32Gamma.FromU32GammaToLinear(0x7FF1C383);
EXPECT_TRUE(colorFromU32Gamma.IsClose(Color(0.22696587351f, 0.54572446137f, 0.879622396888f, 0.498039215686f), 0.00001f));
}
TEST(MATH_Color, Lerp)
{
Color colorSrc(1.0f, 0.0f, 0.2f, 0.8f);
Color colorDest(0.0f, 1.0f, 0.8f, 0.2f);
AZ_TEST_ASSERT(colorSrc.Lerp(colorDest, 0.0f).IsClose(colorSrc));
EXPECT_TRUE(colorSrc.Lerp(colorDest, 0.5f).IsClose(Color(0.5f, 0.5f, 0.5f, 0.5f), 0.00001f));
AZ_TEST_ASSERT(colorSrc.Lerp(colorDest, 1.0f).IsClose(colorDest));
}
TEST(MATH_Color, DotProduct)
{
Color color1(0.6f, 0.4f, 0.3f, 0.1f);
Color color2(0.5f, 0.7f, 0.3f, 0.8f);
Color color3(2.5f, 1.7f, 5.3f, 2.8f);
EXPECT_NEAR(color1.Dot(color2), 0.75f, 0.0001f);
EXPECT_NEAR(color1.Dot(color3), 4.05f, 0.0001f);
EXPECT_NEAR(color2.Dot(color3), 6.27f, 0.0001f);
EXPECT_NEAR(color1.Dot3(color2), 0.67f, 0.0001f);
EXPECT_NEAR(color1.Dot3(color3), 3.77f, 0.0001f);
EXPECT_NEAR(color2.Dot3(color3), 4.03f, 0.0001f);
}
TEST(MATH_Color, Addition)
{
Color color1(0.6f, 0.4f, 0.3f, 0.1f);
Color color2(0.5f, 0.7f, 0.3f, 0.8f);
Color colorSum(1.1f, 1.1f, 0.6f, 0.9f);
EXPECT_TRUE(colorSum.IsClose(color1 + color2, 0.0001f));
color1 += color2;
EXPECT_TRUE(colorSum.IsClose(color1, 0.0001f));
}
TEST(MATH_Color, Subtraction)
{
Color color1(0.6f, 0.4f, 0.3f, 0.1f);
Color color2(0.5f, 0.7f, 0.3f, 0.8f);
Color colorDiff(0.1f, -0.3f, 0.0f, -0.7f);
EXPECT_TRUE(colorDiff.IsClose(color1 - color2, 0.0001f));
color1 -= color2;
EXPECT_TRUE(colorDiff.IsClose(color1, 0.0001f));
}
TEST(MATH_Color, Multiplication)
{
Color color1(0.6f, 0.4f, 0.3f, 0.1f);
Color color2(0.5f, 0.7f, 0.3f, 0.8f);
Color colorProduct(0.3f, 0.28f, 0.09f, 0.08f);
Color color2Double(1.0f, 1.4f, 0.6f, 1.6f);
// Product of two colors
EXPECT_TRUE(colorProduct.IsClose(color1 * color2, 0.0001f));
// Multiply-assignment
color1 *= color2;
EXPECT_TRUE(colorProduct.IsClose(color1, 0.0001f));
// Product of color and float
EXPECT_TRUE(color2Double.IsClose(color2 * 2.0f));
// Multiply-assignment with single float
color2 *= 2.0f;
EXPECT_TRUE(color2Double.IsClose(color2));
}
TEST(MATH_Color, Division)
{
Color color1(0.6f, 0.4f, 0.3f, 0.1f);
Color color2(0.5f, 0.8f, 0.3f, 0.8f);
Color colorQuotient(1.2f, 0.5f, 1.0f, 0.125f);
Color color2Half(0.25f, 0.4f, 0.15f, 0.4f);
// Product of two colors
EXPECT_TRUE(colorQuotient.IsClose(color1 / color2, 0.0001f));
// Multiply-assignment
color1 /= color2;
EXPECT_TRUE(colorQuotient.IsClose(color1, 0.0001f));
// Product of color and float
EXPECT_TRUE(color2Half.IsClose(color2 / 2.0f));
// Multiply-assignment with single float
color2 /= 2.0f;
EXPECT_TRUE(color2Half.IsClose(color2));
}
}
@@ -0,0 +1,123 @@
/*
* 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/Crc.h>
#include <AzCore/std/containers/array.h>
#include <AzCore/UnitTest/TestTypes.h>
#if defined(HAVE_BENCHMARK)
#include "CrcTestsCompileTimeLiterals.h"
namespace Benchmark
{
inline namespace Crc32Internal
{
//! The number of different Crc32 Values to calculate at compile
//! This can be upped to check the Benchmarks
constexpr size_t NumConstevalCrc32CompileValues = 1;
using TestCrc32Array = AZStd::array<AZ::Crc32, NumConstevalCrc32CompileValues>;
constexpr TestCrc32Array CreateCrc32FromLiteral(const AZStd::string_view (&testValues) [TestStringLiteralSize])
{
TestCrc32Array resultArray{};
for (size_t crcIndex = 0; crcIndex < NumConstevalCrc32CompileValues; ++crcIndex)
{
resultArray[crcIndex] = AZ::Crc32(testValues[crcIndex % TestStringLiteralSize]);
}
return resultArray;
}
// Generates an array of Crc32 values based on the generated array of test string literals
// The time it takes this function to compile is the amount of time spent creating Crc32 values
// at compile time
constexpr TestCrc32Array GenerateTestCrc32Values()
{
return CreateCrc32FromLiteral(TestStringLiterals);
}
}
class Crc32BenchmarkEnvironment
: public AZ::Test::BenchmarkEnvironmentBase
{};
static Crc32BenchmarkEnvironment& s_crcBenchmarkEnv = AZ::Test::RegisterBenchmarkEnvironment<Crc32BenchmarkEnvironment>();
static void MeasureCrc32ConstevalTime(::benchmark::State& state)
{
// Runtime performance is not actually being measured by this test.
// This function only exist to calculate AZ::Crc32 values at compile time
for (auto _ : state)
{
constexpr auto resultArray = Crc32Internal::GenerateTestCrc32Values();
}
}
BENCHMARK(MeasureCrc32ConstevalTime);
}
#endif
namespace UnitTest
{
class Crc32Fixture
: public UnitTest::ScopedAllocatorSetupFixture
{
};
TEST_F(Crc32Fixture, Constructor_IsConstexpr)
{
static_assert(AZ::Crc32() == AZ::Crc32{ 0U }, "Default constructed Crc32 should 0");
static_assert(AZ::Crc32(0x6dc044c5) == AZ::Crc32(0x6dc044c5), R"(Crc32 should match the calculation on the string "group")");
static_assert(AZ::Crc32("EditorData") == AZ::Crc32(0xf44f1a1d), R"(Crc32 should match the calculation on the string "editordata")");
// Crc value for lowercase string of "group"
static_assert(AZ::Crc32(AZStd::string_view{ "EditorData+RuntimeData", 10 }) == AZ::Crc32(0xf44f1a1d), R"(Crc32 should match the calculation on the string "editordata")");
static_assert(AZ::Crc32("Editor", 6, true) == AZ::Crc32(0xccf1f1ba), R"(Crc32 should match the calculation on the string "editor")");
static_assert(AZ::Crc32("Editor", 6, false) == AZ::Crc32(0xcb5df48c), R"(Crc32 should match the calculation on the string "Editor")");
constexpr uint8_t binaryData[] = { 'B', 'i', 'n', 0x3 };
static_assert(AZ::Crc32(binaryData, 4, false) == AZ::Crc32(0x3528a896), R"(Crc32 should match the calculation on the binary blob "Bin\x03")");
}
TEST_F(Crc32Fixture, OperatorUint32t_IsConstexpr)
{
static_assert(static_cast<AZ::u32>(AZ::Crc32("EditorData")) == 0xf44f1a1d, R"(Crc32 should match the calculation on the string "editordata")");
}
TEST_F(Crc32Fixture, Add_IsConstexpr)
{
constexpr auto TestAdd = []() constexpr -> AZ::Crc32
{
AZ::Crc32 hello("Hello");
hello.Add(" World");
return hello;
};
constexpr AZ::Crc32 addResult = TestAdd();
static_assert(addResult == AZ::Crc32(0x0d4a1185), R"(Crc32 Add function result is unexpected)");
EXPECT_EQ(AZ::Crc32(0x0d4a1185), addResult);
}
TEST_F(Crc32Fixture, CrcConstevalMacro_IsConstexpr)
{
AZ::Crc32 constEvalLiteralValue = AZ_CRC_CE("Hello");
static_assert(AZ_CRC_CE("Hello") == AZ::Crc32(0x3610a686));
EXPECT_EQ(AZ::Crc32(0x3610a686), constEvalLiteralValue);
AZ::Crc32 constEvalIntValue = AZ_CRC_CE(0x4727dc92);
static_assert(AZ_CRC_CE(0x4727dc92) == AZ::Crc32(0x4727dc92));
EXPECT_EQ(AZ::Crc32(0x4727dc92), constEvalIntValue);
}
}
@@ -0,0 +1,127 @@
/*
* 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/std/string/string_view.h>
namespace Benchmark
{
inline namespace Crc32Internal
{
//! Array of string literals used to test creating Crc32 values at compile time
inline constexpr AZStd::string_view TestStringLiterals[] =
{
"Test0",
"Test1",
"Test2",
"Test3",
"Test4",
"Test5",
"Test6",
"Test7",
"Test8",
"Test9",
"Test10",
"Test11",
"Test12",
"Test13",
"Test14",
"Test15",
"Test16",
"Test17",
"Test18",
"Test19",
"Test20",
"Test21",
"Test22",
"Test23",
"Test24",
"Test25",
"Test26",
"Test27",
"Test28",
"Test29",
"Test30",
"Test31",
"Test32",
"Test33",
"Test34",
"Test35",
"Test36",
"Test37",
"Test38",
"Test39",
"Test40",
"Test41",
"Test42",
"Test43",
"Test44",
"Test45",
"Test46",
"Test47",
"Test48",
"Test49",
"Test50",
"Test51",
"Test52",
"Test53",
"Test54",
"Test55",
"Test56",
"Test57",
"Test58",
"Test59",
"Test60",
"Test61",
"Test62",
"Test63",
"Test64",
"Test65",
"Test66",
"Test67",
"Test68",
"Test69",
"Test70",
"Test71",
"Test72",
"Test73",
"Test74",
"Test75",
"Test76",
"Test77",
"Test78",
"Test79",
"Test80",
"Test81",
"Test82",
"Test83",
"Test84",
"Test85",
"Test86",
"Test87",
"Test88",
"Test89",
"Test90",
"Test91",
"Test92",
"Test93",
"Test94",
"Test95",
"Test96",
"Test97",
"Test98",
"Test99",
};
inline constexpr size_t TestStringLiteralSize = std::size(TestStringLiterals);
}
}
@@ -0,0 +1,85 @@
/*
* 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/Frustum.h>
#include <AzCore/UnitTest/TestTypes.h>
#if defined(HAVE_BENCHMARK)
#include <random>
#include <benchmark/benchmark.h>
namespace Benchmark
{
class BM_MathFrustum
: public benchmark::Fixture
{
public:
void SetUp([[maybe_unused]] const ::benchmark::State& state) override
{
m_testFrustum = AZ::Frustum(AZ::ViewFrustumAttributes(AZ::Transform::CreateIdentity(), 1.0f, 2.0f * atanf(0.5f), 10.0f, 90.0f));
m_dataArray.resize(1000);
const unsigned int seed = 1;
std::mt19937_64 rng(seed);
std::uniform_real_distribution<float> unif;
std::generate(m_dataArray.begin(), m_dataArray.end(), [&unif, &rng]()
{
Data data;
data.sphereCenter = AZ::Vector3(unif(rng), unif(rng), unif(rng)) * 100.0f;
data.sphereRadius = unif(rng) * 10.0f;
data.aabbMin = AZ::Vector3(unif(rng), unif(rng), unif(rng)) * 100.0f;
data.aabbMax = AZ::Vector3(unif(rng), unif(rng), unif(rng)).GetAbs() * 10.0f + data.aabbMin;
return data;
});
}
struct Data
{
AZ::Vector3 sphereCenter;
float sphereRadius;
AZ::Vector3 aabbMin;
AZ::Vector3 aabbMax;
};
std::vector<Data> m_dataArray;
AZ::Frustum m_testFrustum;
};
BENCHMARK_F(BM_MathFrustum, SphereIntersect)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& data : m_dataArray)
{
AZ::IntersectResult result = m_testFrustum.IntersectSphere(data.sphereCenter, data.sphereRadius);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathFrustum, AabbIntersect)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& data : m_dataArray)
{
AZ::IntersectResult result = m_testFrustum.IntersectAabb(data.aabbMin, data.aabbMax);
benchmark::DoNotOptimize(result);
}
}
}
}
#endif
@@ -0,0 +1,682 @@
/*
* 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/Frustum.h>
#include <AzCore/Math/ShapeIntersection.h>
#include <AzCore/UnitTest/TestTypes.h>
#include <AZTestShared/Math/MathTestHelpers.h>
namespace UnitTest
{
// Basic frustum centered on the origin
AZ::Frustum testFrustum1(
AZ::ViewFrustumAttributes(AZ::Transform::CreateIdentity(), 1.0f, AZ::Constants::HalfPi, 1.0f, 100.0f));
// Frustum that has slope 1/2 for the top/bottom/left/right clip planes
AZ::Frustum testFrustum2(
AZ::ViewFrustumAttributes(AZ::Transform::CreateIdentity(), 1.0f, 2.0f * atanf(0.5f), 10.0f, 90.0f));
TEST(MATH_Frustum, TestFrustumSphereDisjointNear)
{
// Test a sphere in front of the near clip with a radius too small to intersect
{
AZ::IntersectResult result = testFrustum1.IntersectSphere(AZ::Vector3::CreateZero(), 0.5f);
AZ_TEST_ASSERT(result == AZ::IntersectResult::Exterior);
}
{
AZ::IntersectResult result = testFrustum2.IntersectSphere(AZ::Vector3(0.0f, 9.0f, 0.0f), 0.5f);
AZ_TEST_ASSERT(result == AZ::IntersectResult::Exterior);
}
}
TEST(MATH_Frustum, TestFrustumSphereDisjointFar)
{
// Test a sphere beyond the far clip
{
AZ::IntersectResult result = testFrustum1.IntersectSphere(AZ::Vector3(0.0f, 102.0f, 0.0f), 1.0f);
AZ_TEST_ASSERT(result == AZ::IntersectResult::Exterior);
}
{
AZ::IntersectResult result = testFrustum2.IntersectSphere(AZ::Vector3(0.0f, 92.0f, 0.0f), 1.0f);
AZ_TEST_ASSERT(result == AZ::IntersectResult::Exterior);
}
}
TEST(MATH_Frustum, TestFrustumSphereIntersectNear)
{
// Test a sphere in front of the near clip with a radius large enough to intersect
{
AZ::IntersectResult result = testFrustum1.IntersectSphere(AZ::Vector3::CreateZero(), 1.5f);
AZ_TEST_ASSERT(result == AZ::IntersectResult::Overlaps);
}
{
AZ::IntersectResult result = testFrustum2.IntersectSphere(AZ::Vector3(0.0f, 9.0f, 0.0f), 1.5f);
AZ_TEST_ASSERT(result == AZ::IntersectResult::Overlaps);
}
}
TEST(MATH_Frustum, TestFrustumSphereIntersectLeft)
{
// Test a sphere on the left clip plane
{
AZ::IntersectResult result = testFrustum1.IntersectSphere(AZ::Vector3(-50.0f, 50.0f, 0.0f), 1.0f);
AZ_TEST_ASSERT(result == AZ::IntersectResult::Overlaps);
}
{
AZ::IntersectResult result = testFrustum2.IntersectSphere(AZ::Vector3(-25.0f, 50.0f, 0.0f), 1.0f);
AZ_TEST_ASSERT(result == AZ::IntersectResult::Overlaps);
}
}
TEST(MATH_Frustum, TestFrustumSphereIntersectRight)
{
// Test a sphere on the right clip plane
{
AZ::IntersectResult result = testFrustum1.IntersectSphere(AZ::Vector3(50.0f, 50.0f, 0.0f), 1.0f);
AZ_TEST_ASSERT(result == AZ::IntersectResult::Overlaps);
}
{
AZ::IntersectResult result = testFrustum2.IntersectSphere(AZ::Vector3(25.0f, 50.0f, 0.0f), 1.0f);
AZ_TEST_ASSERT(result == AZ::IntersectResult::Overlaps);
}
}
TEST(MATH_Frustum, TestFrustumSphereIntersectTop)
{
// Test a sphere on the top clip plane
{
AZ::IntersectResult result = testFrustum1.IntersectSphere(AZ::Vector3(0.0f, 50.0f, 50.0f), 1.0f);
AZ_TEST_ASSERT(result == AZ::IntersectResult::Overlaps);
}
{
AZ::IntersectResult result = testFrustum2.IntersectSphere(AZ::Vector3(0.0f, 50.0f, 25.0f), 1.0f);
AZ_TEST_ASSERT(result == AZ::IntersectResult::Overlaps);
}
}
TEST(MATH_Frustum, TestFrustumSphereIntersectBottom)
{
// Test a sphere on the bottom clip plane
{
AZ::IntersectResult result = testFrustum1.IntersectSphere(AZ::Vector3(0.0f, 50.0f, -50.0f), 1.0f);
AZ_TEST_ASSERT(result == AZ::IntersectResult::Overlaps);
}
{
AZ::IntersectResult result = testFrustum2.IntersectSphere(AZ::Vector3(0.0f, 50.0f, -25.0f), 1.0f);
AZ_TEST_ASSERT(result == AZ::IntersectResult::Overlaps);
}
}
TEST(MATH_Frustum, TestFrustumSphereContained)
{
// Test a sphere near the middle of the frustum
{
AZ::IntersectResult result = testFrustum1.IntersectSphere(AZ::Vector3(0.0f, 50.0f, 0.0f), 1.0f);
AZ_TEST_ASSERT(result == AZ::IntersectResult::Interior);
}
{
AZ::IntersectResult result = testFrustum2.IntersectSphere(AZ::Vector3(0.0f, 50.0f, 0.0f), 1.0f);
AZ_TEST_ASSERT(result == AZ::IntersectResult::Interior);
}
}
TEST(MATH_Frustum, TestFrustumAabbDisjointNear)
{
// Test an AABB in front of the near clip with a size too small to intersect
{
AZ::Vector3 center = AZ::Vector3::CreateZero();
AZ::IntersectResult result = testFrustum1.IntersectAabb(center - AZ::Vector3(0.5f), center + AZ::Vector3(0.5f));
AZ_TEST_ASSERT(result == AZ::IntersectResult::Exterior);
}
{
AZ::Vector3 center = AZ::Vector3(0.0f, 9.0f, 0.0f);
AZ::IntersectResult result = testFrustum2.IntersectAabb(center - AZ::Vector3(0.5f, 0.5f, 0.5f), center + AZ::Vector3(0.5f, 0.5f, 0.5f));
AZ_TEST_ASSERT(result == AZ::IntersectResult::Exterior);
}
}
TEST(MATH_Frustum, TestFrustumAabbDisjointFar)
{
// Test an AABB beyond the far clip
{
AZ::Vector3 center = AZ::Vector3(0.0f, 102.0f, 0.0f);
AZ::IntersectResult result = testFrustum1.IntersectAabb(center - AZ::Vector3(1.0f), center + AZ::Vector3(1.0f));
AZ_TEST_ASSERT(result == AZ::IntersectResult::Exterior);
}
{
AZ::Vector3 center = AZ::Vector3(0.0f, 92.0f, 0.0f);
AZ::IntersectResult result = testFrustum2.IntersectAabb(center - AZ::Vector3(1.0f), center + AZ::Vector3(1.0f));
AZ_TEST_ASSERT(result == AZ::IntersectResult::Exterior);
}
}
TEST(MATH_Frustum, TestFrustumAabbIntersectNear)
{
// Test an AABB in front of the near clip with a size large enough to intersect
{
AZ::Vector3 center = AZ::Vector3::CreateZero();
AZ::IntersectResult result = testFrustum1.IntersectAabb(center - AZ::Vector3(1.5f), center + AZ::Vector3(1.5f));
AZ_TEST_ASSERT(result == AZ::IntersectResult::Overlaps);
}
{
AZ::Vector3 center = AZ::Vector3(0.0f, 9.0f, 0.0f);
AZ::IntersectResult result = testFrustum2.IntersectAabb(center - AZ::Vector3(1.5f, 1.5f, 1.5f), center + AZ::Vector3(1.5f, 1.5f, 1.5f));
AZ_TEST_ASSERT(result == AZ::IntersectResult::Overlaps);
}
}
TEST(MATH_Frustum, TestFrustumAabbIntersectLeft)
{
// Test an AABB on the left clip plane
{
AZ::Vector3 center = AZ::Vector3(-50.0f, 50.0f, 0.0f);
AZ::IntersectResult result = testFrustum1.IntersectAabb(center - AZ::Vector3(1.0f), center + AZ::Vector3(1.0f));
AZ_TEST_ASSERT(result == AZ::IntersectResult::Overlaps);
}
{
AZ::Vector3 center = AZ::Vector3(-25.0f, 50.0f, 0.0f);
AZ::IntersectResult result = testFrustum2.IntersectAabb(center - AZ::Vector3(1.0f), center + AZ::Vector3(1.0f));
AZ_TEST_ASSERT(result == AZ::IntersectResult::Overlaps);
}
}
TEST(MATH_Frustum, TestFrustumAabbIntersectRight)
{
// Test an AABB on the right clip plane
{
AZ::Vector3 center = AZ::Vector3(50.0f, 50.0f, 0.0f);
AZ::IntersectResult result = testFrustum1.IntersectAabb(center - AZ::Vector3(1.0f), center + AZ::Vector3(1.0f));
AZ_TEST_ASSERT(result == AZ::IntersectResult::Overlaps);
}
{
AZ::Vector3 center = AZ::Vector3(25.0f, 50.0f, 0.0f);
AZ::IntersectResult result = testFrustum2.IntersectAabb(center - AZ::Vector3(1.0f), center + AZ::Vector3(1.0f));
AZ_TEST_ASSERT(result == AZ::IntersectResult::Overlaps);
}
}
TEST(MATH_Frustum, TestFrustumAabbIntersectTop)
{
// Test an AABB on the top clip plane
{
AZ::Vector3 center = AZ::Vector3(0.0f, 50.0f, 50.0f);
AZ::IntersectResult result = testFrustum1.IntersectAabb(center - AZ::Vector3(1.0f), center + AZ::Vector3(1.0f));
AZ_TEST_ASSERT(result == AZ::IntersectResult::Overlaps);
}
{
AZ::Vector3 center = AZ::Vector3(0.0f, 50.0f, 25.0f);
AZ::IntersectResult result = testFrustum2.IntersectAabb(center - AZ::Vector3(1.0f), center + AZ::Vector3(1.0f));
AZ_TEST_ASSERT(result == AZ::IntersectResult::Overlaps);
}
}
TEST(MATH_Frustum, TestFrustumAabbIntersectBottom)
{
// Test an AABB on the bottom clip plane
{
AZ::Vector3 center = AZ::Vector3(0.0f, 50.0f, -50.0f);
AZ::IntersectResult result = testFrustum1.IntersectAabb(center - AZ::Vector3(1.0f), center + AZ::Vector3(1.0f));
AZ_TEST_ASSERT(result == AZ::IntersectResult::Overlaps);
}
{
AZ::Vector3 center = AZ::Vector3(0.0f, 50.0f, -25.0f);
AZ::IntersectResult result = testFrustum2.IntersectAabb(center - AZ::Vector3(1.0f), center + AZ::Vector3(1.0f));
AZ_TEST_ASSERT(result == AZ::IntersectResult::Overlaps);
}
}
TEST(MATH_Frustum, TestFrustumAabbContained)
{
// Test an AABB near the middle of the frustum
{
AZ::Vector3 center = AZ::Vector3(0.0f, 50.0f, 0.0f);
AZ::IntersectResult result = testFrustum1.IntersectAabb(center - AZ::Vector3(1.0f), center + AZ::Vector3(1.0f));
AZ_TEST_ASSERT(result == AZ::IntersectResult::Interior);
}
{
AZ::Vector3 center = AZ::Vector3(0.0f, 50.0f, 0.0f);
AZ::IntersectResult result = testFrustum2.IntersectAabb(center - AZ::Vector3(1.0f), center + AZ::Vector3(1.0f));
AZ_TEST_ASSERT(result == AZ::IntersectResult::Interior);
}
}
TEST(MATH_Frustum, CalculateViewFrustumAttributesExample1)
{
const AZ::Vector3 translation(0.1f, 0.2f, 0.3f);
const AZ::Quaternion quaternion(0.52f, 0.56f, 0.56f, 0.32f);
const AZ::Transform transform = AZ::Transform::CreateFromQuaternionAndTranslation(quaternion, translation);
constexpr float aspectRatio = 1.3f;
constexpr float fovRadians = 0.8f;
constexpr float nearClip = 0.045f;
constexpr float farClip = 10.3f;
const AZ::Frustum frustum(AZ::ViewFrustumAttributes(transform, aspectRatio, fovRadians, nearClip, farClip));
const AZ::ViewFrustumAttributes viewFrustumAttributes = frustum.CalculateViewFrustumAttributes();
EXPECT_THAT(viewFrustumAttributes.m_worldTransform, IsClose(transform));
EXPECT_NEAR(viewFrustumAttributes.m_aspectRatio, aspectRatio, 1e-3f);
EXPECT_NEAR(viewFrustumAttributes.m_verticalFovRadians, fovRadians, 1e-3f);
EXPECT_NEAR(viewFrustumAttributes.m_nearClip, nearClip, 1e-3f);
EXPECT_NEAR(viewFrustumAttributes.m_farClip, farClip, 1e-3f);
}
TEST(MATH_Frustum, CalculateViewFrustumAttributesExample2)
{
const auto transform = AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisZ(5.0f)) *
AZ::Transform::CreateRotationX(AZ::DegToRad(45.0f)) * AZ::Transform::CreateRotationZ(AZ::DegToRad(90.0f));
constexpr float aspectRatio = 1024.0f/768.0f;
constexpr float fovRadians = AZ::DegToRad(60.0f);
constexpr float nearClip = 0.1f;
constexpr float farClip = 100.0f;
const AZ::Frustum frustum(AZ::ViewFrustumAttributes(transform, aspectRatio, fovRadians, nearClip, farClip));
const AZ::ViewFrustumAttributes viewFrustumAttributes = frustum.CalculateViewFrustumAttributes();
EXPECT_THAT(viewFrustumAttributes.m_worldTransform, IsClose(transform));
EXPECT_NEAR(viewFrustumAttributes.m_aspectRatio, aspectRatio, 1e-3f);
EXPECT_NEAR(viewFrustumAttributes.m_verticalFovRadians, fovRadians, 1e-3f);
EXPECT_NEAR(viewFrustumAttributes.m_nearClip, nearClip, 1e-3f);
EXPECT_NEAR(viewFrustumAttributes.m_farClip, farClip, 1e-3f);
}
TEST(MATH_Frustum, TestGetSetPlane)
{
// Assumes +x runs to the 'right', +y runs 'out' and +z points 'up'
// A frustum is defined by 6 planes. In this case a box shape.
AZ::Plane near_value = AZ::Plane::CreateFromNormalAndPoint(AZ::Vector3(0.f, 1.f, 0.f), AZ::Vector3(0.f, -5.f, 0.f));
AZ::Plane far_value = AZ::Plane::CreateFromNormalAndPoint(AZ::Vector3(0.f, -1.f, 0.f), AZ::Vector3(0.f, 5.f, 0.f));
AZ::Plane left = AZ::Plane::CreateFromNormalAndPoint(AZ::Vector3(1.f, 0.f, 0.f), AZ::Vector3(-5.f, 0.f, 0.f));
AZ::Plane right = AZ::Plane::CreateFromNormalAndPoint(AZ::Vector3(-1.f, 0.f, 0.f), AZ::Vector3(5.f, 0.f, 0.f));
AZ::Plane top = AZ::Plane::CreateFromNormalAndPoint(AZ::Vector3(0.f, 0.f, -1.f), AZ::Vector3(0.f, 0.f, 5.f));
AZ::Plane bottom = AZ::Plane::CreateFromNormalAndPoint(AZ::Vector3(0.f, 0.f, 1.f), AZ::Vector3(0.f, 0.f, -5.f));
AZ::Frustum frustum(near_value, far_value, left, right, top, bottom);
EXPECT_TRUE(frustum.GetPlane(AZ::Frustum::PlaneId::Near) == near_value);
EXPECT_TRUE(frustum.GetPlane(AZ::Frustum::PlaneId::Far) == far_value);
EXPECT_TRUE(frustum.GetPlane(AZ::Frustum::PlaneId::Left) == left);
EXPECT_TRUE(frustum.GetPlane(AZ::Frustum::PlaneId::Right) == right);
EXPECT_TRUE(frustum.GetPlane(AZ::Frustum::PlaneId::Top) == top);
EXPECT_TRUE(frustum.GetPlane(AZ::Frustum::PlaneId::Bottom) == bottom);
AZ::Plane near1 = AZ::Plane::CreateFromNormalAndPoint(AZ::Vector3(0.f, 1.f, 0.f), AZ::Vector3(0.f, -2.f, 0.f));
AZ::Plane far1 = AZ::Plane::CreateFromNormalAndPoint(AZ::Vector3(0.f, -1.f, 0.f), AZ::Vector3(0.f, 2.f, 0.f));
AZ::Plane left1 = AZ::Plane::CreateFromNormalAndPoint(AZ::Vector3(1.f, 0.f, 0.f), AZ::Vector3(-2.f, 0.f, 0.f));
AZ::Plane right1 = AZ::Plane::CreateFromNormalAndPoint(AZ::Vector3(-1.f, 0.f, 0.f), AZ::Vector3(2.f, 0.f, 0.f));
AZ::Plane top1 = AZ::Plane::CreateFromNormalAndPoint(AZ::Vector3(0.f, 0.f, -1.f), AZ::Vector3(0.f, 0.f, 2.f));
AZ::Plane bottom1 = AZ::Plane::CreateFromNormalAndPoint(AZ::Vector3(0.f, 0.f, 1.f), AZ::Vector3(0.f, 0.f, -2.f));
AZ::Frustum frustum1(near1, far1, left1, right1, top1, bottom1);
EXPECT_FALSE(frustum.IsClose(frustum1));
frustum.Set(frustum1);
EXPECT_TRUE(frustum.IsClose(frustum1));
frustum.SetPlane(AZ::Frustum::PlaneId::Near, near_value);
frustum.SetPlane(AZ::Frustum::PlaneId::Far, far_value);
frustum.SetPlane(AZ::Frustum::PlaneId::Left, left);
frustum.SetPlane(AZ::Frustum::PlaneId::Right, right);
frustum.SetPlane(AZ::Frustum::PlaneId::Top, top);
frustum.SetPlane(AZ::Frustum::PlaneId::Bottom, bottom);
EXPECT_TRUE(frustum.GetPlane(AZ::Frustum::PlaneId::Near) == near_value);
EXPECT_TRUE(frustum.GetPlane(AZ::Frustum::PlaneId::Far) == far_value);
EXPECT_TRUE(frustum.GetPlane(AZ::Frustum::PlaneId::Left) == left);
EXPECT_TRUE(frustum.GetPlane(AZ::Frustum::PlaneId::Right) == right);
EXPECT_TRUE(frustum.GetPlane(AZ::Frustum::PlaneId::Top) == top);
EXPECT_TRUE(frustum.GetPlane(AZ::Frustum::PlaneId::Bottom) == bottom);
frustum = frustum1;
EXPECT_TRUE(frustum.IsClose(frustum1));
}
// TODO: Test frustum creation from View-Projection Matrices
struct FrustumTestCase
{
AZ::Vector3 nearTopLeft;
AZ::Vector3 nearTopRight;
AZ::Vector3 nearBottomLeft;
AZ::Vector3 nearBottomRight;
AZ::Vector3 farTopLeft;
AZ::Vector3 farTopRight;
AZ::Vector3 farBottomLeft;
AZ::Vector3 farBottomRight;
// Used to set which plane is being used
AZ::Frustum::PlaneId plane;
// Used to set a test case name
std::string testCaseName;
};
std::ostream& operator<< (std::ostream& stream, const UnitTest::FrustumTestCase& frustumTestCase)
{
stream << "NearTopLeft: " << frustumTestCase.nearTopLeft
<< std::endl << "NearTopRight: " << frustumTestCase.nearTopRight
<< std::endl << "NearBottomRight: " << frustumTestCase.nearBottomRight
<< std::endl << "NearBottomLeft: " << frustumTestCase.nearBottomLeft
<< std::endl << "FarTopLeft: " << frustumTestCase.farTopLeft
<< std::endl << "FarTopRight: " << frustumTestCase.farTopRight
<< std::endl << "FarBottomLeft: " << frustumTestCase.farBottomLeft
<< std::endl << "FarBottomRight: " << frustumTestCase.farBottomRight;
return stream;
}
class Tests
: public ::testing::WithParamInterface <FrustumTestCase>
, public UnitTest::AllocatorsTestFixture
{
protected:
void SetUp() override
{
UnitTest::AllocatorsTestFixture::SetUp();
// Build a frustum from 8 points
// This allows us to generate test cases in each of the 9 regions in the 3x3 grid divided by the 6 planes,
// as well as test cases that span across those regions
m_testCase = GetParam();
// Planes can be generated from triangles. Points must wind counter-clockwise (right-handed) for the normal to point in the correct direction.
// The Frustum class assumes the plane normals point inwards
m_planes[AZ::Frustum::PlaneId::Near] = AZ::Plane::CreateFromTriangle(m_testCase.nearTopLeft, m_testCase.nearTopRight, m_testCase.nearBottomRight);
m_planes[AZ::Frustum::PlaneId::Far] = AZ::Plane::CreateFromTriangle(m_testCase.farTopRight, m_testCase.farTopLeft, m_testCase.farBottomLeft);
m_planes[AZ::Frustum::PlaneId::Left] = AZ::Plane::CreateFromTriangle(m_testCase.nearTopLeft, m_testCase.nearBottomLeft, m_testCase.farTopLeft);
m_planes[AZ::Frustum::PlaneId::Right] = AZ::Plane::CreateFromTriangle(m_testCase.nearTopRight, m_testCase.farTopRight, m_testCase.farBottomRight);
m_planes[AZ::Frustum::PlaneId::Top] = AZ::Plane::CreateFromTriangle(m_testCase.nearTopLeft, m_testCase.farTopLeft, m_testCase.farTopRight);
m_planes[AZ::Frustum::PlaneId::Bottom] = AZ::Plane::CreateFromTriangle(m_testCase.nearBottomLeft, m_testCase.nearBottomRight, m_testCase.farBottomRight);
// AZ::Plane::CreateFromTriangle uses Vector3::GetNormalized to create a normal, which is not perfectly normalized.
// Since the distance value is set by float dist = -(normal.Dot(v0));, this means that an imperfect normal actually gives you a slightly different distance
// and thus a slightly different plane. Correct that here.
m_planes[AZ::Frustum::PlaneId::Near] = AZ::Plane::CreateFromNormalAndPoint(m_planes[AZ::Frustum::PlaneId::Near].GetNormal().GetNormalized(), m_testCase.nearTopLeft);
m_planes[AZ::Frustum::PlaneId::Far] = AZ::Plane::CreateFromNormalAndPoint(m_planes[AZ::Frustum::PlaneId::Far].GetNormal().GetNormalized(), m_testCase.farTopRight);
m_planes[AZ::Frustum::PlaneId::Left] = AZ::Plane::CreateFromNormalAndPoint(m_planes[AZ::Frustum::PlaneId::Left].GetNormal().GetNormalized(), m_testCase.nearTopLeft);
m_planes[AZ::Frustum::PlaneId::Right] = AZ::Plane::CreateFromNormalAndPoint(m_planes[AZ::Frustum::PlaneId::Right].GetNormal().GetNormalized(), m_testCase.nearTopRight);
m_planes[AZ::Frustum::PlaneId::Top] = AZ::Plane::CreateFromNormalAndPoint(m_planes[AZ::Frustum::PlaneId::Top].GetNormal().GetNormalized(), m_testCase.nearTopLeft);
m_planes[AZ::Frustum::PlaneId::Bottom] = AZ::Plane::CreateFromNormalAndPoint(m_planes[AZ::Frustum::PlaneId::Bottom].GetNormal().GetNormalized(), m_testCase.nearBottomLeft);
// Create the frustum itself
for (AZ::Frustum::PlaneId planeId = AZ::Frustum::PlaneId::Near; planeId < AZ::Frustum::PlaneId::MAX; ++planeId)
{
m_frustum.SetPlane(planeId, m_planes[planeId]);
}
// Get the center points
m_centerPoints[AZ::Frustum::PlaneId::Near] = m_testCase.nearTopLeft + 0.5f * (m_testCase.nearBottomRight - m_testCase.nearTopLeft);
m_centerPoints[AZ::Frustum::PlaneId::Far] = m_testCase.farTopLeft + 0.5f * (m_testCase.farBottomRight - m_testCase.farTopLeft);
m_centerPoints[AZ::Frustum::PlaneId::Left] = m_testCase.nearTopLeft + 0.5f * (m_testCase.farBottomLeft - m_testCase.nearTopLeft);
m_centerPoints[AZ::Frustum::PlaneId::Right] = m_testCase.nearTopRight + 0.5f * (m_testCase.farBottomRight - m_testCase.nearTopRight);
m_centerPoints[AZ::Frustum::PlaneId::Top] = m_testCase.nearTopLeft + 0.5f * (m_testCase.farTopRight - m_testCase.nearTopLeft);
m_centerPoints[AZ::Frustum::PlaneId::Bottom] = m_testCase.nearBottomLeft + 0.5f * (m_testCase.farBottomRight - m_testCase.nearBottomLeft);
// Get the shortest edge of the frustum
AZStd::vector<AZ::Vector3> edges;
// Near plane
edges.push_back(m_testCase.nearTopLeft - m_testCase.nearTopRight);
edges.push_back(m_testCase.nearTopRight - m_testCase.nearBottomRight);
edges.push_back(m_testCase.nearBottomRight - m_testCase.nearBottomLeft);
edges.push_back(m_testCase.nearBottomLeft - m_testCase.nearTopLeft);
// Edges from near plane to far plane
edges.push_back(m_testCase.nearTopLeft - m_testCase.farTopLeft);
edges.push_back(m_testCase.nearTopRight - m_testCase.farTopRight);
edges.push_back(m_testCase.nearBottomRight - m_testCase.farBottomRight);
edges.push_back(m_testCase.nearBottomLeft - m_testCase.farBottomLeft);
// Far plane
edges.push_back(m_testCase.farTopLeft - m_testCase.farTopRight);
edges.push_back(m_testCase.farTopRight - m_testCase.farBottomRight);
edges.push_back(m_testCase.farBottomRight - m_testCase.farBottomLeft);
edges.push_back(m_testCase.farBottomLeft - m_testCase.farTopLeft);
m_minEdgeLength = std::numeric_limits<float>::max();
for (const AZ::Vector3& edge : edges)
{
m_minEdgeLength = AZ::GetMin(m_minEdgeLength, static_cast<float>(edge.GetLength()));
}
}
AZ::Sphere GenerateSphereOutsidePlane(const AZ::Plane& plane, const AZ::Vector3& planeCenter)
{
// Get a radius small enough for the entire sphere to fit outside the plane without extending past the other planes in the frustum
float radius = 0.25f * m_minEdgeLength;
// Get the outward pointing normal of the plane
AZ::Vector3 normal = -1.0f * plane.GetNormal();
// Create a sphere that is outside the plane
AZ::Vector3 center = planeCenter + normal * (radius + m_marginOfErrorOffset);
return AZ::Sphere(center, radius);
}
AZ::Sphere GenerateSphereInsidePlane(const AZ::Plane& plane, const AZ::Vector3& planeCenter)
{
// Get a radius small enough for the entire sphere to fit outside the plane without extending past the other planes in the frustum
float radius = 0.25f * m_minEdgeLength;
// Get the inward pointing normal of the plane
AZ::Vector3 normal = plane.GetNormal();
// Create a sphere that is inside the plane
AZ::Vector3 center = planeCenter + normal * (radius + m_marginOfErrorOffset);
return AZ::Sphere(center, radius);
}
// The points used to generate the test cases
FrustumTestCase m_testCase;
// The planes generated from the points.
// Keep these around for access to the normals, which are used to generate shapes inside/outside of the frustum
AZ::Plane m_planes[AZ::Frustum::PlaneId::MAX];
// The center points on the frustum for each plane
AZ::Vector3 m_centerPoints[AZ::Frustum::PlaneId::MAX];
// The length of the shortest edge in the frustum
float m_minEdgeLength = std::numeric_limits<float>::max();
// Shapes generated inside/outside the frustum will be offset by this much as a margin of error
// Through trial and error determined that, for the test cases below, the sphere needs to be offset from the frustum by at least 0.05625f to be guaranteed to pass,
// which gives a reasonable idea of how precise these intersection tests are.
// For the box shaped frustum, the tests passed when offset by FLT_EPSILON, but the frustums further away from the origin were less precise.
float m_marginOfErrorOffset = 0.05625f;
// The frustum under test
AZ::Frustum m_frustum;
};
// Tests that a frustum does not contain a sphere that is outside the frustum
TEST_P(Tests, FrustumContainsSphere_SphereOutsidePlane_False)
{
AZ::Sphere testSphere = GenerateSphereOutsidePlane(m_planes[m_testCase.plane], m_centerPoints[m_testCase.plane]);
EXPECT_FALSE(AZ::ShapeIntersection::Contains(m_frustum, testSphere)) << "Frustum contains sphere even though sphere is completely outside the frustum." << std::endl << "Frustum:" << std::endl << m_testCase << std::endl << "Sphere:" << std::endl << testSphere << std::endl;
}
// Tests that a sphere outside the frustum does not overlap the frustum
TEST_P(Tests, SphereOverlapsFrustum_SphereOutsidePlane_False)
{
AZ::Sphere testSphere = GenerateSphereOutsidePlane(m_planes[m_testCase.plane], m_centerPoints[m_testCase.plane]);
EXPECT_FALSE(AZ::ShapeIntersection::Overlaps(testSphere, m_frustum)) << "Sphere overlaps frustum even though sphere is completely outside the frustum." << std::endl << "Frustum:" << std::endl << m_testCase << std::endl << "Sphere:" << std::endl << testSphere << std::endl;
}
// Tests that a frustum contains a sphere that is inside the frustum
TEST_P(Tests, FrustumContainsSphere_SphereInsidePlane_True)
{
AZ::Sphere testSphere = GenerateSphereInsidePlane(m_planes[m_testCase.plane], m_centerPoints[m_testCase.plane]);
EXPECT_TRUE(AZ::ShapeIntersection::Contains(m_frustum, testSphere)) << "Frustum does not contain sphere even though sphere is completely inside the frustum." << std::endl << "Frustum:" << std::endl << m_testCase << std::endl << "Sphere:" << std::endl << testSphere << std::endl;
}
// Tests that a sphere inside the frustum overlaps the frustum
TEST_P(Tests, SphereOverlapsFrustum_SphereInsidePlane_True)
{
AZ::Sphere testSphere = GenerateSphereInsidePlane(m_planes[m_testCase.plane], m_centerPoints[m_testCase.plane]);
EXPECT_TRUE(AZ::ShapeIntersection::Overlaps(testSphere, m_frustum)) << "Sphere does not overlap frustum even though sphere is completely inside the frustum." << std::endl << "Frustum:" << std::endl << m_testCase << std::endl << "Sphere:" << std::endl << testSphere << std::endl;
}
// Tests that a frustum does not contain a sphere that is half inside half outside the frustum
TEST_P(Tests, FrustumContainsSphere_SphereHalfInsideHalfOutsidePlane_False)
{
AZ::Sphere testSphere(m_centerPoints[m_testCase.plane], m_minEdgeLength * .25f);
EXPECT_FALSE(AZ::ShapeIntersection::Contains(m_frustum, testSphere)) << "Frustum contains sphere even though sphere is partially outside the frustum." << std::endl << "Frustum:" << std::endl << m_testCase << std::endl << "Sphere:" << std::endl << testSphere << std::endl;
}
// Tests that a sphere half inside half outside the frustum overlaps the frustum
TEST_P(Tests, SphereOverlapsFrustum_SphereHalfInsideHalfOutsidePlane_True)
{
AZ::Sphere testSphere(m_centerPoints[m_testCase.plane], m_minEdgeLength * .25f);
EXPECT_TRUE(AZ::ShapeIntersection::Overlaps(testSphere, m_frustum)) << "Sphere does not overlap frustum even though sphere is partially inside the frustum." << std::endl << "Frustum:" << std::endl << m_testCase << std::endl << "Sphere:" << std::endl << testSphere << std::endl;
}
std::vector<FrustumTestCase> GenerateFrustumIntersectionTestCases()
{
std::vector<FrustumTestCase> testCases;
std::vector<FrustumTestCase> frustums;
// 2x2x2 box (Z-up coordinate system)
FrustumTestCase box;
box.nearTopLeft = AZ::Vector3(-1.0f, -1.0f, 1.0f);
box.nearTopRight = AZ::Vector3(1.0f, -1.0f, 1.0f);
box.nearBottomLeft = AZ::Vector3(-1.0f, -1.0f, -1.0f);
box.nearBottomRight = AZ::Vector3(1.0f, -1.0f, -1.0f);
box.farTopLeft = AZ::Vector3(-1.0f, 1.0f, 1.0f);
box.farTopRight = AZ::Vector3(1.0f, 1.0f, 1.0f);
box.farBottomLeft = AZ::Vector3(-1.0f, 1.0f, -1.0f);
box.farBottomRight = AZ::Vector3(1.0f, 1.0f, -1.0f);
box.testCaseName = "BoxShaped";
frustums.push_back(box);
// Default values in a CCamera from Cry_Camera.h
FrustumTestCase defaultCameraFrustum;
defaultCameraFrustum.nearTopLeft = AZ::Vector3(-0.204621f, 0.200000f, 0.153465f);
defaultCameraFrustum.nearTopRight = AZ::Vector3(0.204621f, 0.200000f, 0.153465f);
defaultCameraFrustum.nearBottomLeft = AZ::Vector3(-0.204621f, 0.200000f, -0.153465f);
defaultCameraFrustum.nearBottomRight = AZ::Vector3(0.204621f, 0.200000f, -0.153465f);
defaultCameraFrustum.farTopLeft = AZ::Vector3(-1047.656982f, 1024.000000f, 785.742737f);
defaultCameraFrustum.farTopRight = AZ::Vector3(1047.656982f, 1024.000000f, 785.742737f);
defaultCameraFrustum.farBottomLeft = AZ::Vector3(-1047.656982f, 1024.000000f, -785.742737f);
defaultCameraFrustum.farBottomRight = AZ::Vector3(1047.656982f, 1024.000000f, -785.742737f);
defaultCameraFrustum.testCaseName = "DefaultCamera";
frustums.push_back(defaultCameraFrustum);
// These frustums were generated from flying around StarterGame and dumping the frustum values for the viewport camera and shadow cascade frustums to a log file
FrustumTestCase starterGame0;
starterGame0.nearTopLeft = AZ::Vector3(41656.351563f, 794907.750000f, -604483.687500f);
starterGame0.nearTopRight = AZ::Vector3(41662.343750f, 794907.375000f, -604483.687500f);
starterGame0.nearBottomLeft = AZ::Vector3(41656.164063f, 794904.125000f, -604488.437500f);
starterGame0.nearBottomRight = AZ::Vector3(41662.156250f, 794903.750000f, -604488.437500f);
starterGame0.farTopLeft = AZ::Vector3(41677.691406f, 795314.937500f, -604793.375000f);
starterGame0.farTopRight = AZ::Vector3(41683.683594f, 795314.562500f, -604793.375000f);
starterGame0.farBottomLeft = AZ::Vector3(41677.503906f, 795311.312500f, -604798.125000f);
starterGame0.farBottomRight = AZ::Vector3(41683.496094f, 795310.937500f, -604798.125000f);
starterGame0.testCaseName = "StarterGame0";
frustums.push_back(starterGame0);
FrustumTestCase starterGame1;
starterGame1.nearTopLeft = AZ::Vector3(0.166996f, 0.240156f, 0.117468f);
starterGame1.nearTopRight = AZ::Vector3(0.226816f, -0.184736f, 0.117423f);
starterGame1.nearBottomLeft = AZ::Vector3(0.169258f, 0.240499f, -0.113460f);
starterGame1.nearBottomRight = AZ::Vector3(0.229078f, -0.184393f, -0.113506f);
starterGame1.farTopLeft = AZ::Vector3(83.497986f, 120.077904f, 58.734165f);
starterGame1.farTopRight = AZ::Vector3(113.408234f, -92.368172f, 58.711285f);
starterGame1.farBottomLeft = AZ::Vector3(84.628860f, 120.249550f, -56.730221f);
starterGame1.farBottomRight = AZ::Vector3(114.539108f, -92.196526f, -56.753101f);
starterGame1.testCaseName = "StarterGame1";
frustums.push_back(starterGame1);
FrustumTestCase starterGame2;
starterGame2.nearTopLeft = AZ::Vector3(-0.007508f, 0.091724f, -0.043323f);
starterGame2.nearTopRight = AZ::Vector3(0.018772f, 0.090096f, -0.043323f);
starterGame2.nearBottomLeft = AZ::Vector3(-0.008393f, 0.077435f, -0.065421f);
starterGame2.nearBottomRight = AZ::Vector3(0.017887f, 0.075807f, -0.065421f);
starterGame2.farTopLeft = AZ::Vector3(-11.637362f, 142.172897f, -67.151001f);
starterGame2.farTopRight = AZ::Vector3(29.096817f, 139.649338f, -67.151001f);
starterGame2.farBottomLeft = AZ::Vector3(-13.009484f, 120.024765f, -101.403290f);
starterGame2.farBottomRight = AZ::Vector3(27.724697f, 117.501205f, -101.403290f);
starterGame2.testCaseName = "StarterGame2";
frustums.push_back(starterGame2);
FrustumTestCase starterGame3;
starterGame3.nearTopLeft = AZ::Vector3(0.211831f, -0.207308f, 0.107297f);
starterGame3.nearTopRight = AZ::Vector3(-0.217216f, -0.201659f, 0.107297f);
starterGame3.nearBottomLeft = AZ::Vector3(0.211954f, -0.197980f, -0.123455f);
starterGame3.nearBottomRight = AZ::Vector3(-0.217093f, -0.192331f, -0.123455f);
starterGame3.farTopLeft = AZ::Vector3(8473.246094f, -8292.310547f, 4291.892578f);
starterGame3.farTopRight = AZ::Vector3(-8688.623047f, -8066.359863f, 4291.892578f);
starterGame3.farBottomLeft = AZ::Vector3(8478.158203f, -7919.196777f, -4938.199219f);
starterGame3.farBottomRight = AZ::Vector3(-8683.710938f, -7693.245605f, -4938.199219f);
starterGame3.testCaseName = "StarterGame3";
frustums.push_back(starterGame3);
// For each test frustum, create test cases for every plane
for (FrustumTestCase frustum : frustums)
{
for (int i = 0; i < AZ::Frustum::PlaneId::MAX; ++i)
{
frustum.plane = static_cast<AZ::Frustum::PlaneId>(i);
testCases.push_back(frustum);
}
}
return testCases;
}
std::string GenerateFrustumIntersectionTestCaseName(const ::testing::TestParamInfo<FrustumTestCase>& info)
{
std::string testCaseName = info.param.testCaseName;
switch (info.param.plane)
{
case AZ::Frustum::PlaneId::Near:
testCaseName += "_Near";
break;
case AZ::Frustum::PlaneId::Far:
testCaseName += "_Far";
break;
case AZ::Frustum::PlaneId::Left:
testCaseName += "_Left";
break;
case AZ::Frustum::PlaneId::Right:
testCaseName += "_Right";
break;
case AZ::Frustum::PlaneId::Top:
testCaseName += "_Top";
break;
case AZ::Frustum::PlaneId::Bottom:
testCaseName += "_Bottom";
break;
}
return testCaseName;
}
INSTANTIATE_TEST_CASE_P(
MATH_Frustum, Tests, ::testing::ValuesIn(GenerateFrustumIntersectionTestCases()),
GenerateFrustumIntersectionTestCaseName);
TEST(MATH_Frustum, AabbInsideOrientatedFrustum)
{
// position the frustum slightly along the x-axis looking down the negative x-axis
const AZ::Vector3 frustumOrigin = AZ::Vector3(5.0f, 0.0f, 0.0f);
const AZ::Quaternion frustumOrientation = AZ::Quaternion::CreateRotationZ(AZ::DegToRad(90.0f));
const AZ::Transform frustumTransform =
AZ::Transform::CreateFromQuaternionAndTranslation(frustumOrientation, frustumOrigin);
const AZ::Frustum viewFrustum = AZ::Frustum(
AZ::ViewFrustumAttributes(frustumTransform, 1920.0f / 1080.0f, AZ::DegToRad(60.0f), 0.1f, 100.0f));
// position the aabb at the origin (inside the frustum's view)
const AZ::Aabb aabb = AZ::Aabb::CreateFromMinMax(AZ::Vector3(-0.5f), AZ::Vector3(0.5f));
// the aabb is contained within the frustum
EXPECT_TRUE(AZ::ShapeIntersection::Contains(viewFrustum, aabb));
}
} // namespace UnitTest
@@ -0,0 +1,940 @@
/*
* 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/Vector2.h>
#include <AzCore/Math/Vector3.h>
#include <AzCore/Math/IntersectSegment.h>
#include <AzCore/UnitTest/TestTypes.h>
using namespace AZ;
namespace UnitTest
{
TEST(MATH_Intersection, ClosestSegmentSegment)
{
// line2 right and above line1 (no overlap, parallel)
{
Vector3 line1Start(0.0f, 0.0f, 0.0f);
Vector3 line1End(4.0f, 0.0f, 0.0f);
Vector3 line2Start(7.0f, 0.0f, 4.0f);
Vector3 line2End(10.0f, 0.0f, 4.0f);
Vector3 line1ClosestPoint;
Vector3 line2ClosestPoint;
float line1Proportion;
float line2Proportion;
Intersect::ClosestSegmentSegment(line1Start, line1End, line2Start, line2End, line1Proportion, line2Proportion, line1ClosestPoint, line2ClosestPoint);
float pointDifference = (line2ClosestPoint - line1ClosestPoint).GetLength();
EXPECT_TRUE(pointDifference == 5.0f);
EXPECT_TRUE(line1Proportion == 1.0f);
EXPECT_TRUE(line2Proportion == 0.0f);
}
// line2 halfway over the top of the line1 (overlap, parallel)
{
Vector3 line1Start(0.0f, 0.0f, 0.0f);
Vector3 line1End(4.0f, 0.0f, 0.0f);
Vector3 line2Start(2.0f, 0.0f, 3.0f);
Vector3 line2End(6.0f, 0.0f, 3.0f);
Vector3 line1ClosestPoint;
Vector3 line2ClosestPoint;
float line1Proportion;
float line2Proportion;
Intersect::ClosestSegmentSegment(line1Start, line1End, line2Start, line2End, line1Proportion, line2Proportion, line1ClosestPoint, line2ClosestPoint);
float pointDifference = (line2ClosestPoint - line1ClosestPoint).GetLength();
EXPECT_TRUE(pointDifference == 3.0f);
EXPECT_TRUE(line1Proportion == 0.5f);
EXPECT_TRUE(line2Proportion == 0.0f);
}
// line2 over the top of the line1 (inside, parallel)
{
Vector3 line1Start(0.0f, 0.0f, 0.0f);
Vector3 line1End(8.0f, 0.0f, 0.0f);
Vector3 line2Start(2.0f, 0.0f, 3.0f);
Vector3 line2End(6.0f, 0.0f, 3.0f);
Vector3 line1ClosestPoint;
Vector3 line2ClosestPoint;
float line1Proportion;
float line2Proportion;
Intersect::ClosestSegmentSegment(line1Start, line1End, line2Start, line2End, line1Proportion, line2Proportion, line1ClosestPoint, line2ClosestPoint);
float pointDifference = (line2ClosestPoint - line1ClosestPoint).GetLength();
EXPECT_TRUE(pointDifference == 3.0f);
EXPECT_TRUE(line1Proportion == 0.25f);
EXPECT_TRUE(line2Proportion == 0.0f);
}
// line2 over the top of the line1 (overlap, skew (cross))
{
Vector3 line1Start(0.0f, 0.0f, 0.0f);
Vector3 line1End(8.0f, 0.0f, 0.0f);
Vector3 line2Start(4.0f, 4.0f, 4.0f);
Vector3 line2End(4.0f, -4.0f, 4.0f);
Vector3 line1ClosestPoint;
Vector3 line2ClosestPoint;
float line1Proportion;
float line2Proportion;
Intersect::ClosestSegmentSegment(line1Start, line1End, line2Start, line2End, line1Proportion, line2Proportion, line1ClosestPoint, line2ClosestPoint);
float pointDifference = (line2ClosestPoint - line1ClosestPoint).GetLength();
EXPECT_TRUE(pointDifference == 4.0f);
EXPECT_TRUE(line1Proportion == 0.5f);
EXPECT_TRUE(line2Proportion == 0.5f);
}
// line2 flat diagonal to line1 (no overlap, skew)
{
Vector3 line1Start(0.0f, 0.0f, 0.0f);
Vector3 line1End(4.0f, 4.0f, 0.0f);
Vector3 line2Start(10.0f, 0.0f, 0.0f);
Vector3 line2End(6.0f, 4.0f, 0.0f);
Vector3 line1ClosestPoint;
Vector3 line2ClosestPoint;
float line1Proportion;
float line2Proportion;
Intersect::ClosestSegmentSegment(line1Start, line1End, line2Start, line2End, line1Proportion, line2Proportion, line1ClosestPoint, line2ClosestPoint);
float pointDifference = (line2ClosestPoint - line1ClosestPoint).GetLength();
EXPECT_TRUE(pointDifference == 2.0f);
EXPECT_TRUE(line1Proportion == 1.0f);
EXPECT_TRUE(line2Proportion == 1.0f);
}
// line2 perpendicular to line1 (skew, no overlap)
{
Vector3 line1Start(0.0f, 0.0f, 0.0f);
Vector3 line1End(4.0f, 0.0f, 0.0f);
Vector3 line2Start(2.0f, 1.0f, 0.0f);
Vector3 line2End(2.0f, 4.0f, 0.0f);
Vector3 line1ClosestPoint;
Vector3 line2ClosestPoint;
float line1Proportion;
float line2Proportion;
Intersect::ClosestSegmentSegment(line1Start, line1End, line2Start, line2End, line1Proportion, line2Proportion, line1ClosestPoint, line2ClosestPoint);
float pointDifference = (line2ClosestPoint - line1ClosestPoint).GetLength();
EXPECT_TRUE(pointDifference == 1.0f);
EXPECT_TRUE(line1Proportion == 0.5f);
EXPECT_TRUE(line2Proportion == 0.0f);
}
// line 1 degenerates to point
{
Vector3 line1Start(4.0f, 2.0f, 0.0f);
Vector3 line1End(4.0f, 2.0f, 0.0f);
Vector3 line2Start(2.0f, 0.0f, 0.0f);
Vector3 line2End(2.0f, 4.0f, 0.0f);
Vector3 line1ClosestPoint;
Vector3 line2ClosestPoint;
float line1Proportion;
float line2Proportion;
Intersect::ClosestSegmentSegment(line1Start, line1End, line2Start, line2End, line1Proportion, line2Proportion, line1ClosestPoint, line2ClosestPoint);
float pointDifference = (line2ClosestPoint - line1ClosestPoint).GetLength();
EXPECT_TRUE(pointDifference == 2.0f);
EXPECT_TRUE(line1Proportion == 0.0f);
EXPECT_TRUE(line2Proportion == 0.5f);
}
// line 2 degenerates to point
{
Vector3 line1Start(0.0f, 0.0f, 0.0f);
Vector3 line1End(4.0f, 0.0f, 0.0f);
Vector3 line2Start(2.0f, 1.0f, 0.0f);
Vector3 line2End(2.0f, 1.0f, 0.0f);
Vector3 line1ClosestPoint;
Vector3 line2ClosestPoint;
float line1Proportion;
float line2Proportion;
Intersect::ClosestSegmentSegment(line1Start, line1End, line2Start, line2End, line1Proportion, line2Proportion, line1ClosestPoint, line2ClosestPoint);
float pointDifference = (line2ClosestPoint - line1ClosestPoint).GetLength();
EXPECT_TRUE(pointDifference == 1.0f);
EXPECT_TRUE(line1Proportion == 0.5f);
EXPECT_TRUE(line2Proportion == 0.0f);
}
// both lines degenerate to points
{
Vector3 line1Start(5.0f, 5.0f, 5.0f);
Vector3 line1End(5.0f, 5.0f, 5.0f);
Vector3 line2Start(10.0f, 10.0f, 10.0f);
Vector3 line2End(10.0f, 10.0f, 10.0f);
Vector3 line1ClosestPoint;
Vector3 line2ClosestPoint;
float line1Proportion;
float line2Proportion;
Intersect::ClosestSegmentSegment(line1Start, line1End, line2Start, line2End, line1Proportion, line2Proportion, line1ClosestPoint, line2ClosestPoint);
float pointDifference = (line2ClosestPoint - line1ClosestPoint).GetLength();
// (10, 10, 10) - (5, 5, 5) == (5, 5, 5)
// |(5,5,5)| == sqrt(5*5+5*5+5*5) == sqrt(75)
EXPECT_TRUE(pointDifference == sqrtf(75.0f));
EXPECT_TRUE(line1Proportion == 0.0f);
EXPECT_TRUE(line2Proportion == 0.0f);
}
}
TEST(MATH_Intersection, ClosestPointSegment)
{
// point above center of line
{
Vector3 lineStart(0.0f, 0.0f, 0.0f);
Vector3 lineEnd(4.0f, 0.0f, 0.0f);
Vector3 point(2.0f, 2.0f, 0.0f);
Vector3 lineClosestPoint;
float lineProportion;
Intersect::ClosestPointSegment(point, lineStart, lineEnd, lineProportion, lineClosestPoint);
float pointDifference = (lineClosestPoint - point).GetLength();
EXPECT_TRUE(pointDifference == 2.0f);
EXPECT_TRUE(lineProportion == 0.5f);
}
// point same height behind line
{
Vector3 lineStart(0.0f, 0.0f, 0.0f);
Vector3 lineEnd(0.0f, 4.0f, 0.0f);
Vector3 point(0.0f, -2.0f, 0.0f);
Vector3 lineClosestPoint;
float lineProportion;
Intersect::ClosestPointSegment(point, lineStart, lineEnd, lineProportion, lineClosestPoint);
float pointDifference = (lineClosestPoint - point).GetLength();
EXPECT_TRUE(pointDifference == 2.0f);
EXPECT_TRUE(lineProportion == 0.0f);
}
// point passed end of line
{
Vector3 lineStart(0.0f, 0.0f, 0.0f);
Vector3 lineEnd(0.0f, 0.0f, 10.0f);
Vector3 point(0.0f, 0.0f, 15.0f);
Vector3 lineClosestPoint;
float lineProportion;
Intersect::ClosestPointSegment(point, lineStart, lineEnd, lineProportion, lineClosestPoint);
float pointDifference = (lineClosestPoint - point).GetLength();
EXPECT_TRUE(pointDifference == 5.0f);
EXPECT_TRUE(lineProportion == 1.0f);
}
// point above part way along line
{
Vector3 lineStart(0.0f, 0.0f, 0.0f);
Vector3 lineEnd(4.0f, 4.0f, 0.0f);
Vector3 point(3.0f, 3.0f, -1.0f);
Vector3 lineClosestPoint;
float lineProportion;
Intersect::ClosestPointSegment(point, lineStart, lineEnd, lineProportion, lineClosestPoint);
float pointDifference = (lineClosestPoint - point).GetLength();
EXPECT_TRUE(pointDifference == 1.0f);
EXPECT_TRUE(lineProportion == 0.75f);
}
}
class MATH_IntersectRayCappedCylinderTest
: public AllocatorsFixture
{
protected:
void SetUp() override
{
m_cylinderEnd1 = Vector3(1.1f, 2.2f, 3.3f);
m_cylinderDir = Vector3(1.0, 1.0f, 1.0f);
m_cylinderDir.Normalize();
m_radiusDir = Vector3(-1.0f, 1.0f, 0.0f);
m_radiusDir.Normalize();
m_cylinderHeight = 5.5f;
m_cylinderRadius = 3.768f;
}
void TearDown() override
{
}
Vector3 m_cylinderEnd1;
Vector3 m_cylinderDir;
Vector3 m_radiusDir;
float m_cylinderHeight;
float m_cylinderRadius;
};
TEST_F(MATH_IntersectRayCappedCylinderTest, RayOverlapCylinderAxis)
{
Vector3 rayOrigin = m_cylinderEnd1 - 0.5f * m_cylinderHeight * m_cylinderDir;
Vector3 rayDir = m_cylinderDir;
float t1 = AZ::Constants::FloatMax;
float t2 = AZ::Constants::FloatMax;
int hits = Intersect::IntersectRayCappedCylinder(rayOrigin, rayDir, m_cylinderEnd1, m_cylinderDir, m_cylinderHeight, m_cylinderRadius, t1, t2);
EXPECT_EQ(hits, 2);
}
TEST_F(MATH_IntersectRayCappedCylinderTest, RayParallelToAndInsideCylinder)
{
Vector3 rayOrigin = m_cylinderEnd1 - 0.5f * m_cylinderHeight * m_cylinderDir + 0.5f * m_cylinderRadius * m_radiusDir;
Vector3 rayDir = m_cylinderDir;
float t1 = AZ::Constants::FloatMax;
float t2 = AZ::Constants::FloatMax;
int hits = Intersect::IntersectRayCappedCylinder(rayOrigin, rayDir, m_cylinderEnd1, m_cylinderDir, m_cylinderHeight, m_cylinderRadius, t1, t2);
EXPECT_EQ(hits, 2);
}
TEST_F(MATH_IntersectRayCappedCylinderTest, RayAlongCylinderSuface)
{
Vector3 rayOrigin = m_cylinderEnd1 - 0.5f * m_cylinderHeight * m_cylinderDir + m_cylinderRadius * m_radiusDir;
Vector3 rayDir = m_cylinderDir;
float t1 = AZ::Constants::FloatMax;
float t2 = AZ::Constants::FloatMax;
int hits = Intersect::IntersectRayCappedCylinder(rayOrigin, rayDir, m_cylinderEnd1, m_cylinderDir, m_cylinderHeight, m_cylinderRadius, t1, t2);
EXPECT_EQ(hits, 2);
}
TEST_F(MATH_IntersectRayCappedCylinderTest, RayParallelAndRayOriginInsideCylinder)
{
Vector3 rayOrigin = m_cylinderEnd1 + 0.2f * m_cylinderHeight * m_cylinderDir + 0.5f * m_cylinderRadius * m_radiusDir;
Vector3 rayDir = m_cylinderDir;
float t1 = AZ::Constants::FloatMax;
float t2 = AZ::Constants::FloatMax;
int hits = Intersect::IntersectRayCappedCylinder(rayOrigin, rayDir, m_cylinderEnd1, m_cylinderDir, m_cylinderHeight, m_cylinderRadius, t1, t2);
EXPECT_EQ(hits, 1);
}
TEST_F(MATH_IntersectRayCappedCylinderTest, RayParallelToButOutsideCylinder)
{
Vector3 rayOrigin = m_cylinderEnd1 - 0.5f * m_cylinderHeight * m_cylinderDir + 1.1f * m_cylinderRadius * m_radiusDir;
Vector3 rayDir = m_cylinderDir;
float t1 = AZ::Constants::FloatMax;
float t2 = AZ::Constants::FloatMax;
int hits = Intersect::IntersectRayCappedCylinder(rayOrigin, rayDir, m_cylinderEnd1, m_cylinderDir, m_cylinderHeight, m_cylinderRadius, t1, t2);
EXPECT_EQ(hits, 0);
}
TEST_F(MATH_IntersectRayCappedCylinderTest, RayOriginOuside_RayDirParallelButPointingAwayCylinder)
{
Vector3 rayOrigin = m_cylinderEnd1 + 1.5f * m_cylinderHeight * m_cylinderDir + 0.5f * m_cylinderRadius * m_radiusDir;
Vector3 rayDir = m_cylinderDir;
rayDir.Normalize();
float t1 = AZ::Constants::FloatMax;
float t2 = AZ::Constants::FloatMax;
int hits = Intersect::IntersectRayCappedCylinder(rayOrigin, rayDir, m_cylinderEnd1, m_cylinderDir, m_cylinderHeight, m_cylinderRadius, t1, t2);
EXPECT_EQ(hits, 0);
}
TEST_F(MATH_IntersectRayCappedCylinderTest, RayOriginOutsideEnds_BothIntersectionsInbetweenEnds)
{
Vector3 intersection2 = m_cylinderEnd1 + 0.8f * m_cylinderHeight * m_cylinderDir - m_cylinderRadius * m_radiusDir;
Vector3 intersection1 = m_cylinderEnd1 + 0.2f * m_cylinderHeight * m_cylinderDir + m_cylinderRadius * m_radiusDir;
Vector3 rayDir = intersection2 - intersection1;
Vector3 rayOrigin = intersection1 - 2.0f * rayDir;
rayDir.Normalize();
float t1 = AZ::Constants::FloatMax;
float t2 = AZ::Constants::FloatMax;
int hits = Intersect::IntersectRayCappedCylinder(rayOrigin, rayDir, m_cylinderEnd1, m_cylinderDir, m_cylinderHeight, m_cylinderRadius, t1, t2);
EXPECT_EQ(hits, 2);
}
TEST_F(MATH_IntersectRayCappedCylinderTest, RayOriginOutsideEnds_Intersection1OnEnd1_Intersection2InbetweenEnds)
{
Vector3 intersection2 = m_cylinderEnd1 + 0.6f * m_cylinderHeight * m_cylinderDir - m_cylinderRadius * m_radiusDir;
Vector3 intersection1 = m_cylinderEnd1 + 0.6f * m_cylinderRadius * m_radiusDir;
Vector3 rayDir = intersection2 - intersection1;
Vector3 rayOrigin = intersection1 - 2.0f * rayDir;
rayDir.Normalize();
float t1 = AZ::Constants::FloatMax;
float t2 = AZ::Constants::FloatMax;
int hits = Intersect::IntersectRayCappedCylinder(rayOrigin, rayDir, m_cylinderEnd1, m_cylinderDir, m_cylinderHeight, m_cylinderRadius, t1, t2);
EXPECT_EQ(hits, 2);
}
TEST_F(MATH_IntersectRayCappedCylinderTest, RayOriginInside_RayGoThroughCylinder)
{
Vector3 rayOrigin = m_cylinderEnd1 + 0.3f * m_cylinderHeight * m_cylinderDir - 0.4f * m_cylinderRadius * m_radiusDir;
Vector3 intersection = m_cylinderEnd1 + 0.8f * m_cylinderHeight * m_cylinderDir + m_cylinderRadius * m_radiusDir;
Vector3 rayDir = (intersection - rayOrigin).GetNormalized();
float t1 = AZ::Constants::FloatMax;
float t2 = AZ::Constants::FloatMax;
int hits = Intersect::IntersectRayCappedCylinder(rayOrigin, rayDir, m_cylinderEnd1, m_cylinderDir, m_cylinderHeight, m_cylinderRadius, t1, t2);
EXPECT_EQ(hits, 1);
}
TEST_F(MATH_IntersectRayCappedCylinderTest, RayOriginOutsideCylinderButInBetweenEnds_RayDirPointingAwayCylinder)
{
Vector3 rayOrigin = m_cylinderEnd1 + 0.4f * m_cylinderHeight * m_cylinderDir - 1.6f * m_cylinderRadius * m_radiusDir;
Vector3 rayDir = rayOrigin - m_cylinderEnd1;
rayDir.Normalize();
float t1 = AZ::Constants::FloatMax;
float t2 = AZ::Constants::FloatMax;
int hits = Intersect::IntersectRayCappedCylinder(rayOrigin, rayDir, m_cylinderEnd1, m_cylinderDir, m_cylinderHeight, m_cylinderRadius, t1, t2);
EXPECT_EQ(hits, 0);
}
TEST_F(MATH_IntersectRayCappedCylinderTest, RayOriginOutsideCylinderButInBetweenEnds_RayDirMissingCylinderEnds)
{
Vector3 rayOrigin = m_cylinderEnd1 + 0.4f * m_cylinderHeight * m_cylinderDir - 1.6f * m_cylinderRadius * m_radiusDir;
Vector3 rayDir = (m_cylinderEnd1 + m_cylinderHeight * m_cylinderDir - 1.2f * m_cylinderRadius * m_radiusDir) - rayOrigin;
rayDir.Normalize();
float t1 = AZ::Constants::FloatMax;
float t2 = AZ::Constants::FloatMax;
int hits = Intersect::IntersectRayCappedCylinder(rayOrigin, rayDir, m_cylinderEnd1, m_cylinderDir, m_cylinderHeight, m_cylinderRadius, t1, t2);
EXPECT_EQ(hits, 0);
}
TEST_F(MATH_IntersectRayCappedCylinderTest, RayOriginOutsideCylinderButInBetweenEnds_RayDirShootingOutEnd1)
{
Vector3 rayOrigin = m_cylinderEnd1 + 0.4f * m_cylinderHeight * m_cylinderDir - 1.6f * m_cylinderRadius * m_radiusDir;
Vector3 rayDir = (m_cylinderEnd1 - 0.2f * m_cylinderRadius * m_radiusDir) - rayOrigin;
rayDir.Normalize();
float t1 = AZ::Constants::FloatMax;
float t2 = AZ::Constants::FloatMax;
int hits = Intersect::IntersectRayCappedCylinder(rayOrigin, rayDir, m_cylinderEnd1, m_cylinderDir, m_cylinderHeight, m_cylinderRadius, t1, t2);
EXPECT_EQ(hits, 2);
}
TEST_F(MATH_IntersectRayCappedCylinderTest, RayOriginOutsideCylinderButInBetweenEnds_RayDirShootingOutEnd2)
{
Vector3 rayOrigin = m_cylinderEnd1 + 0.4f * m_cylinderHeight * m_cylinderDir - 1.6f * m_cylinderRadius * m_radiusDir;
Vector3 rayDir = (m_cylinderEnd1 + 0.67f * m_cylinderHeight * m_cylinderDir) - rayOrigin;
rayDir.Normalize();
float t1 = AZ::Constants::FloatMax;
float t2 = AZ::Constants::FloatMax;
int hits = Intersect::IntersectRayCappedCylinder(rayOrigin, rayDir, m_cylinderEnd1, m_cylinderDir, m_cylinderHeight, m_cylinderRadius, t1, t2);
EXPECT_EQ(hits, 2);
}
TEST_F(MATH_IntersectRayCappedCylinderTest, RayOriginOutsideCylinderButInBetweenEnds_RayDirShootingOutCylinder)
{
Vector3 perpDir(1.0f, 0, 0);
float parallelDiff = 0.453f;
float perpendicularDiff = 2.54f;
Vector3 rayOrigin = (m_cylinderEnd1 + parallelDiff * m_cylinderHeight * m_cylinderDir) + perpendicularDiff * m_cylinderRadius * perpDir;
Vector3 rayDir = (m_cylinderEnd1 + m_cylinderHeight * m_cylinderDir - 2.6f * m_cylinderRadius * perpDir) - rayOrigin;
rayDir.Normalize();
float t1 = AZ::Constants::FloatMax;
float t2 = AZ::Constants::FloatMax;
int hits = Intersect::IntersectRayCappedCylinder(rayOrigin, rayDir, m_cylinderEnd1, m_cylinderDir, m_cylinderHeight, m_cylinderRadius, t1, t2);
EXPECT_EQ(hits, 2);
}
class MATH_IntersectRayConeTest
: public AllocatorsFixture
{
protected:
void SetUp() override
{
m_coneApex = Vector3(1.264f, 6.773f, 9.612f);
m_coneDir = Vector3(-64.0f, -82.783f, -12.97f);
m_radiusDir = Vector3(m_coneDir.GetY(), -m_coneDir.GetX(), 0.0f);
m_coneDir.Normalize();
m_coneHeight = 1.643f;
m_coneRadius = m_coneHeight;
m_radiusDir.Normalize();
m_tangentDir = m_coneHeight * m_coneDir + m_coneRadius * m_radiusDir;
m_tangentDir.Normalize();
}
void TearDown() override
{
}
Vector3 m_coneApex;
Vector3 m_coneDir;
Vector3 m_radiusDir;
Vector3 m_tangentDir; // direction of tangent line on the surface from the apex to the base
float m_coneHeight;
float m_coneRadius;
};
TEST_F(MATH_IntersectRayConeTest, RayOriginAtApex_RayDirParallelConeSurface)
{
Vector3 rayOrigin = m_coneApex;
Vector3 rayDir = m_tangentDir;
float t1 = 0.0f;
float t2 = 0.0f;
int hits = Intersect::IntersectRayCone(rayOrigin, rayDir, m_coneApex, m_coneDir, m_coneHeight, m_coneRadius, t1, t2);
EXPECT_EQ(hits, 2);
}
TEST_F(MATH_IntersectRayConeTest, RayOriginOutsideConeBelowApex_RayDirParallelConeSurface)
{
Vector3 rayOrigin = m_coneApex + 0.5f * m_coneHeight * m_coneDir - m_coneRadius * m_radiusDir;
Vector3 rayDir = m_tangentDir;
float t1 = 0.0f;
float t2 = 0.0f;
int hits = Intersect::IntersectRayCone(rayOrigin, rayDir, m_coneApex, m_coneDir, m_coneHeight, m_coneRadius, t1, t2);
EXPECT_EQ(hits, 2);
}
TEST_F(MATH_IntersectRayConeTest, RayOriginOutsideConeBelowBase_RayDirParallelConeSurface)
{
Vector3 rayOrigin = m_coneApex + 1.5f * m_coneHeight * m_coneDir - m_coneRadius * m_radiusDir;
Vector3 rayDir = m_tangentDir;
float t1 = 0.0f;
float t2 = 0.0f;
int hits = Intersect::IntersectRayCone(rayOrigin, rayDir, m_coneApex, m_coneDir, m_coneHeight, m_coneRadius, t1, t2);
EXPECT_EQ(hits, 0);
}
TEST_F(MATH_IntersectRayConeTest, RayOriginOutsideConeAboveApex_RayDirParallelConeSurface)
{
Vector3 rayOrigin = m_coneApex - 1.5f * m_coneHeight * m_coneDir - m_coneRadius * m_radiusDir;
Vector3 rayDir = m_tangentDir;
float t1 = 0.0f;
float t2 = 0.0f;
int hits = Intersect::IntersectRayCone(rayOrigin, rayDir, m_coneApex, m_coneDir, m_coneHeight, m_coneRadius, t1, t2);
EXPECT_EQ(hits, 0);
}
TEST_F(MATH_IntersectRayConeTest, RayOriginInsideCone_RayDirParallelConeSurface)
{
Vector3 rayOrigin = m_coneApex + 0.5f * m_coneHeight * m_coneDir;
Vector3 rayDir = m_tangentDir;
float t1 = 0.0f;
float t2 = 0.0f;
int hits = Intersect::IntersectRayCone(rayOrigin, rayDir, m_coneApex, m_coneDir, m_coneHeight, m_coneRadius, t1, t2);
EXPECT_EQ(hits, 1);
}
TEST_F(MATH_IntersectRayConeTest, RayOriginInsideCone_RayDirParallelConeSurfaceOppositeDirection)
{
Vector3 rayOrigin = m_coneApex + 0.5f * m_coneHeight * m_coneDir;
Vector3 rayDir = -m_tangentDir;
float t1 = 0.0f;
float t2 = 0.0f;
int hits = Intersect::IntersectRayCone(rayOrigin, rayDir, m_coneApex, m_coneDir, m_coneHeight, m_coneRadius, t1, t2);
EXPECT_EQ(hits, 1);
}
TEST_F(MATH_IntersectRayConeTest, RayOriginOutsideCone_RayDirThroughApex)
{
Vector3 rayOrigin = m_coneApex + 0.03f * m_coneDir + m_coneRadius * m_radiusDir;
Vector3 rayDir = (m_coneApex - rayOrigin).GetNormalized();
float t1 = 0.0f;
float t2 = 0.0f;
int hits = Intersect::IntersectRayCone(rayOrigin, rayDir, m_coneApex, m_coneDir, m_coneHeight, m_coneRadius, t1, t2);
EXPECT_EQ(hits, 1);
}
TEST_F(MATH_IntersectRayConeTest, RayOriginInsideMirrorCone_RayDirThroughApex)
{
Vector3 rayOrigin = m_coneApex - 0.5f * m_coneHeight * m_coneDir + 0.25f * m_coneRadius * m_radiusDir;
Vector3 rayDir = (m_coneApex - rayOrigin).GetNormalized();
float t1 = 0.0f;
float t2 = 0.0f;
int hits = Intersect::IntersectRayCone(rayOrigin, rayDir, m_coneApex, m_coneDir, m_coneHeight, m_coneRadius, t1, t2);
EXPECT_EQ(hits, 2);
}
#if AZ_TRAIT_DISABLE_FAILED_MATH_TESTS
TEST_F(MATH_IntersectRayConeTest, DISABLED_RayOriginOutsideBase_RayDirThroughApex)
#else
TEST_F(MATH_IntersectRayConeTest, RayOriginOutsideBase_RayDirThroughApex)
#endif // AZ_TRAIT_DISABLE_FAILED_MATH_TESTS
{
Vector3 rayOrigin = m_coneApex + 1.3f * m_coneHeight * m_coneDir + 0.3f * m_coneRadius * m_radiusDir;
Vector3 rayDir = (m_coneApex - rayOrigin).GetNormalized();
float t1 = 0.0f;
float t2 = 0.0f;
int hits = Intersect::IntersectRayCone(rayOrigin, rayDir, m_coneApex, m_coneDir, m_coneHeight, m_coneRadius, t1, t2);
EXPECT_EQ(hits, 2);
}
TEST_F(MATH_IntersectRayConeTest, RayOriginOutsideBase_RayDirThroughConeSurface)
{
Vector3 rayOrigin = m_coneApex + 1.3f * m_coneHeight * m_coneDir + 0.3f * m_coneRadius * m_radiusDir;
Vector3 rayDir = (m_coneApex - 0.3f * m_coneRadius * m_radiusDir - rayOrigin).GetNormalized();
float t1 = 0.0f;
float t2 = 0.0f;
int hits = Intersect::IntersectRayCone(rayOrigin, rayDir, m_coneApex, m_coneDir, m_coneHeight, m_coneRadius, t1, t2);
EXPECT_EQ(hits, 2);
}
TEST_F(MATH_IntersectRayConeTest, RayOriginOutsideConeApexSide_RayDirThroughConeSurface)
{
Vector3 rayOrigin = m_coneApex + 1.5f * m_coneHeight * m_coneDir + 0.7f * m_coneRadius * m_radiusDir;
Vector3 rayDir = (m_coneApex + 0.7f * m_coneHeight * m_coneDir - 0.8f * m_coneRadius * m_radiusDir - rayOrigin).GetNormalized();
float t1 = 0.0f;
float t2 = 0.0f;
int hits = Intersect::IntersectRayCone(rayOrigin, rayDir, m_coneApex, m_coneDir, m_coneHeight, m_coneRadius, t1, t2);
EXPECT_EQ(hits, 2);
}
TEST_F(MATH_IntersectRayConeTest, RayOriginOutsideConeApexSide_RayDirMissCone)
{
Vector3 rayOrigin = m_coneApex - 0.5f * m_coneHeight * m_coneDir + 0.7f * m_coneRadius * m_radiusDir;
Vector3 rayDir = (m_coneApex + 1.1f * m_coneHeight * m_coneDir + 1.1f * m_coneRadius * m_radiusDir - rayOrigin).GetNormalized();
float t1 = 0.0f;
float t2 = 0.0f;
int hits = Intersect::IntersectRayCone(rayOrigin, rayDir, m_coneApex, m_coneDir, m_coneHeight, m_coneRadius, t1, t2);
EXPECT_EQ(hits, 0);
}
TEST_F(MATH_IntersectRayConeTest, RayOriginInsideMirrorConeApexSide_RayDirMissCone)
{
Vector3 rayOrigin = m_coneApex - 0.7f * m_coneHeight * m_coneDir + 0.6f * m_coneRadius * m_radiusDir;
Vector3 rayDir = (m_coneApex + 1.5f * m_coneHeight * m_coneDir + 1.5f * m_coneRadius * m_radiusDir - rayOrigin).GetNormalized();
float t1 = 0.0f;
float t2 = 0.0f;
int hits = Intersect::IntersectRayCone(rayOrigin, rayDir, m_coneApex, m_coneDir, m_coneHeight, m_coneRadius, t1, t2);
EXPECT_EQ(hits, 0);
}
class MATH_IntersectRayQuadTest
: public AllocatorsFixture
{
protected:
void SetUp() override
{
m_vertexA = Vector3(1.04f, 2.46f, 5.26f);
m_axisB = Vector3(1.0f, 2.0f, 3.0f);
m_axisD = Vector3(3.0f, -2.0f, 1.0f);
m_axisB.Normalize();
m_axisD.Normalize();
m_lengthAxisB = 4.56f;
m_lengthAxisD = 7.19f;
m_normal = m_axisB.Cross(m_axisD);
m_normal.Normalize();
m_vertexB = m_vertexA + m_lengthAxisB * m_axisB;
m_vertexC = m_vertexA + m_lengthAxisB * m_axisB + m_lengthAxisD * m_axisD;
m_vertexD = m_vertexA + m_lengthAxisD * m_axisD;
}
void TearDown() override
{
}
Vector3 m_vertexA;
Vector3 m_vertexB;
Vector3 m_vertexC;
Vector3 m_vertexD;
// two axes defining the quad plane, originating from m_vertexA
Vector3 m_axisB;
Vector3 m_axisD;
float m_lengthAxisB;
float m_lengthAxisD;
Vector3 m_normal;
};
TEST_F(MATH_IntersectRayQuadTest, RayShootingAway_CCW)
{
Vector3 rayOrigin = (m_vertexA + 0.23f * m_axisB + 0.75f * m_axisD) + 2.0f * m_normal;
Vector3 rayDir = m_normal;
rayDir.Normalize();
float t = 0.0f;
int hit = Intersect::IntersectRayQuad(rayOrigin, rayDir, m_vertexA, m_vertexB, m_vertexC, m_vertexD, t);
EXPECT_EQ(hit, 0);
}
TEST_F(MATH_IntersectRayQuadTest, RayShootingAway_CW)
{
Vector3 rayOrigin = (m_vertexA + 0.23f * m_axisB + 0.75f * m_axisD) + 2.0f * m_normal;
Vector3 rayDir = m_normal;
rayDir.Normalize();
float t = 0.0f;
int hit = Intersect::IntersectRayQuad(rayOrigin, rayDir, m_vertexC, m_vertexB, m_vertexA, m_vertexD, t);
EXPECT_EQ(hit, 0);
}
TEST_F(MATH_IntersectRayQuadTest, RayIntersectTriangleABC_CCW)
{
Vector3 rayOrigin = m_vertexA + 2.0f * m_normal;
Vector3 rayDir = ((0.5f * (0.5f * m_vertexA + 0.5f * m_vertexC)) + 0.5f * m_vertexB) - rayOrigin;
rayDir.Normalize();
float t = 0.0f;
int hit = Intersect::IntersectRayQuad(rayOrigin, rayDir, m_vertexA, m_vertexB, m_vertexC, m_vertexD, t);
EXPECT_EQ(hit, 1);
}
TEST_F(MATH_IntersectRayQuadTest, RayIntersectTriangleABC_CW)
{
Vector3 rayOrigin = m_vertexA + 2.0f * m_normal;
Vector3 rayDir = ((0.5f * (0.5f * m_vertexA + 0.5f * m_vertexC)) + 0.5f * m_vertexB) - rayOrigin;
rayDir.Normalize();
float t = 0.0f;
int hit = Intersect::IntersectRayQuad(rayOrigin, rayDir, m_vertexD, m_vertexC, m_vertexB, m_vertexA, t);
EXPECT_EQ(hit, 1);
}
TEST_F(MATH_IntersectRayQuadTest, RayIntersectTriangleACD_CCW)
{
Vector3 rayOrigin = m_vertexA + 2.0f * m_normal;
Vector3 rayDir = ((0.5f * (0.5f * m_vertexA + 0.5f * m_vertexC)) + 0.5f * m_vertexD) - rayOrigin;
rayDir.Normalize();
float t = 0.0f;
int hit = Intersect::IntersectRayQuad(rayOrigin, rayDir, m_vertexA, m_vertexB, m_vertexC, m_vertexD, t);
EXPECT_EQ(hit, 1);
}
TEST_F(MATH_IntersectRayQuadTest, RayIntersectTriangleACD_CW)
{
Vector3 rayOrigin = m_vertexA + 2.0f * m_normal;
Vector3 rayDir = ((0.5f * (0.5f * m_vertexA + 0.5f * m_vertexC)) + 0.5f * m_vertexD) - rayOrigin;
rayDir.Normalize();
float t = 0.0f;
int hit = Intersect::IntersectRayQuad(rayOrigin, rayDir, m_vertexA, m_vertexD, m_vertexC, m_vertexB, t);
EXPECT_EQ(hit, 1);
}
TEST_F(MATH_IntersectRayQuadTest, RayIntersectLineAC_CCW)
{
Vector3 rayOrigin = m_vertexA + 2.0f * m_normal;
Vector3 rayDir = (0.5f * m_vertexA + 0.5f * m_vertexC) - rayOrigin;
rayDir.Normalize();
float t = 0.0f;
int hit = Intersect::IntersectRayQuad(rayOrigin, rayDir, m_vertexA, m_vertexB, m_vertexC, m_vertexD, t);
EXPECT_EQ(hit, 1);
}
TEST_F(MATH_IntersectRayQuadTest, RayShootOverAB_CCW)
{
Vector3 rayOrigin = m_vertexA + 2.0f * m_normal;
Vector3 rayDir = m_vertexA + 1.7f * m_lengthAxisB * m_axisB - rayOrigin;
rayDir.Normalize();
float t = 0.0f;
int hit = Intersect::IntersectRayQuad(rayOrigin, rayDir, m_vertexA, m_vertexB, m_vertexC, m_vertexD, t);
EXPECT_EQ(hit, 0);
}
TEST_F(MATH_IntersectRayQuadTest, RayShootOverAC_CW)
{
Vector3 rayOrigin = m_vertexA + 2.0f * m_normal;
Vector3 rayDir = m_vertexA + 1.3f * (m_vertexD - m_vertexA) - rayOrigin;
rayDir.Normalize();
float t = 0.0f;
int hit = Intersect::IntersectRayQuad(rayOrigin, rayDir, m_vertexA, m_vertexD, m_vertexC, m_vertexB, t);
EXPECT_EQ(hit, 0);
}
class MATH_IntersectRayBoxTest
: public AllocatorsFixture
{
protected:
void SetUp() override
{
m_boxCenter = Vector3(1.234f, 2.345f, 9.824f);
m_boxAxis1 = Vector3(1.0f, 2.0f, 3.0f);
m_boxAxis2 = Vector3(-3.0f, 2.0f, -1.0f);
m_boxAxis3 = m_boxAxis1.Cross(m_boxAxis2);
m_boxAxis1.Normalize();
m_boxAxis2.Normalize();
m_boxAxis3.Normalize();
m_boxHalfExtentAxis1 = 4.775f;
m_boxHalfExtentAxis2 = 8.035f;
m_boxHalfExtentAxis3 = 14.007f;
}
void TearDown() override
{
}
Vector3 m_boxCenter;
Vector3 m_boxAxis1;
Vector3 m_boxAxis2;
Vector3 m_boxAxis3;
float m_boxHalfExtentAxis1;
float m_boxHalfExtentAxis2;
float m_boxHalfExtentAxis3;
};
TEST_F(MATH_IntersectRayBoxTest, RayOriginOutside_HitBoxAxis1Side)
{
Vector3 rayOrigin = m_boxCenter + 2.0f * m_boxHalfExtentAxis1 * m_boxAxis1 + 2.0f * m_boxHalfExtentAxis2 * m_boxAxis2;
Vector3 rayDir = m_boxCenter + m_boxHalfExtentAxis1 * m_boxAxis1 - rayOrigin;
rayDir.Normalize();
float t = 0.0f;
int hit = Intersect::IntersectRayBox(rayOrigin, rayDir, m_boxCenter, m_boxAxis1, m_boxAxis2, m_boxAxis3,
m_boxHalfExtentAxis1, m_boxHalfExtentAxis2, m_boxHalfExtentAxis3, t);
EXPECT_EQ(hit, 1);
}
TEST_F(MATH_IntersectRayBoxTest, RayOriginOutside_ShootAwayFromBox)
{
Vector3 rayOrigin = m_boxCenter + 2.0f * m_boxHalfExtentAxis3 * m_boxAxis3 + 2.0f * m_boxHalfExtentAxis2 * m_boxAxis2;
Vector3 rayDir = rayOrigin - m_boxCenter + m_boxHalfExtentAxis3 * m_boxAxis3;
rayDir.Normalize();
float t = 0.0f;
int hit = Intersect::IntersectRayBox(rayOrigin, rayDir, m_boxCenter, m_boxAxis1, m_boxAxis2, m_boxAxis3,
m_boxHalfExtentAxis1, m_boxHalfExtentAxis2, m_boxHalfExtentAxis3, t);
EXPECT_EQ(hit, 0);
}
TEST_F(MATH_IntersectRayBoxTest, RayOriginOutside_RayParallelToAxis2MissBox)
{
Vector3 rayOrigin = m_boxCenter + 2.0f * m_boxHalfExtentAxis3 * m_boxAxis3 + 2.0f * m_boxHalfExtentAxis2 * m_boxAxis2;
Vector3 rayDir = m_boxAxis2;
float t = 0.0f;
int hit = Intersect::IntersectRayBox(rayOrigin, rayDir, m_boxCenter, m_boxAxis1, m_boxAxis2, m_boxAxis3,
m_boxHalfExtentAxis1, m_boxHalfExtentAxis2, m_boxHalfExtentAxis3, t);
EXPECT_EQ(hit, 0);
}
TEST_F(MATH_IntersectRayBoxTest, RayOriginInside_ShootToConner)
{
Vector3 rayOrigin = m_boxCenter + 0.5f * m_boxHalfExtentAxis1 * m_boxAxis1 +
0.5f * m_boxHalfExtentAxis2 * m_boxAxis2 + 0.5f * m_boxHalfExtentAxis3 * m_boxAxis3;
Vector3 rayDir = (m_boxCenter - m_boxHalfExtentAxis1 * m_boxAxis1 -
m_boxHalfExtentAxis2 * m_boxAxis2 - m_boxHalfExtentAxis3 * m_boxAxis3) - rayOrigin;
rayDir.Normalize();
float t = 0.0f;
int hit = Intersect::IntersectRayBox(rayOrigin, rayDir, m_boxCenter, m_boxAxis1, m_boxAxis2, m_boxAxis3,
m_boxHalfExtentAxis1, m_boxHalfExtentAxis2, m_boxHalfExtentAxis3, t);
EXPECT_EQ(hit, 1);
}
class MATH_IntersectRayPolyhedronTest
: public AllocatorsFixture
{
protected:
void SetUp() override
{
// base of cube
m_vertices[0] = Vector3(0.0f, 0.0f, 0.0f);
m_vertices[1] = Vector3(10.0f, 0.0f, 0.0f);
m_vertices[2] = Vector3(10.0f, 10.0f, 0.0f);
m_vertices[3] = Vector3(0.0f, 10.0f, 0.0f);
// setup planes
for (size_t i = 0; i < 4; ++i)
{
const Vector3 start = m_vertices[i];
const Vector3 end = m_vertices[(i + 1) % 4];
const Vector3 top = start + Vector3::CreateAxisZ();
const Vector3 normal = (end - start).Cross(top - start).GetNormalizedSafe();
m_planes[i] = Plane::CreateFromNormalAndPoint(normal, start);
}
const Vector3 normalTop =
(m_vertices[2] - m_vertices[0]).Cross(m_vertices[0] - m_vertices[1]).GetNormalizedSafe();
const Vector3 normalBottom = -normalTop;
const float height = 10.0f;
m_planes[4] = Plane::CreateFromNormalAndPoint(normalTop, m_vertices[0] + Vector3::CreateAxisZ(height));
m_planes[5] = Plane::CreateFromNormalAndPoint(normalBottom, m_vertices[0]);
}
void TearDown() override
{
}
Vector3 m_vertices[4];
Plane m_planes[6];
};
TEST_F(MATH_IntersectRayPolyhedronTest, RayParallelHit)
{
const Vector3 src = Vector3(0.0f, -1.0f, 1.0f);
const Vector3 dir = Vector3(0.0f, 1.0f, 0.0f);
const Vector3 end = (src + dir * 100.0f) - src;
float f, l;
int firstPlane, lastPlane;
const int intersections = Intersect::IntersectSegmentPolyhedron(src, end, m_planes, 6, f, l, firstPlane, lastPlane);
EXPECT_EQ(intersections, 1);
}
TEST_F(MATH_IntersectRayPolyhedronTest, RayAboveMiss)
{
const Vector3 src = Vector3(5.0f, 11.0f, 11.0f);
const Vector3 dir = Vector3(0.0f, -1.0f, 0.0f);
const Vector3 end = (src + dir * 100.0f) - src;
float f, l;
int firstPlane, lastPlane;
const int intersections = Intersect::IntersectSegmentPolyhedron(src, end, m_planes, 6, f, l, firstPlane, lastPlane);
EXPECT_EQ(intersections, 0);
}
TEST_F(MATH_IntersectRayPolyhedronTest, RayDiagonalDownHit)
{
const Vector3 src = Vector3(5.0f, -1.0f, 11.0f);
const Vector3 end = Vector3(5.0f, 11.0f, -11.0f);
float f, l;
int firstPlane, lastPlane;
const int intersections = Intersect::IntersectSegmentPolyhedron(src, end, m_planes, 6, f, l, firstPlane, lastPlane);
EXPECT_EQ(intersections, 1);
}
TEST_F(MATH_IntersectRayPolyhedronTest, RayDiagonalAcrossHit)
{
const Vector3 src = Vector3(-5.0f, -5.0f, 5.0f);
const Vector3 end = Vector3(15.0f, 15.0f, 5.0f);
float f, l;
int firstPlane, lastPlane;
const int intersections = Intersect::IntersectSegmentPolyhedron(src, end, m_planes, 6, f, l, firstPlane, lastPlane);
EXPECT_EQ(intersections, 1);
}
TEST_F(MATH_IntersectRayPolyhedronTest, RayDiagonalAcrossMiss)
{
const Vector3 src = Vector3(-5.0f, -15.0f, 5.0f);
const Vector3 end = Vector3(15.0f, 5.0f, 5.0f);
float f, l;
int firstPlane, lastPlane;
const int intersections = Intersect::IntersectSegmentPolyhedron(src, end, m_planes, 6, f, l, firstPlane, lastPlane);
EXPECT_EQ(intersections, 0);
}
TEST_F(MATH_IntersectRayPolyhedronTest, RayStartInside)
{
const Vector3 src = Vector3(5.0f, 5.0f, 5.0f);
const Vector3 end = Vector3(5.0f, 5.0f, 5.0f);
float f, l;
int firstPlane, lastPlane;
const int intersections = Intersect::IntersectSegmentPolyhedron(src, end, m_planes, 6, f, l, firstPlane, lastPlane);
EXPECT_EQ(intersections, 0);
}
}
@@ -0,0 +1,117 @@
/*
* 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/UnitTest/TestTypes.h>
#include <AzCore/Math/MathIntrinsics.h>
namespace UnitTest
{
TEST(MathIntrinsics, CountLeadingZeros)
{
// Split up the binary literal in by every 4 bits
{
constexpr uint32_t leadingZero32 = 0b1000'0000'0000'0010'0000'0000'0000'0010;
EXPECT_EQ(0, az_clz_u32(leadingZero32));
EXPECT_EQ(32, az_clz_u64(leadingZero32));
}
{
constexpr uint64_t leadingZero64 = 0b0000'0100'1111'1011'0001'1000'0010'0110'1000'0000'0000'0010'0000'0000'0000'0010ULL;
EXPECT_EQ(5, az_clz_u64(leadingZero64));
}
#if defined(AZ_COMPILER_MSVC)
{
// All Zero case
// When using the clang compiler, this macro calls __builtin_clz,
// whose return value is undefined when the input is 0
constexpr uint32_t leadingZero32 = 0b0000'0000'0000'0000'0000'0000'0000'0000;
EXPECT_EQ(32, az_clz_u32(leadingZero32));
EXPECT_EQ(64, az_clz_u64(leadingZero32));
}
#endif
{
// All One case
constexpr uint32_t leadingZero32 = 0b1111'1111'1111'1111'1111'1111'1111'1111;
EXPECT_EQ(0, az_clz_u32(leadingZero32));
EXPECT_EQ(32, az_clz_u64(leadingZero32));
}
}
TEST(MathIntrinsics, CountTrailingZeros)
{
// Split up the binary literal in by every 4 bits
{
constexpr uint32_t trailingZero32 = 0b1000'0000'0000'0010'0000'0000'0010'0000;
EXPECT_EQ(5, az_ctz_u32(trailingZero32));
EXPECT_EQ(5, az_ctz_u64(trailingZero32));
}
{
constexpr uint64_t trailingZero64 = 0b0000'0100'1111'1011'0001'1000'0010'0110'1000'0000'0000'0010'0000'0000'0000'0010ULL;
EXPECT_EQ(1, az_ctz_u64(trailingZero64));
}
#if defined(AZ_COMPILER_MSVC)
{
// All Zero case
// When using the clang compiler, this macro calls __builtin_ctz,
// whose return value is undefined when the input is 0
constexpr uint32_t trailingZero32 = 0b0000'0000'0000'0000'0000'0000'0000'0000;
EXPECT_EQ(32, az_ctz_u32(trailingZero32));
EXPECT_EQ(64, az_ctz_u64(trailingZero32));
}
#endif
{
// All One case
constexpr uint32_t trailingZero32 = 0b1111'1111'1111'1111'1111'1111'1111'1111;
EXPECT_EQ(0, az_ctz_u32(trailingZero32));
EXPECT_EQ(0, az_ctz_u64(trailingZero32));
}
}
TEST(MathIntrinsics, CountOneBits)
{
// Split up the binary literal in by every 4 bits
{
constexpr uint32_t oneBits32 = 0b1010'1010'1011'1010'0010'0000'1010'0001;
EXPECT_EQ(13, az_popcnt_u32(oneBits32));
EXPECT_EQ(13, az_popcnt_u64(oneBits32));
}
{
constexpr uint64_t oneBits64 = 0b0000'0100'1111'1011'0001'1000'0010'0110'1000'0000'1111'0010'0001'1000'0000'0010ULL;
EXPECT_EQ(22, az_popcnt_u64(oneBits64));
}
{
// All Zero case
constexpr uint32_t oneBits32 = 0b0000'0000'0000'0000'0000'0000'0000'0000;
EXPECT_EQ(0, az_popcnt_u32(oneBits32));
EXPECT_EQ(0, az_popcnt_u64(oneBits32));
}
{
// All One case
constexpr uint32_t oneBits32 = 0b1111'1111'1111'1111'1111'1111'1111'1111;
EXPECT_EQ(32, az_popcnt_u32(oneBits32));
EXPECT_EQ(32, az_popcnt_u64(oneBits32));
}
{
// Bitwise-not cases(~0)
EXPECT_EQ(32, az_popcnt_u32(uint32_t(~0LL)));
EXPECT_EQ(31, az_popcnt_u32(uint32_t(~1LL)));
EXPECT_EQ(64, az_popcnt_u64(uint64_t(~0LL)));
EXPECT_EQ(63, az_popcnt_u64(uint64_t(~1LL)));
}
}
}
@@ -0,0 +1,93 @@
/*
* 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/Math/MathUtils.h>
#include <AzCore/Math/Matrix3x3.h>
#include <AzCore/Math/Matrix3x4.h>
#include <AzCore/Math/Quaternion.h>
#include <AzCore/Math/Transform.h>
#include <AzCore/Math/Vector3.h>
namespace MathTestData
{
static const AZ::Vector3 Vector3s[] = {
AZ::Vector3::CreateZero(),
AZ::Vector3::CreateOne(),
AZ::Vector3(-2.5f, -1.8f, 9.3f),
AZ::Vector3(1000.0f, 1e-3f, 0.0f)
};
static const float Angles[] = { 0.0f, 1.0f, AZ::Constants::Pi, 100.0f, -0.5f };
static const AZ::Matrix3x3 Matrix3x3s[] = {
AZ::Matrix3x3::CreateIdentity(),
AZ::Matrix3x3::CreateRotationZ(0.3f),
AZ::Matrix3x3::CreateFromQuaternion(AZ::Quaternion(-0.46f, 0.26f, -0.22f, 0.82f)),
AZ::Matrix3x3::CreateScale(AZ::Vector3(0.7f, 1.3f, 0.9f))
};
using AxisPair = AZStd::pair<AZ::Constants::Axis, AZ::Vector3>;
static const AxisPair Axes[] = {
{ AZ::Constants::Axis::XPositive, AZ::Vector3::CreateAxisX(1.0f) },
{ AZ::Constants::Axis::XNegative, AZ::Vector3::CreateAxisX(-1.0f) },
{ AZ::Constants::Axis::YPositive, AZ::Vector3::CreateAxisY(1.0f) },
{ AZ::Constants::Axis::YNegative, AZ::Vector3::CreateAxisY(-1.0f) },
{ AZ::Constants::Axis::ZPositive, AZ::Vector3::CreateAxisZ(1.0f) },
{ AZ::Constants::Axis::ZNegative, AZ::Vector3::CreateAxisZ(-1.0f) }
};
static const AZ::Matrix3x4 NonOrthogonalMatrix3x4s[] = {
AZ::Matrix3x4::CreateScale(AZ::Vector3(2.4f, 0.3f, 1.7f)),
AZ::Matrix3x4::CreateRotationX(2.2f) * AZ::Matrix3x4::CreateDiagonal(AZ::Vector3(0.2f, 0.8f, 1.4f))
};
static const AZ::Matrix3x4 OrthogonalMatrix3x4s[] = {
AZ::Matrix3x4::CreateIdentity(),
AZ::Matrix3x4::CreateRotationX(-0.6f),
AZ::Matrix3x4::CreateFromQuaternion(AZ::Quaternion(0.24f, -0.08f, -0.48f, 0.84f)),
AZ::Matrix3x4::CreateTranslation(AZ::Vector3(7.9f, 2.4f, -4.6f)),
AZ::Matrix3x4::CreateFromQuaternionAndTranslation(AZ::Quaternion(0.12f, -0.24f, -0.72f, 0.64f), AZ::Vector3(2.3f, -5.2f, 0.7f))
};
static const AZ::Transform NonOrthogonalTransforms[] = {
AZ::Transform::CreateScale(AZ::Vector3(2.4f, 0.3f, 1.7f)),
AZ::Transform::CreateRotationX(2.2f) * AZ::Transform::CreateScale(AZ::Vector3(0.2f, 0.8f, 1.4f))
};
static const AZ::Transform OrthogonalTransforms[] = {
AZ::Transform::CreateIdentity(),
AZ::Transform::CreateRotationX(-0.6f),
AZ::Transform::CreateFromQuaternion(AZ::Quaternion(0.24f, -0.08f, -0.48f, 0.84f)),
AZ::Transform::CreateTranslation(AZ::Vector3(7.9f, 2.4f, -4.6f)),
AZ::Transform::CreateFromQuaternionAndTranslation(AZ::Quaternion(0.12f, -0.24f, -0.72f, 0.64f), AZ::Vector3(2.3f, -5.2f, 0.7f))
};
static const AZ::Vector3 EulerAnglesDegrees[] = {
AZ::Vector3::CreateZero(),
AZ::Vector3(70.0f, -32.0f, 119.0f),
AZ::Vector3(1284.0f, -2734.0f, -1929.0f)
};
static const AZ::Vector3 EulerAnglesRadians[] = {
AZ::Vector3::CreateZero(),
AZ::Vector3(0.8f, -0.4f, 1.7f),
AZ::Vector3(10.2f, 9.7f, -6.8f)
};
static const AZ::Quaternion UnitQuaternions[] = {
AZ::Quaternion::CreateIdentity(),
AZ::Quaternion(0.58f, -0.22f, -0.26f, 0.74f),
AZ::Quaternion::CreateRotationX(0.2f)
};
} // namespace MathTestData
@@ -0,0 +1,65 @@
/*
* 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/MathUtils.h>
#include <AzCore/UnitTest/TestTypes.h>
using namespace AZ;
namespace UnitTest
{
TEST(MATH_Lerp, Test)
{
// Float
EXPECT_EQ(2.5f, AZ::Lerp(2.0f, 4.0f, 0.25f));
EXPECT_EQ(6.0f, AZ::Lerp(2.0f, 4.0f, 2.0f));
// Double
EXPECT_EQ(3.5, AZ::Lerp(2.0, 4.0, 0.75));
EXPECT_EQ(0.0, AZ::Lerp(2.0, 4.0, -1.0));
}
TEST(MATH_LerpInverse, Test)
{
// Float
EXPECT_NEAR(0.25f, AZ::LerpInverse(2.0f, 4.0f, 2.5f), 0.0001f);
EXPECT_NEAR(2.0f, AZ::LerpInverse(2.0f, 4.0f, 6.0f), 0.0001f);
// Double
EXPECT_NEAR(0.75, AZ::LerpInverse(2.0, 4.0, 3.5), 0.0001);
EXPECT_NEAR(-1.0, AZ::LerpInverse(2.0, 4.0, 0.0), 0.0001);
// min/max need to be substantially different to return a useful t value
// Float
const float epsilonF = std::numeric_limits<float>::epsilon();
const float doesntMatterF = std::numeric_limits<float>::signaling_NaN();
float lowerF = 2.3f, upperF = 2.3f;
EXPECT_EQ(0.0f, AZ::LerpInverse(lowerF, upperF, doesntMatterF));
EXPECT_EQ(0.0f, AZ::LerpInverse(0.0f, 0.5f * epsilonF, doesntMatterF));
EXPECT_EQ(0.0f, AZ::LerpInverse(0.0f, 5.0f * epsilonF, 0.0f));
EXPECT_NEAR(0.4f, AZ::LerpInverse(0.0f, 5.0f * epsilonF, 2.0f * epsilonF), epsilonF);
EXPECT_NEAR(0.6f, AZ::LerpInverse(1.0f, 1.0f + 5.0f * epsilonF, 1.0f + 3.0f * epsilonF), epsilonF);
EXPECT_NEAR(1.0f, AZ::LerpInverse(1.0f, 1.0f + 5.0f * epsilonF, 1.0f + 5.0f * epsilonF), epsilonF);
// Double
const double epsilonD = std::numeric_limits<double>::epsilon();
const double doesntMatterD = std::numeric_limits<double>::signaling_NaN();
double lowerD = 2.3, upperD = 2.3;
EXPECT_EQ(0.0, AZ::LerpInverse(lowerD, upperD, doesntMatterD));
EXPECT_EQ(0.0, AZ::LerpInverse(0.0, 0.5 * epsilonD, doesntMatterD));
EXPECT_EQ(0.0, AZ::LerpInverse(0.0, 5.0 * epsilonD, 0.0));
EXPECT_NEAR(0.4, AZ::LerpInverse(0.0, 5.0 * epsilonD, 2.0 * epsilonD), epsilonD);
EXPECT_NEAR(0.6, AZ::LerpInverse(1.0, 1.0 + 5.0 * epsilonD, 1.0 + 3.0 * epsilonD), epsilonD);
EXPECT_NEAR(1.0, AZ::LerpInverse(1.0, 1.0 + 5.0 * epsilonD, 1.0 + 5.0 * epsilonD), epsilonD);
}
}
@@ -0,0 +1,661 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyrightand 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.
*
*/
#if defined(HAVE_BENCHMARK)
#include <AzCore/Math/Matrix3x3.h>
#include <AzCore/Math/Matrix3x4.h>
#include <AzCore/Math/Matrix4x4.h>
#include <AzCore/Math/Transform.h>
#include <AzCore/Math/Quaternion.h>
#include <AzCore/UnitTest/TestTypes.h>
#include <random>
namespace Benchmark
{
const static float s_mat3x3testArray[] = { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f };
class BM_MathMatrix3x3
: public benchmark::Fixture
{
public:
void SetUp([[maybe_unused]] const ::benchmark::State& state) override
{
m_testDataArray.resize(1000);
const unsigned int seed = 1;
std::mt19937_64 rng(seed);
std::uniform_real_distribution<float> unif;
std::generate(m_testDataArray.begin(), m_testDataArray.end(), [&unif, &rng]()
{
TestData testData;
testData.q1 = AZ::Quaternion(unif(rng), unif(rng), unif(rng), unif(rng)).GetNormalized();
testData.m1 = AZ::Matrix3x3::CreateFromQuaternion(testData.q1);
testData.m2 = AZ::Matrix3x3::CreateFromQuaternion(testData.q1);
testData.m3 = AZ::Matrix4x4::CreateFromQuaternionAndTranslation(testData.q1, AZ::Vector3(unif(rng), unif(rng), unif(rng)));
testData.t1 = AZ::Transform::CreateFromQuaternionAndTranslation(testData.q1, AZ::Vector3(unif(rng), unif(rng), unif(rng)));
testData.v1 = AZ::Vector3(unif(rng), unif(rng), unif(rng));
return testData;
});
}
struct TestData
{
AZ::Matrix3x3 m1;
AZ::Matrix3x3 m2;
AZ::Matrix4x4 m3;
AZ::Transform t1;
AZ::Quaternion q1;
AZ::Vector3 v1;
};
std::vector<TestData> m_testDataArray;
};
BENCHMARK_F(BM_MathMatrix3x3, CreateIdentity)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
AZ::Matrix3x3 result = AZ::Matrix3x3::CreateIdentity();
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathMatrix3x3, CreateZero)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
AZ::Matrix3x3 result = AZ::Matrix3x3::CreateZero();
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathMatrix3x3, GetRowX3)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
AZ::Vector3 result = testData.m1.GetRow(0);
benchmark::DoNotOptimize(result);
result = testData.m1.GetRow(1);
benchmark::DoNotOptimize(result);
result = testData.m1.GetRow(2);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathMatrix3x3, GetColumnX3)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
AZ::Vector3 result = testData.m1.GetColumn(0);
benchmark::DoNotOptimize(result);
result = testData.m1.GetColumn(1);
benchmark::DoNotOptimize(result);
result = testData.m1.GetColumn(2);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathMatrix3x3, CreateFromValue)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
AZ::Matrix3x3 result = AZ::Matrix3x3::CreateFromValue(testData.v1.GetX());
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathMatrix3x3, CreateFromRowMajorFloat9)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
AZ::Matrix3x3 result = AZ::Matrix3x3::CreateFromRowMajorFloat9(s_mat3x3testArray);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathMatrix3x3, CreateFromColumnMajorFloat9)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
AZ::Matrix3x3 result = AZ::Matrix3x3::CreateFromColumnMajorFloat9(s_mat3x3testArray);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathMatrix3x3, StoreToRowMajorFloat9)(benchmark::State& state)
{
float storeValues[9];
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
testData.m1.StoreToRowMajorFloat9(storeValues);
benchmark::DoNotOptimize(storeValues);
}
}
}
BENCHMARK_F(BM_MathMatrix3x3, StoreToColumnMajorFloat9)(benchmark::State& state)
{
float storeValues[9];
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
testData.m1.StoreToColumnMajorFloat9(storeValues);
benchmark::DoNotOptimize(storeValues);
}
}
}
BENCHMARK_F(BM_MathMatrix3x3, CreateRotationX)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
AZ::Matrix3x3 result = AZ::Matrix3x3::CreateRotationX(testData.v1.GetX());
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathMatrix3x3, CreateRotationY)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
AZ::Matrix3x3 result = AZ::Matrix3x3::CreateRotationY(testData.v1.GetY());
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathMatrix3x3, CreateRotationZ)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
AZ::Matrix3x3 result = AZ::Matrix3x3::CreateRotationZ(testData.v1.GetZ());
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathMatrix3x3, CreateFromTransform)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
AZ::Matrix3x3 result = AZ::Matrix3x3::CreateFromTransform(testData.t1);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathMatrix3x3, CreateFromMatrix4x4)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
AZ::Matrix3x3 result = AZ::Matrix3x3::CreateFromMatrix4x4(testData.m3);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathMatrix3x3, CreateFromQuaternion)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
AZ::Matrix3x3 result = AZ::Matrix3x3::CreateFromQuaternion(testData.q1);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathMatrix3x3, CreateScale)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
AZ::Matrix3x3 result = AZ::Matrix3x3::CreateScale(testData.v1);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathMatrix3x3, CreateDiagonal)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
AZ::Matrix3x3 result = AZ::Matrix3x3::CreateDiagonal(testData.v1);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathMatrix3x3, CreateCrossProduct)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
AZ::Matrix3x3 result = AZ::Matrix3x3::CreateCrossProduct(testData.v1);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathMatrix3x3, GetElement)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
float result = testData.m1.GetElement(1, 2);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathMatrix3x3, SetElement)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
AZ::Matrix3x3 testMatrix = testData.m2;
testMatrix.SetElement(1, 2, -5.0f);
benchmark::DoNotOptimize(testMatrix);
}
}
}
BENCHMARK_F(BM_MathMatrix3x3, SetRowX3)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
AZ::Matrix3x3 testMatrix = testData.m2;
testMatrix.SetRow(0, testData.v1);
testMatrix.SetRow(1, testData.v1);
testMatrix.SetRow(2, testData.v1);
benchmark::DoNotOptimize(testMatrix);
}
}
}
BENCHMARK_F(BM_MathMatrix3x3, SetColumnX3)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
AZ::Matrix3x3 testMatrix = testData.m2;
testMatrix.SetColumn(0, testData.v1);
testMatrix.SetColumn(1, testData.v1);
testMatrix.SetColumn(2, testData.v1);
benchmark::DoNotOptimize(testMatrix);
}
}
}
BENCHMARK_F(BM_MathMatrix3x3, GetBasisX)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
AZ::Vector3 result = testData.m1.GetBasisX();
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathMatrix3x3, GetBasisY)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
AZ::Vector3 result = testData.m1.GetBasisY();
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathMatrix3x3, GetBasisZ)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
AZ::Vector3 result = testData.m1.GetBasisZ();
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathMatrix3x3, OperatorAssign)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
AZ::Matrix3x3 result = testData.m1;
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathMatrix3x3, OperatorMultiply)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
AZ::Matrix3x3 result = testData.m1 * testData.m2;
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathMatrix3x3, TransposedMultiply)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
AZ::Matrix3x3 result = testData.m1.TransposedMultiply(testData.m2);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathMatrix3x3, MultiplyVector)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
AZ::Vector3 result = testData.m1 * testData.v1;
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathMatrix3x3, OperatorSum)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
AZ::Matrix3x3 result = testData.m1 + testData.m2;
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathMatrix3x3, OperatorDifference)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
AZ::Matrix3x3 result = testData.m1 - testData.m2;
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathMatrix3x3, OperatorMultiplyScalar)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
AZ::Matrix3x3 result = testData.m1 * testData.v1.GetX();
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathMatrix3x3, OperatorDivideScalar)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
AZ::Matrix3x3 result = testData.m1 / testData.v1.GetY();
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathMatrix3x3, Transpose)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
testData.m1.Transpose();
}
}
}
BENCHMARK_F(BM_MathMatrix3x3, GetTranspose)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
AZ::Matrix3x3 result = testData.m1.GetTranspose();
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathMatrix3x3, RetrieveScale)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
AZ::Vector3 result = testData.m1.RetrieveScale();
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathMatrix3x3, ExtractScale)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
AZ::Vector3 result = testData.m1.ExtractScale();
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathMatrix3x3, MultiplyByScaleX2)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
testData.m1.MultiplyByScale(testData.v1);
testData.m1.MultiplyByScale(testData.v1.GetReciprocal());
}
}
}
BENCHMARK_F(BM_MathMatrix3x3, GetPolarDecomposition)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
AZ::Matrix3x3 result = testData.m1.GetPolarDecomposition();
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathMatrix3x3, GetPolarDecomposition2)(benchmark::State& state)
{
AZ::Matrix3x3 orthogonalOut;
AZ::Matrix3x3 symmetricOut;
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
testData.m1.GetPolarDecomposition(&orthogonalOut, &symmetricOut);
}
}
}
BENCHMARK_F(BM_MathMatrix3x3, GetInverseFast)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
AZ::Matrix3x3 result = testData.m1.GetInverseFast();
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathMatrix3x3, GetInverseFull)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
AZ::Matrix3x3 result = testData.m1.GetInverseFull();
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathMatrix3x3, Orthogonalize)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
testData.m1.Orthogonalize();
}
}
}
BENCHMARK_F(BM_MathMatrix3x3, GetOrthogonalized)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
AZ::Matrix3x3 result = testData.m1.GetOrthogonalized();
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathMatrix3x3, IsOrthogonal)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
bool result = testData.m1.IsOrthogonal();
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathMatrix3x3, GetDiagonal)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
AZ::Vector3 result = testData.m1.GetDiagonal();
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathMatrix3x3, GetDeterminant)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
float result = testData.m1.GetDeterminant();
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathMatrix3x3, GetAdjugate)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
AZ::Matrix3x3 result = testData.m1.GetAdjugate();
benchmark::DoNotOptimize(result);
}
}
}
}
#endif
@@ -0,0 +1,518 @@
/*
* 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/Matrix3x3.h>
#include <AzCore/Math/Transform.h>
#include <AzCore/Math/Quaternion.h>
#include <AzCore/UnitTest/TestTypes.h>
using namespace AZ;
namespace UnitTest
{
static constexpr int32_t testIndices3x3[] = { 0, 1, 2, 3 };
float testFloats[] = { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f };
float testFloatsMtx[9];
TEST(MATH_Matrix3x3, TestCreateIdentity)
{
Matrix3x3 m1 = Matrix3x3::CreateIdentity();
AZ_TEST_ASSERT(m1.GetRow(0) == Vector3(1.0f, 0.0f, 0.0f));
AZ_TEST_ASSERT(m1.GetRow(1) == Vector3(0.0f, 1.0f, 0.0f));
AZ_TEST_ASSERT(m1.GetRow(2) == Vector3(0.0f, 0.0f, 1.0f));
}
TEST(MATH_Matrix3x3, TestCreateZero)
{
Matrix3x3 m1 = Matrix3x3::CreateZero();
AZ_TEST_ASSERT(m1.GetRow(0) == Vector3(0.0f));
AZ_TEST_ASSERT(m1.GetRow(1) == Vector3(0.0f));
AZ_TEST_ASSERT(m1.GetRow(2) == Vector3(0.0f));
}
TEST(MATH_Matrix3x3, TestCreateFromValue)
{
Matrix3x3 m1 = Matrix3x3::CreateFromValue(2.0f);
AZ_TEST_ASSERT(m1.GetRow(0) == Vector3(2.0f));
AZ_TEST_ASSERT(m1.GetRow(1) == Vector3(2.0f));
AZ_TEST_ASSERT(m1.GetRow(2) == Vector3(2.0f));
}
TEST(MATH_Matrix3x3, TestCreateFromRowMajorFloat9)
{
Matrix3x3 m1 = Matrix3x3::CreateFromRowMajorFloat9(testFloats);
AZ_TEST_ASSERT(m1.GetRow(0) == Vector3(1.0f, 2.0f, 3.0f));
AZ_TEST_ASSERT(m1.GetRow(1) == Vector3(4.0f, 5.0f, 6.0f));
AZ_TEST_ASSERT(m1.GetRow(2) == Vector3(7.0f, 8.0f, 9.0f));
m1.StoreToRowMajorFloat9(testFloatsMtx);
AZ_TEST_ASSERT(memcmp(testFloatsMtx, testFloats, sizeof(testFloatsMtx)) == 0);
}
TEST(MATH_Matrix3x3, TestCreateFromColumnMajorFloat9)
{
Matrix3x3 m1 = Matrix3x3::CreateFromColumnMajorFloat9(testFloats);
AZ_TEST_ASSERT(m1.GetRow(0) == Vector3(1.0f, 4.0f, 7.0f));
AZ_TEST_ASSERT(m1.GetRow(1) == Vector3(2.0f, 5.0f, 8.0f));
AZ_TEST_ASSERT(m1.GetRow(2) == Vector3(3.0f, 6.0f, 9.0f));
m1.StoreToColumnMajorFloat9(testFloatsMtx);
AZ_TEST_ASSERT(memcmp(testFloatsMtx, testFloats, sizeof(testFloatsMtx)) == 0);
}
TEST(MATH_Matrix3x3, TestCreateRotationX)
{
Matrix3x3 m1 = Matrix3x3::CreateRotationX(DegToRad(30.0f));
AZ_TEST_ASSERT(m1.GetRow(0).IsClose(Vector3(1.0f, 0.0f, 0.0f)));
AZ_TEST_ASSERT(m1.GetRow(1).IsClose(Vector3(0.0f, 0.866f, -0.5f)));
AZ_TEST_ASSERT(m1.GetRow(2).IsClose(Vector3(0.0f, 0.5f, 0.866f)));
}
TEST(MATH_Matrix3x3, TestCreateRotationY)
{
Matrix3x3 m1 = Matrix3x3::CreateRotationY(DegToRad(30.0f));
AZ_TEST_ASSERT(m1.GetRow(0).IsClose(Vector3(0.866f, 0.0f, 0.5f)));
AZ_TEST_ASSERT(m1.GetRow(1).IsClose(Vector3(0.0f, 1.0f, 0.0f)));
AZ_TEST_ASSERT(m1.GetRow(2).IsClose(Vector3(-0.5f, 0.0f, 0.866f)));
}
TEST(MATH_Matrix3x3, TestCreateRotationZ)
{
Matrix3x3 m1 = Matrix3x3::CreateRotationZ(DegToRad(30.0f));
AZ_TEST_ASSERT(m1.GetRow(0).IsClose(Vector3(0.866f, -0.5f, 0.0f)));
AZ_TEST_ASSERT(m1.GetRow(1).IsClose(Vector3(0.5f, 0.866f, 0.0f)));
AZ_TEST_ASSERT(m1.GetRow(2).IsClose(Vector3(0.0f, 0.0f, 1.0f)));
}
TEST(MATH_Matrix3x3, TestCreateFromTransform)
{
Matrix3x3 m1 = Matrix3x3::CreateFromTransform(Transform::CreateRotationX(DegToRad(30.0f)));
AZ_TEST_ASSERT(m1.GetRow(0).IsClose(Vector3(1.0f, 0.0f, 0.0f)));
AZ_TEST_ASSERT(m1.GetRow(1).IsClose(Vector3(0.0f, 0.866f, -0.5f)));
AZ_TEST_ASSERT(m1.GetRow(2).IsClose(Vector3(0.0f, 0.5f, 0.866f)));
}
TEST(MATH_Matrix3x3, TestCreateFromMatrix4x4)
{
Matrix3x3 m1 = Matrix3x3::CreateFromMatrix4x4(Matrix4x4::CreateRotationX(DegToRad(30.0f)));
AZ_TEST_ASSERT(m1.GetRow(0).IsClose(Vector3(1.0f, 0.0f, 0.0f)));
AZ_TEST_ASSERT(m1.GetRow(1).IsClose(Vector3(0.0f, 0.866f, -0.5f)));
AZ_TEST_ASSERT(m1.GetRow(2).IsClose(Vector3(0.0f, 0.5f, 0.866f)));
}
TEST(MATH_Matrix3x3, TestCreateFromQuaternion)
{
Matrix3x3 m1 = Matrix3x3::CreateFromQuaternion(AZ::Quaternion::CreateRotationX(DegToRad(30.0f)));
AZ_TEST_ASSERT(m1.GetRow(0).IsClose(Vector3(1.0f, 0.0f, 0.0f)));
AZ_TEST_ASSERT(m1.GetRow(1).IsClose(Vector3(0.0f, 0.866f, -0.5f)));
AZ_TEST_ASSERT(m1.GetRow(2).IsClose(Vector3(0.0f, 0.5f, 0.866f)));
}
TEST(MATH_Matrix3x3, TestCreateScale)
{
Matrix3x3 m1 = Matrix3x3::CreateScale(Vector3(1.0f, 2.0f, 3.0f));
AZ_TEST_ASSERT(m1.GetRow(0) == Vector3(1.0f, 0.0f, 0.0f));
AZ_TEST_ASSERT(m1.GetRow(1) == Vector3(0.0f, 2.0f, 0.0f));
AZ_TEST_ASSERT(m1.GetRow(2) == Vector3(0.0f, 0.0f, 3.0f));
}
TEST(MATH_Matrix3x3, TestCreateDiagonal)
{
Matrix3x3 m1 = Matrix3x3::CreateDiagonal(Vector3(2.0f, 3.0f, 4.0f));
AZ_TEST_ASSERT(m1.GetRow(0) == Vector3(2.0f, 0.0f, 0.0f));
AZ_TEST_ASSERT(m1.GetRow(1) == Vector3(0.0f, 3.0f, 0.0f));
AZ_TEST_ASSERT(m1.GetRow(2) == Vector3(0.0f, 0.0f, 4.0f));
}
TEST(MATH_Matrix3x3, TestCreateCrossProduct)
{
Matrix3x3 m1 = Matrix3x3::CreateCrossProduct(Vector3(1.0f, 2.0f, 3.0f));
AZ_TEST_ASSERT(m1.GetRow(0) == Vector3(0.0f, -3.0f, 2.0f));
AZ_TEST_ASSERT(m1.GetRow(1) == Vector3(3.0f, 0.0f, -1.0f));
AZ_TEST_ASSERT(m1.GetRow(2) == Vector3(-2.0f, 1.0f, 0.0f));
}
TEST(MATH_Matrix3x3, TestElementAccess)
{
Matrix3x3 m1 = Matrix3x3::CreateRotationX(DegToRad(30.0f));
AZ_TEST_ASSERT_FLOAT_CLOSE(m1.GetElement(1, 2), -0.5f);
AZ_TEST_ASSERT_FLOAT_CLOSE(m1.GetElement(2, 2), 0.866f);
m1.SetElement(2, 1, 5.0f);
AZ_TEST_ASSERT(m1.GetElement(2, 1) == 5.0f);
AZ_TEST_ASSERT_FLOAT_CLOSE(m1(1, 2), -0.5f);
AZ_TEST_ASSERT_FLOAT_CLOSE(m1(2, 2), 0.866f);
m1.SetElement(2, 1, 15.0f);
AZ_TEST_ASSERT(m1(2, 1) == 15.0f);
}
TEST(MATH_Matrix3x3, TestRowAccess)
{
Matrix3x3 m1 = Matrix3x3::CreateRotationX(DegToRad(30.0f));
AZ_TEST_ASSERT(m1.GetRow(2).IsClose(Vector3(0.0f, 0.5f, 0.866f)));
m1.SetRow(0, 1.0f, 2.0f, 3.0f);
AZ_TEST_ASSERT(m1.GetRow(0).IsClose(Vector3(1.0f, 2.0f, 3.0f)));
m1.SetRow(1, Vector3(4.0f, 5.0f, 6.0f));
AZ_TEST_ASSERT(m1.GetRow(1).IsClose(Vector3(4.0f, 5.0f, 6.0f)));
m1.SetRow(2, Vector3(7.0f, 8.0f, 9.0f));
AZ_TEST_ASSERT(m1.GetRow(2).IsClose(Vector3(7.0f, 8.0f, 9.0f)));
// test GetRow with non-constant, we have different implementations for constants and variables
AZ_TEST_ASSERT(m1.GetRow(testIndices3x3[0]).IsClose(Vector3(1.0f, 2.0f, 3.0f)));
AZ_TEST_ASSERT(m1.GetRow(testIndices3x3[1]).IsClose(Vector3(4.0f, 5.0f, 6.0f)));
AZ_TEST_ASSERT(m1.GetRow(testIndices3x3[2]).IsClose(Vector3(7.0f, 8.0f, 9.0f)));
Vector3 row0, row1, row2;
m1.GetRows(&row0, &row1, &row2);
AZ_TEST_ASSERT(row0.IsClose(Vector3(1.0f, 2.0f, 3.0f)));
AZ_TEST_ASSERT(row1.IsClose(Vector3(4.0f, 5.0f, 6.0f)));
AZ_TEST_ASSERT(row2.IsClose(Vector3(7.0f, 8.0f, 9.0f)));
m1.SetRows(Vector3(10.0f, 11.0f, 12.0f), Vector3(13.0f, 14.0f, 15.0f), Vector3(16.0f, 17.0f, 18.0f));
AZ_TEST_ASSERT(m1.GetRow(0).IsClose(Vector3(10.0f, 11.0f, 12.0f)));
AZ_TEST_ASSERT(m1.GetRow(1).IsClose(Vector3(13.0f, 14.0f, 15.0f)));
AZ_TEST_ASSERT(m1.GetRow(2).IsClose(Vector3(16.0f, 17.0f, 18.0f)));
}
TEST(MATH_Matrix3x3, TestColumnAccess)
{
Matrix3x3 m1 = Matrix3x3::CreateRotationX(DegToRad(30.0f));
AZ_TEST_ASSERT(m1.GetColumn(1).IsClose(Vector3(0.0f, 0.866f, 0.5f)));
m1.SetColumn(2, 1.0f, 2.0f, 3.0f);
AZ_TEST_ASSERT(m1.GetColumn(2).IsClose(Vector3(1.0f, 2.0f, 3.0f)));
AZ_TEST_ASSERT(m1.GetRow(0).IsClose(Vector3(1.0f, 0.0f, 1.0f))); // checking all components in case others get messed up with the shuffling
AZ_TEST_ASSERT(m1.GetRow(1).IsClose(Vector3(0.0f, 0.866f, 2.0f)));
AZ_TEST_ASSERT(m1.GetRow(2).IsClose(Vector3(0.0f, 0.5f, 3.0f)));
m1.SetColumn(0, Vector3(2.0f, 3.0f, 4.0f));
AZ_TEST_ASSERT(m1.GetColumn(0).IsClose(Vector3(2.0f, 3.0f, 4.0f)));
AZ_TEST_ASSERT(m1.GetRow(0).IsClose(Vector3(2.0f, 0.0f, 1.0f)));
AZ_TEST_ASSERT(m1.GetRow(1).IsClose(Vector3(3.0f, 0.866f, 2.0f)));
AZ_TEST_ASSERT(m1.GetRow(2).IsClose(Vector3(4.0f, 0.5f, 3.0f)));
// test GetColumn with non-constant, we have different implementations for constants and variables
AZ_TEST_ASSERT(m1.GetColumn(testIndices3x3[0]).IsClose(Vector3(2.0f, 3.0f, 4.0f)));
AZ_TEST_ASSERT(m1.GetColumn(testIndices3x3[1]).IsClose(Vector3(0.0f, 0.866f, 0.5f)));
AZ_TEST_ASSERT(m1.GetColumn(testIndices3x3[2]).IsClose(Vector3(1.0f, 2.0f, 3.0f)));
Vector3 col0, col1, col2;
m1.GetColumns(&col0, &col1, &col2);
AZ_TEST_ASSERT(col0.IsClose(Vector3(2.0f, 3.0f, 4.0f)));
AZ_TEST_ASSERT(col1.IsClose(Vector3(0.0f, 0.866f, 0.5f)));
AZ_TEST_ASSERT(col2.IsClose(Vector3(1.0f, 2.0f, 3.0f)));
m1.SetColumns(Vector3(10.0f, 11.0f, 12.0f), Vector3(13.0f, 14.0f, 15.0f), Vector3(16.0f, 17.0f, 18.0f));
AZ_TEST_ASSERT(m1.GetColumn(0).IsClose(Vector3(10.0f, 11.0f, 12.0f)));
AZ_TEST_ASSERT(m1.GetColumn(1).IsClose(Vector3(13.0f, 14.0f, 15.0f)));
AZ_TEST_ASSERT(m1.GetColumn(2).IsClose(Vector3(16.0f, 17.0f, 18.0f)));
}
TEST(MATH_Matrix3x3, TestBasisAccess)
{
Matrix3x3 m1 = Matrix3x3::CreateRotationX(DegToRad(30.0f));
AZ_TEST_ASSERT(m1.GetBasisX().IsClose(Vector3(1.0f, 0.0f, 0.0f)));
AZ_TEST_ASSERT(m1.GetBasisY().IsClose(Vector3(0.0f, 0.866f, 0.5f)));
AZ_TEST_ASSERT(m1.GetBasisZ().IsClose(Vector3(0.0f, -0.5f, 0.866f)));
m1.SetBasisX(1.0f, 2.0f, 3.0f);
AZ_TEST_ASSERT(m1.GetBasisX().IsClose(Vector3(1.0f, 2.0f, 3.0f)));
m1.SetBasisY(4.0f, 5.0f, 6.0f);
AZ_TEST_ASSERT(m1.GetBasisY().IsClose(Vector3(4.0f, 5.0f, 6.0f)));
m1.SetBasisZ(7.0f, 8.0f, 9.0f);
AZ_TEST_ASSERT(m1.GetBasisZ().IsClose(Vector3(7.0f, 8.0f, 9.0f)));
m1.SetBasisX(Vector3(10.0f, 11.0f, 12.0f));
AZ_TEST_ASSERT(m1.GetBasisX().IsClose(Vector3(10.0f, 11.0f, 12.0f)));
m1.SetBasisY(Vector3(13.0f, 14.0f, 15.0f));
AZ_TEST_ASSERT(m1.GetBasisY().IsClose(Vector3(13.0f, 14.0f, 15.0f)));
m1.SetBasisZ(Vector3(16.0f, 17.0f, 18.0f));
AZ_TEST_ASSERT(m1.GetBasisZ().IsClose(Vector3(16.0f, 17.0f, 18.0f)));
Vector3 basis0, basis1, basis2;
m1.GetBasis(&basis0, &basis1, &basis2);
AZ_TEST_ASSERT(basis0.IsClose(Vector3(10.0f, 11.0f, 12.0f)));
AZ_TEST_ASSERT(basis1.IsClose(Vector3(13.0f, 14.0f, 15.0f)));
AZ_TEST_ASSERT(basis2.IsClose(Vector3(16.0f, 17.0f, 18.0f)));
m1.SetBasis(Vector3(1.0f, 2.0f, 3.0f), Vector3(4.0f, 5.0f, 6.0f), Vector3(7.0f, 8.0f, 9.0f));
AZ_TEST_ASSERT(m1.GetBasisX().IsClose(Vector3(1.0f, 2.0f, 3.0f)));
AZ_TEST_ASSERT(m1.GetBasisY().IsClose(Vector3(4.0f, 5.0f, 6.0f)));
AZ_TEST_ASSERT(m1.GetBasisZ().IsClose(Vector3(7.0f, 8.0f, 9.0f)));
}
TEST(MATH_Matrix3x3, TestMatrixMultiplication)
{
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);
Matrix3x3 m2;
m2.SetRow(0, 7.0f, 8.0f, 9.0f);
m2.SetRow(1, 10.0f, 11.0f, 12.0f);
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)));
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)));
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)));
}
TEST(MATH_Matrix3x3, TestVectorMultiplication)
{
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);
Matrix3x3 m2;
m2.SetRow(0, 7.0f, 8.0f, 9.0f);
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)));
Vector3 v1(1.0f, 2.0f, 3.0f);
AZ_TEST_ASSERT((v1 * m1).IsClose(Vector3(30.0f, 36.0f, 42.0f)));
v1 *= m1;
AZ_TEST_ASSERT(v1.IsClose(Vector3(30.0f, 36.0f, 42.0f)));
}
TEST(MATH_Matrix3x3, TestSum)
{
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);
Matrix3x3 m2;
m2.SetRow(0, 7.0f, 8.0f, 9.0f);
m2.SetRow(1, 10.0f, 11.0f, 12.0f);
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)));
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)));
}
TEST(MATH_Matrix3x3, TestDifference)
{
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);
Matrix3x3 m2;
m2.SetRow(0, 7.0f, 8.0f, 9.0f);
m2.SetRow(1, 10.0f, 11.0f, 12.0f);
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)));
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)));
}
TEST(MATH_Matrix3x3, TestScalarMultiplication)
{
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);
Matrix3x3 m2;
m2.SetRow(0, 7.0f, 8.0f, 9.0f);
m2.SetRow(1, 10.0f, 11.0f, 12.0f);
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)));
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)));
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)));
}
TEST(MATH_Matrix3x3, TestScalarDivision)
{
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);
Matrix3x3 m2;
m2.SetRow(0, 7.0f, 8.0f, 9.0f);
m2.SetRow(1, 10.0f, 11.0f, 12.0f);
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)));
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)));
}
TEST(MATH_Matrix3x3, TestTranspose)
{
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);
Matrix3x3 m2 = m1.GetTranspose();
AZ_TEST_ASSERT(m2.GetRow(0).IsClose(Vector3(1.0f, 4.0f, 7.0f)));
AZ_TEST_ASSERT(m2.GetRow(1).IsClose(Vector3(2.0f, 5.0f, 8.0f)));
AZ_TEST_ASSERT(m2.GetRow(2).IsClose(Vector3(3.0f, 6.0f, 9.0f)));
m2 = m1;
m2.Transpose();
AZ_TEST_ASSERT(m2.GetRow(0).IsClose(Vector3(1.0f, 4.0f, 7.0f)));
AZ_TEST_ASSERT(m2.GetRow(1).IsClose(Vector3(2.0f, 5.0f, 8.0f)));
AZ_TEST_ASSERT(m2.GetRow(2).IsClose(Vector3(3.0f, 6.0f, 9.0f)));
}
TEST(MATH_Matrix3x3, TestFastInverse)
{
// orthogonal matrix only
Matrix3x3 m1 = Matrix3x3::CreateRotationX(1.0f);
AZ_TEST_ASSERT((m1 * m1.GetInverseFast()).IsClose(Matrix3x3::CreateIdentity(), 0.02f));
Matrix3x3 m2 = Matrix3x3::CreateRotationZ(2.0f) * Matrix3x3::CreateRotationX(1.0f);
Matrix3x3 m3 = m2.GetInverseFast();
// allow a little bigger threshold, because of the 2 rot matrices (sin,cos differences)
AZ_TEST_ASSERT((m2 * m3).IsClose(Matrix3x3::CreateIdentity(), 0.1f));
AZ_TEST_ASSERT(m3.GetRow(0).IsClose(Vector3(-0.420f, 0.909f, 0.0f), 0.06f));
AZ_TEST_ASSERT(m3.GetRow(1).IsClose(Vector3(-0.493f, -0.228f, 0.841f), 0.06f));
AZ_TEST_ASSERT(m3.GetRow(2).IsClose(Vector3(0.765f, 0.353f, 0.542f), 0.06f));
}
TEST(MATH_Matrix3x3, TestFullInverse)
{
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);
AZ_TEST_ASSERT((m1 * m1.GetInverseFull()).IsClose(Matrix3x3::CreateIdentity()));
}
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()));
m1.MultiplyByScale(Vector3(3.0f, 4.0f, 5.0f));
AZ_TEST_ASSERT(m1.RetrieveScale().IsClose(Vector3(3.0f, 4.0f, 5.0f)));
}
TEST(MATH_Matrix3x3, TestPolarDecomposition)
{
Matrix3x3 m1 = Matrix3x3::CreateRotationX(DegToRad(30.0f));
Matrix3x3 m2 = Matrix3x3::CreateScale(Vector3(5.0f, 6.0f, 7.0f));
Matrix3x3 m3 = m1 * m2;
AZ_TEST_ASSERT(m3.GetPolarDecomposition().IsClose(m1));
Matrix3x3 m4, m5;
m3.GetPolarDecomposition(&m4, &m5);
AZ_TEST_ASSERT((m4 * m5).IsClose(m3));
AZ_TEST_ASSERT(m4.IsClose(m1));
AZ_TEST_ASSERT(m5.IsClose(m2, 0.01f));
}
TEST(MATH_Matrix3x3, TestOrthogonalize)
{
Matrix3x3 m1 = Matrix3x3::CreateRotationX(AZ::DegToRad(30.0f)) * Matrix3x3::CreateScale(Vector3(2.0f, 3.0f, 4.0f));
m1.SetElement(0, 1, 0.2f);
Matrix3x3 m2 = m1.GetOrthogonalized();
AZ_TEST_ASSERT(AZ::IsClose(m2.GetRow(0).GetLength(), 1.0f));
AZ_TEST_ASSERT(AZ::IsClose(m2.GetRow(1).GetLength(), 1.0f));
AZ_TEST_ASSERT(AZ::IsClose(m2.GetRow(2).GetLength(), 1.0f));
AZ_TEST_ASSERT(AZ::IsClose(m2.GetRow(0).Dot(m2.GetRow(1)), 0.0f));
AZ_TEST_ASSERT(AZ::IsClose(m2.GetRow(0).Dot(m2.GetRow(2)), 0.0f));
AZ_TEST_ASSERT(AZ::IsClose(m2.GetRow(1).Dot(m2.GetRow(2)), 0.0f));
AZ_TEST_ASSERT(m2.GetRow(0).Cross(m2.GetRow(1)).IsClose(m2.GetRow(2)));
AZ_TEST_ASSERT(m2.GetRow(1).Cross(m2.GetRow(2)).IsClose(m2.GetRow(0)));
AZ_TEST_ASSERT(m2.GetRow(2).Cross(m2.GetRow(0)).IsClose(m2.GetRow(1)));
m1.Orthogonalize();
AZ_TEST_ASSERT(m1.IsClose(m2));
}
TEST(MATH_Matrix3x3, TestOrthogonal)
{
Matrix3x3 m1 = Matrix3x3::CreateRotationX(AZ::DegToRad(30.0f));
AZ_TEST_ASSERT(m1.IsOrthogonal(0.05f));
m1.SetRow(1, m1.GetRow(1) * 2.0f);
AZ_TEST_ASSERT(!m1.IsOrthogonal(0.05f));
m1 = Matrix3x3::CreateRotationX(AZ::DegToRad(30.0f));
m1.SetRow(1, Vector3(0.0f, 1.0f, 0.0f));
AZ_TEST_ASSERT(!m1.IsOrthogonal(0.05f));
}
TEST(MATH_Matrix3x3, TestIsClose)
{
Matrix3x3 m1 = Matrix3x3::CreateRotationX(DegToRad(30.0f));
Matrix3x3 m2 = m1;
AZ_TEST_ASSERT(m1.IsClose(m2));
m2.SetElement(0, 0, 2.0f);
AZ_TEST_ASSERT(!m1.IsClose(m2));
m2 = m1;
m2.SetElement(0, 2, 2.0f);
AZ_TEST_ASSERT(!m1.IsClose(m2));
}
TEST(MATH_Matrix3x3, TestGetDiagonal)
{
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);
AZ_TEST_ASSERT(m1.GetDiagonal() == Vector3(1.0f, 5.0f, 9.0f));
}
TEST(MATH_Matrix3x3, TestDeterminant)
{
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);
AZ_TEST_ASSERT(AZ::IsClose(m1.GetDeterminant(), 240.0f));
}
TEST(MATH_Matrix3x3, TestAdjugate)
{
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);
Matrix3x3 m2 = m1.GetAdjugate();
AZ_TEST_ASSERT(m2.GetRow(0).IsClose(Vector3(-3.0f, 6.0f, -3.0f)));
AZ_TEST_ASSERT(m2.GetRow(1).IsClose(Vector3(6.0f, -12.0f, 6.0f)));
AZ_TEST_ASSERT(m2.GetRow(2).IsClose(Vector3(-3.0f, 6.0f, -3.0f)));
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,858 @@
/*
* 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/UnitTest/TestTypes.h>
#include <AzCore/Math/Matrix3x4.h>
#include <AzCore/Math/Matrix3x3.h>
#include <AzCore/Math/Quaternion.h>
#include <AZTestShared/Math/MathTestHelpers.h>
#include "MathTestData.h"
namespace UnitTest
{
using Matrix3x4CreateFixture = ::testing::TestWithParam<AZ::Vector3>;
TEST_P(Matrix3x4CreateFixture, CreateIdentity)
{
const AZ::Matrix3x4 matrix = AZ::Matrix3x4::CreateIdentity();
const AZ::Vector3 vector = GetParam();
EXPECT_THAT(matrix * vector, IsClose(vector));
}
TEST_P(Matrix3x4CreateFixture, Identity)
{
const AZ::Matrix3x4 matrix = AZ::Matrix3x4::Identity();
const AZ::Vector3 vector = GetParam();
EXPECT_THAT(matrix * vector, IsClose(vector));
}
TEST_P(Matrix3x4CreateFixture, CreateZero)
{
const AZ::Matrix3x4 matrix = AZ::Matrix3x4::CreateZero();
const AZ::Vector3 vector = GetParam();
EXPECT_THAT(matrix * vector, IsClose(AZ::Vector3::CreateZero()));
}
TEST_P(Matrix3x4CreateFixture, CreateTranslation)
{
const AZ::Vector3 translation(0.8f, 2.3f, -1.9f);
const AZ::Matrix3x4 matrix = AZ::Matrix3x4::CreateTranslation(translation);
const AZ::Vector3 vector = GetParam();
EXPECT_THAT(matrix * vector, IsClose(vector + translation));
}
INSTANTIATE_TEST_CASE_P(MATH_Matrix3x4, Matrix3x4CreateFixture, ::testing::ValuesIn(MathTestData::Vector3s));
TEST(MATH_Matrix3x4, CreateFromValue)
{
const float value = 2.3f;
const AZ::Matrix3x4 matrix = AZ::Matrix3x4::CreateFromValue(value);
for (int row = 0; row < 3; ++row)
{
for (int col = 0; col < 4; ++col)
{
EXPECT_NEAR(matrix.GetElement(row, col), value, 1e-3f);
}
}
}
TEST(MATH_Matrix3x4, CreateFromRowMajorFloat12)
{
const float values[12] = { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f, 10.0f, 11.0f, 12.0f };
AZ::Matrix3x4 matrix = AZ::Matrix3x4::CreateFromRowMajorFloat12(values);
float storedValues[12];
matrix.StoreToRowMajorFloat12(storedValues);
EXPECT_THAT(storedValues, ::testing::Pointwise(::testing::FloatNear(1e-5f), values));
EXPECT_THAT(matrix.GetColumn(0), IsClose(AZ::Vector3(1.0f, 5.0f, 9.0f)));
EXPECT_THAT(matrix.GetColumn(1), IsClose(AZ::Vector3(2.0f, 6.0f, 10.0f)));
EXPECT_THAT(matrix.GetColumn(2), IsClose(AZ::Vector3(3.0f, 7.0f, 11.0f)));
EXPECT_THAT(matrix.GetColumn(3), IsClose(AZ::Vector3(4.0f, 8.0f, 12.0f)));
}
TEST(MATH_Matrix3x4, CreateFromColumnMajorFloat12)
{
const float values[12] = { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f, 10.0f, 11.0f, 12.0f };
AZ::Matrix3x4 matrix = AZ::Matrix3x4::CreateFromColumnMajorFloat12(values);
float storedValues[12];
matrix.StoreToColumnMajorFloat12(storedValues);
EXPECT_THAT(storedValues, ::testing::Pointwise(::testing::FloatNear(1e-5f), values));
EXPECT_THAT(matrix.GetColumn(0), IsClose(AZ::Vector3(1.0f, 2.0f, 3.0f)));
EXPECT_THAT(matrix.GetColumn(1), IsClose(AZ::Vector3(4.0f, 5.0f, 6.0f)));
EXPECT_THAT(matrix.GetColumn(2), IsClose(AZ::Vector3(7.0f, 8.0f, 9.0f)));
EXPECT_THAT(matrix.GetColumn(3), IsClose(AZ::Vector3(10.0f, 11.0f, 12.0f)));
}
TEST(MATH_Matrix3x4, CreateFromColumnMajorFloat16)
{
const float values[16] = { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f, 10.0f, 11.0f, 12.0f, 13.0f, 14.0f, 15.0f, 16.0f };
AZ::Matrix3x4 matrix = AZ::Matrix3x4::CreateFromColumnMajorFloat16(values);
float storedValues[16];
matrix.StoreToColumnMajorFloat16(storedValues);
for (int row = 0; row < 3; ++row)
{
for (int col = 0; col < 4; ++col)
{
EXPECT_THAT(storedValues[4 * col + row], ::testing::FloatNear(values[4 * col + row], 1e-5f));
}
}
EXPECT_THAT(matrix.GetColumn(0), IsClose(AZ::Vector3(1.0f, 2.0f, 3.0f)));
EXPECT_THAT(matrix.GetColumn(1), IsClose(AZ::Vector3(5.0f, 6.0f, 7.0f)));
EXPECT_THAT(matrix.GetColumn(2), IsClose(AZ::Vector3(9.0f, 10.0f, 11.0f)));
EXPECT_THAT(matrix.GetColumn(3), IsClose(AZ::Vector3(13.0f, 14.0f, 15.0f)));
}
using Matrix3x4CreateRotationFixture = ::testing::TestWithParam<float>;
TEST_P(Matrix3x4CreateRotationFixture, CreateRotationX)
{
const float angle = GetParam();
AZ::Matrix3x4 matrix = AZ::Matrix3x4::CreateRotationX(angle);
EXPECT_TRUE(matrix.IsOrthogonal());
const AZ::Vector3 vector(1.5f, -0.2f, 2.7f);
const AZ::Vector3 rotatedVector = matrix.TransformVector(vector);
// rotating a vector should not affect its length
EXPECT_TRUE(AZ::IsClose(rotatedVector.GetLengthSq(), vector.GetLengthSq()));
// rotating about the X axis should not affect the X component
EXPECT_NEAR(rotatedVector.GetX(), vector.GetX(), AZ::Constants::Tolerance);
// when projected into the Y-Z plane, the angle between the rotated vector and the original vector
// should wrap to the same as the input angle parameter
const float xSquared = vector.GetX() * vector.GetX();
const float projectedDotProduct = rotatedVector.Dot(vector) - xSquared;
const float projectedMagnitudeSq = vector.Dot(vector) - xSquared;
EXPECT_NEAR(projectedDotProduct, projectedMagnitudeSq * cosf(angle), 1e-3f);
}
TEST_P(Matrix3x4CreateRotationFixture, CreateRotationY)
{
const float angle = GetParam();
AZ::Matrix3x4 matrix = AZ::Matrix3x4::CreateRotationY(angle);
EXPECT_TRUE(matrix.IsOrthogonal());
const AZ::Vector3 vector(1.5f, -0.2f, 2.7f);
const AZ::Vector3 rotatedVector = matrix.TransformVector(vector);
// rotating a vector should not affect its length
EXPECT_TRUE(AZ::IsClose(rotatedVector.GetLengthSq(), vector.GetLengthSq()));
// rotating about the Y axis should not affect the Y component
EXPECT_NEAR(rotatedVector.GetY(), vector.GetY(), AZ::Constants::Tolerance);
// when projected into the X-Z plane, the angle between the rotated vector and the original vector
// should wrap to the same as the input angle parameter
const float ySquared = vector.GetY() * vector.GetY();
const float projectedDotProduct = rotatedVector.Dot(vector) - ySquared;
const float projectedMagnitudeSq = vector.Dot(vector) - ySquared;
EXPECT_NEAR(projectedDotProduct, projectedMagnitudeSq * cosf(angle), 1e-3f);
}
TEST_P(Matrix3x4CreateRotationFixture, CreateRotationZ)
{
const float angle = GetParam();
AZ::Matrix3x4 matrix = AZ::Matrix3x4::CreateRotationZ(angle);
EXPECT_TRUE(matrix.IsOrthogonal());
const AZ::Vector3 vector(1.5f, -0.2f, 2.7f);
const AZ::Vector3 rotatedVector = matrix.TransformVector(vector);
// rotating a vector should not affect its length
EXPECT_TRUE(AZ::IsClose(rotatedVector.GetLengthSq(), vector.GetLengthSq()));
// rotating about the Z axis should not affect the Z component
EXPECT_NEAR(rotatedVector.GetZ(), vector.GetZ(), AZ::Constants::Tolerance);
// when projected into the X-Y plane, the angle between the rotated vector and the original vector
// should wrap to the same as the input angle parameter
const float zSquared = vector.GetZ() * vector.GetZ();
const float projectedDotProduct = rotatedVector.Dot(vector) - zSquared;
const float projectedMagnitudeSq = vector.Dot(vector) - zSquared;
EXPECT_NEAR(projectedDotProduct, projectedMagnitudeSq * cosf(angle), 1e-3f);
}
INSTANTIATE_TEST_CASE_P(MATH_Matrix3x4, Matrix3x4CreateRotationFixture, ::testing::ValuesIn(MathTestData::Angles));
TEST(MATH_Matrix3x4, CreateFromRows)
{
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 AZ::Matrix3x4 matrix = AZ::Matrix3x4::CreateFromRows(row0, row1, row2);
const AZ::Vector3 vector(0.2f, 0.1f, -0.3f);
const AZ::Vector3 transformedVector = matrix * vector;
const AZ::Vector3 expected(2.8248f, -1.8436f, 3.788f);
EXPECT_THAT(transformedVector, IsClose(expected));
}
TEST(MATH_Matrix3x4, GetSetRows)
{
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);
AZ::Matrix3x4 matrix;
matrix.SetRows(row0, row1, row2);
AZ::Vector4 rows[3];
matrix.GetRows(&rows[0], &rows[1], &rows[2]);
EXPECT_THAT(matrix.GetRow(0), IsClose(rows[0]));
EXPECT_THAT(matrix.GetRow(1), IsClose(rows[1]));
EXPECT_THAT(matrix.GetRow(2), IsClose(rows[2]));
const AZ::Vector3 vector(0.2f, 0.1f, -0.3f);
const AZ::Vector3 transformedVector = matrix * vector;
const AZ::Vector3 expected(2.8248f, -1.8436f, 3.788f);
EXPECT_THAT(transformedVector, IsClose(expected));
}
TEST(MATH_Matrix3x4, GetSetRow)
{
AZ::Matrix3x4 matrix;
const AZ::Vector4 row0(1.488f, 2.56f, 0.096f, 2.3f);
matrix.SetRow(0, row0);
const AZ::Vector3 row1(0.384f, -1.92f, 0.428f);
const float w1 = -1.6f;
matrix.SetRow(1, row1, w1);
const float x2 = 1.28f;
const float y2 = -2.4f;
const float z2 = -0.24f;
const float w2 = 3.7f;
matrix.SetRow(2, x2, y2, z2, w2);
EXPECT_THAT(matrix.GetRow(0), IsClose(row0));
EXPECT_THAT(matrix.GetRow(2), IsClose(AZ::Vector4(x2, y2, z2, w2)));
EXPECT_THAT(matrix.GetRowAsVector3(0), IsClose(row0.GetAsVector3()));
EXPECT_THAT(matrix.GetRowAsVector3(1), IsClose(row1));
EXPECT_THAT(matrix.GetRowAsVector3(2), IsClose(AZ::Vector3(x2, y2, z2)));
const AZ::Vector3 vector(0.2f, 0.1f, -0.3f);
const AZ::Vector3 transformedVector = matrix * vector;
const AZ::Vector3 expected(2.8248f, -1.8436f, 3.788f);
EXPECT_THAT(transformedVector, IsClose(expected));
}
TEST(MATH_Matrix3x4, CreateFromColumns)
{
const AZ::Vector3 col0(1.488f, 0.384f, 1.28f);
const AZ::Vector3 col1(2.56f, -1.92f, -2.4f);
const AZ::Vector3 col2(0.096f, 0.428f, -0.24f);
const AZ::Vector3 col3(2.3f, -1.6f, 3.7f);
const AZ::Matrix3x4 matrix = AZ::Matrix3x4::CreateFromColumns(col0, col1, col2, col3);
const AZ::Vector3 vector(0.2f, 0.1f, -0.3f);
const AZ::Vector3 transformedVector = matrix * vector;
const AZ::Vector3 expected(2.8248f, -1.8436f, 3.788f);
EXPECT_THAT(transformedVector, IsClose(expected));
}
TEST(MATH_Matrix3x4, GetSetColumns)
{
const AZ::Vector3 inputCol0(2.2f, -0.4f, -1.2f);
const AZ::Vector3 inputCol1(-0.3f, 1.2f, 0.2f);
const AZ::Vector3 inputCol2(0.6f, -0.2f, 1.7f);
const AZ::Vector3 inputCol3(3.2f, 1.7f, -0.9f);
AZ::Matrix3x4 matrix;
matrix.SetColumns(inputCol0, inputCol1, inputCol2, inputCol3);
AZ::Vector3 col0, col1, col2, col3;
matrix.GetColumns(&col0, &col1, &col2, &col3);
EXPECT_THAT(col0, IsClose(inputCol0));
EXPECT_THAT(col1, IsClose(inputCol1));
EXPECT_THAT(col2, IsClose(inputCol2));
EXPECT_THAT(col3, IsClose(inputCol3));
}
TEST(MATH_Matrix3x4, GetSetColumn)
{
const AZ::Vector3 inputCol0(1.3f, 1.4f, -0.2f);
const float x = -0.7f;
const float y = 1.2f;
const float z = -0.4f;
AZ::Matrix3x4 matrix;
matrix.SetColumn(0, inputCol0);
matrix.SetColumn(1, x, y, z);
EXPECT_THAT(matrix.GetColumn(0), IsClose(inputCol0));
EXPECT_THAT(matrix.GetColumn(1), IsClose(AZ::Vector3(x, y, z)));
}
TEST(MATH_Matrix3x4, GetSetBasisAndTranslation)
{
const AZ::Vector3 inputBasisX(-1.9f, -0.2f, 2.3f);
const AZ::Vector3 inputBasisY(1.4f, 0.1f, -1.9f);
const AZ::Vector3 inputBasisZ(2.1f, 0.6f, 1.1f);
const AZ::Vector3 inputTranslation(-0.4f, -0.9f, -1.3f);
AZ::Matrix3x4 matrix;
matrix.SetBasisAndTranslation(inputBasisX, inputBasisY, inputBasisZ, inputTranslation);
AZ::Vector3 basisX, basisY, basisZ, translation;
matrix.GetBasisAndTranslation(&basisX, &basisY, &basisZ, &translation);
EXPECT_THAT(basisX, IsClose(inputBasisX));
EXPECT_THAT(basisY, IsClose(inputBasisY));
EXPECT_THAT(basisZ, IsClose(inputBasisZ));
EXPECT_THAT(translation, IsClose(inputTranslation));
}
TEST(MATH_Matrix3x4, GetSetBasisX)
{
const AZ::Vector3 inputBasisX(1.6f, 1.3f, -0.8f);
const float x = 0.2f;
const float y = 1.3f;
const float z = -3.4f;
AZ::Matrix3x4 matrix;
matrix.SetBasisX(inputBasisX);
EXPECT_THAT(matrix.GetBasisX(), IsClose(inputBasisX));
matrix.SetBasisX(x, y, z);
EXPECT_THAT(matrix.GetBasisX(), IsClose(AZ::Vector3(x, y, z)));
}
TEST(MATH_Matrix3x4, GetSetBasisY)
{
const AZ::Vector3 inputBasisY(-0.7f, 0.9f, 2.7f);
const float x = -0.5f;
const float y = -0.3f;
const float z = -0.6f;
AZ::Matrix3x4 matrix;
matrix.SetBasisY(inputBasisY);
EXPECT_THAT(matrix.GetBasisY(), IsClose(inputBasisY));
matrix.SetBasisY(x, y, z);
EXPECT_THAT(matrix.GetBasisY(), IsClose(AZ::Vector3(x, y, z)));
}
TEST(MATH_Matrix3x4, GetSetBasisZ)
{
const AZ::Vector3 inputBasisZ(2.8f, 0.9f, -2.9f);
const float x = 0.1f;
const float y = 0.6f;
const float z = 0.9f;
AZ::Matrix3x4 matrix;
matrix.SetBasisZ(inputBasisZ);
EXPECT_THAT(matrix.GetBasisZ(), IsClose(inputBasisZ));
matrix.SetBasisZ(x, y, z);
EXPECT_THAT(matrix.GetBasisZ(), IsClose(AZ::Vector3(x, y, z)));
}
TEST(MATH_Matrix3x4, GetSetTranslation)
{
const AZ::Vector3 inputTranslation(-1.2f, -0.2f, 1.9f);
const float x = -2.7f;
const float y = -0.7f;
const float z = -1.2f;
AZ::Matrix3x4 matrix;
matrix.SetTranslation(inputTranslation);
EXPECT_THAT(matrix.GetTranslation(), IsClose(inputTranslation));
matrix.SetTranslation(x, y, z);
EXPECT_THAT(matrix.GetTranslation(), IsClose(AZ::Vector3(x, y, z)));
}
using Matrix3x4CreateFromQuaternionFixture = ::testing::TestWithParam<AZ::Quaternion>;
TEST_P(Matrix3x4CreateFromQuaternionFixture, CreateFromQuaternion)
{
const AZ::Quaternion quaternion = GetParam();
const AZ::Matrix3x4 matrix = AZ::Matrix3x4::CreateFromQuaternion(quaternion);
EXPECT_THAT(matrix.GetTranslation(), IsClose(AZ::Vector3::CreateZero()));
const AZ::Vector3 vector(2.3f, -0.6, 1.8f);
EXPECT_THAT(matrix * vector, IsClose(quaternion.TransformVector(vector)));
}
TEST_P(Matrix3x4CreateFromQuaternionFixture, CreateFromQuaternionAndTranslation)
{
const AZ::Quaternion quaternion = GetParam();
const AZ::Vector3 translation(-2.6f, 1.7f, 0.8f);
const AZ::Matrix3x4 matrix = AZ::Matrix3x4::CreateFromQuaternionAndTranslation(quaternion, translation);
EXPECT_THAT(matrix.GetTranslation(), IsClose(translation));
const AZ::Vector3 vector(2.3f, -0.6, 1.8f);
EXPECT_THAT(matrix * vector, IsClose(quaternion.TransformVector(vector) + translation));
}
TEST_P(Matrix3x4CreateFromQuaternionFixture, SetRotationPartFromQuaternion)
{
const AZ::Quaternion quaternion = GetParam();
AZ::Matrix3x4 matrix = AZ::Matrix3x4::CreateIdentity();
matrix.SetRotationPartFromQuaternion(quaternion);
const AZ::Vector3 vector(2.3f, -0.6, 1.8f);
EXPECT_THAT(matrix * vector, IsClose(quaternion.TransformVector(vector)));
}
INSTANTIATE_TEST_CASE_P(MATH_Matrix3x4, Matrix3x4CreateFromQuaternionFixture, ::testing::ValuesIn(MathTestData::UnitQuaternions));
using Matrix3x4CreateFromMatrix3x3Fixture = ::testing::TestWithParam<AZ::Matrix3x3>;
TEST_P(Matrix3x4CreateFromMatrix3x3Fixture, CreateFromMatrix3x3)
{
const AZ::Matrix3x3 matrix3x3 = GetParam();
const AZ::Matrix3x4 matrix3x4 = AZ::Matrix3x4::CreateFromMatrix3x3(matrix3x3);
EXPECT_THAT(matrix3x4.GetTranslation(), IsClose(AZ::Vector3::CreateZero()));
const AZ::Vector3 vector(2.3f, -0.6, 1.8f);
EXPECT_THAT(matrix3x4.TransformVector(vector), IsClose(matrix3x3 * vector));
}
TEST_P(Matrix3x4CreateFromMatrix3x3Fixture, CreateFromMatrix3x3AndTranslation)
{
const AZ::Matrix3x3 matrix3x3 = GetParam();
const AZ::Vector3 translation(-2.6f, 1.7f, 0.8f);
const AZ::Matrix3x4 matrix3x4 = AZ::Matrix3x4::CreateFromMatrix3x3AndTranslation(matrix3x3, translation);
EXPECT_THAT(matrix3x4.GetTranslation(), IsClose(translation));
const AZ::Vector3 vector(2.3f, -0.6, 1.8f);
EXPECT_THAT(matrix3x4 * vector, IsClose(matrix3x3 * vector + translation));
}
INSTANTIATE_TEST_CASE_P(MATH_Matrix3x4, Matrix3x4CreateFromMatrix3x3Fixture, ::testing::ValuesIn(MathTestData::Matrix3x3s));
TEST(MATH_Matrix3x4, CreateScale)
{
const AZ::Vector3 scale(1.7f, 0.3f, 2.4f);
const AZ::Matrix3x4 matrix = AZ::Matrix3x4::CreateScale(scale);
const AZ::Vector3 vector(0.2f, -1.6f, 0.4f);
EXPECT_THAT(matrix.GetTranslation(), IsClose(AZ::Vector3::CreateZero()));
const AZ::Vector3 transformedVector = matrix * vector;
const AZ::Vector3 expected(0.34f, -0.48f, 0.96f);
EXPECT_THAT(transformedVector, IsClose(expected));
}
TEST(MATH_Matrix3x4, CreateDiagonal)
{
const AZ::Vector3 diagonal(0.6f, -1.4f, -0.7f);
const AZ::Matrix3x4 matrix = AZ::Matrix3x4::CreateDiagonal(diagonal);
const AZ::Vector3 vector(-0.3f, -0.6f, 0.2f);
EXPECT_THAT(matrix.GetTranslation(), IsClose(AZ::Vector3::CreateZero()));
const AZ::Vector3 transformedVector = matrix * vector;
const AZ::Vector3 expected(-0.18f, 0.84f, -0.14f);
EXPECT_THAT(transformedVector, IsClose(expected));
}
TEST(MATH_Matrix3x4, GetSetElement)
{
AZ::Matrix3x4 matrix = AZ::Matrix3x4::CreateIdentity();
EXPECT_NEAR(matrix(1, 1), 1.0f, 1e-3f);
matrix.SetElement(1, 1, -2.3f);
EXPECT_NEAR(matrix(1, 1), -2.3f, 1e-3f);
}
using Matrix3x4CreateLookAtFixture = ::testing::TestWithParam<MathTestData::AxisPair>;
TEST_P(Matrix3x4CreateLookAtFixture, CreateLookAt)
{
const AZ::Matrix3x4::Axis axis = GetParam().first;
const AZ::Vector3 axisDirection = GetParam().second;
const AZ::Vector3 from(2.5f, 0.2f, 3.6f);
const AZ::Vector3 to(1.3f, 0.5f, 3.2f);
const AZ::Vector3 expectedForward = to - from;
const AZ::Matrix3x4 matrix = AZ::Matrix3x4::CreateLookAt(from, to, axis);
EXPECT_TRUE(matrix.IsOrthogonal());
EXPECT_THAT(matrix.GetColumn(0).Cross(matrix.GetColumn(1)), IsClose(matrix.GetColumn(2)));
EXPECT_THAT(matrix.GetTranslation(), IsClose(from));
// the column of the matrix corresponding to the axis direction should be parallel to the expected forward direction
const AZ::Vector3 forward = matrix.Multiply3x3(axisDirection);
EXPECT_THAT(forward, IsClose(expectedForward.GetNormalized()));
}
INSTANTIATE_TEST_CASE_P(MATH_Matrix3x4, Matrix3x4CreateLookAtFixture, ::testing::ValuesIn(MathTestData::Axes));
TEST(MATH_Matrix3x4, CreateLookAtDegenerateCases)
{
const AZ::Vector3 from(2.5f, 0.2f, 3.6f);
// to and from are the same, should generate an error
AZ_TEST_START_TRACE_SUPPRESSION;
EXPECT_TRUE(AZ::Matrix3x4::CreateLookAt(from, from).IsClose(AZ::Matrix3x4::Identity()));
AZ_TEST_STOP_TRACE_SUPPRESSION(1);
// to - from is parallel to usual up direction
const AZ::Vector3 to(2.5f, 0.2f, 5.2f);
const AZ::Matrix3x4 matrix = AZ::Matrix3x4::CreateLookAt(from, to);
EXPECT_TRUE(matrix.IsOrthogonal());
EXPECT_THAT(matrix.GetTranslation(), IsClose(from));
// the default is for the Y basis of the look at matrix to be the forward direction
const AZ::Vector3 axisDirection = AZ::Vector3::CreateAxisY();
const AZ::Vector3 forwardDirection = (to - from).GetNormalized();
EXPECT_THAT(matrix.Multiply3x3(axisDirection), IsClose(forwardDirection));
}
TEST(MATH_Matrix3x4, MultiplyByMatrix3x4)
{
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));
}
TEST(MATH_Matrix3x4, MultiplyByVector3)
{
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);
AZ::Matrix3x4 matrix = AZ::Matrix3x4::CreateFromRows(row0, row1, row2);
const AZ::Vector3 vector(0.2f, 0.1f, -0.3f);
const AZ::Vector3 expected(2.8248f, -1.8436f, 3.788f);
EXPECT_THAT(matrix * vector, IsClose(expected));
}
TEST(MATH_Matrix3x4, Multiply3x3)
{
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);
AZ::Matrix3x4 matrix = AZ::Matrix3x4::CreateFromRows(row0, row1, row2);
const AZ::Vector3 vector(0.2f, 0.1f, -0.3f);
const AZ::Vector3 expected(0.5248f, -0.2436f, 0.088f);
EXPECT_THAT(matrix.Multiply3x3(vector), IsClose(expected));
matrix.SetTranslation(AZ::Vector3(0.9f, 2.6f, -2.2f));
EXPECT_THAT(matrix.Multiply3x3(vector), IsClose(expected));
}
TEST(MATH_Matrix3x4, MultiplyByVector4)
{
const AZ::Vector4 row0(-0.4f, 0.5f, 0.4f, -0.5f);
const AZ::Vector4 row1(0.9f, -1.0f, 0.6f, 0.4f);
const AZ::Vector4 row2(0.4f, 0.3f, 0.3f, -0.8f);
const AZ::Vector4 vector(0.4f, -1.0f, -0.2f, 0.3f);
const AZ::Matrix3x4 matrix = AZ::Matrix3x4::CreateFromRows(row0, row1, row2);
const AZ::Vector4 product = matrix * vector;
const AZ::Vector4 expected(-0.89f, 1.36f, -0.44f, 0.3f);
EXPECT_THAT(product, IsClose(expected));
}
using Matrix3x4TransposeFixture = ::testing::TestWithParam<AZ::Matrix3x4>;
TEST_P(Matrix3x4TransposeFixture, GetTranspose)
{
const AZ::Matrix3x4 matrix = GetParam();
const AZ::Matrix3x4 transpose = matrix.GetTranspose();
EXPECT_THAT(transpose.GetTranslation(), IsClose(AZ::Vector3::CreateZero()));
EXPECT_THAT(transpose.GetColumn(0), IsClose(matrix.GetRowAsVector3(0)));
EXPECT_THAT(transpose.GetColumn(1), IsClose(matrix.GetRowAsVector3(1)));
EXPECT_THAT(transpose.GetColumn(2), IsClose(matrix.GetRowAsVector3(2)));
}
TEST_P(Matrix3x4TransposeFixture, Transpose)
{
const AZ::Matrix3x4 matrix = GetParam();
AZ::Matrix3x4 transpose = matrix;
transpose.Transpose();
EXPECT_THAT(transpose.GetTranslation(), IsClose(AZ::Vector3::CreateZero()));
EXPECT_THAT(transpose.GetColumn(0), IsClose(matrix.GetRowAsVector3(0)));
EXPECT_THAT(transpose.GetColumn(1), IsClose(matrix.GetRowAsVector3(1)));
EXPECT_THAT(transpose.GetColumn(2), IsClose(matrix.GetRowAsVector3(2)));
}
TEST_P(Matrix3x4TransposeFixture, GetTranspose3x3)
{
const AZ::Matrix3x4 matrix = GetParam();
const AZ::Matrix3x4 transpose = matrix.GetTranspose3x3();
EXPECT_THAT(transpose.GetTranslation(), IsClose(matrix.GetTranslation()));
EXPECT_THAT(transpose.GetColumn(0), IsClose(matrix.GetRowAsVector3(0)));
EXPECT_THAT(transpose.GetColumn(1), IsClose(matrix.GetRowAsVector3(1)));
EXPECT_THAT(transpose.GetColumn(2), IsClose(matrix.GetRowAsVector3(2)));
}
TEST_P(Matrix3x4TransposeFixture, Transpose3x3)
{
const AZ::Matrix3x4 matrix = GetParam();
AZ::Matrix3x4 transpose = matrix;
transpose.Transpose3x3();
EXPECT_THAT(transpose.GetTranslation(), IsClose(matrix.GetTranslation()));
EXPECT_THAT(transpose.GetColumn(0), IsClose(matrix.GetRowAsVector3(0)));
EXPECT_THAT(transpose.GetColumn(1), IsClose(matrix.GetRowAsVector3(1)));
EXPECT_THAT(transpose.GetColumn(2), IsClose(matrix.GetRowAsVector3(2)));
}
INSTANTIATE_TEST_CASE_P(MATH_Matrix3x4, Matrix3x4TransposeFixture, ::testing::ValuesIn(MathTestData::NonOrthogonalMatrix3x4s));
using Matrix3x4InvertFullFixture = ::testing::TestWithParam<AZ::Matrix3x4>;
TEST_P(Matrix3x4InvertFullFixture, GetInverseFull)
{
const AZ::Matrix3x4 matrix = GetParam();
const AZ::Matrix3x4 inverse = matrix.GetInverseFull();
const AZ::Vector3 vector(0.9f, 3.2f, -1.4f);
EXPECT_THAT((inverse * matrix) * vector, IsClose(vector));
EXPECT_THAT((matrix * inverse) * vector, IsClose(vector));
EXPECT_TRUE((inverse * matrix).IsClose(AZ::Matrix3x4::Identity()));
}
TEST_P(Matrix3x4InvertFullFixture, InvertFull)
{
const AZ::Matrix3x4 matrix = GetParam();
AZ::Matrix3x4 inverse = matrix;
inverse.InvertFull();
const AZ::Vector3 vector(2.8f, -1.3f, 2.6f);
EXPECT_THAT((inverse * matrix) * vector, IsClose(vector));
EXPECT_THAT((matrix * inverse) * vector, IsClose(vector));
EXPECT_TRUE((inverse * matrix).IsClose(AZ::Matrix3x4::Identity()));
}
INSTANTIATE_TEST_CASE_P(MATH_Matrix3x4, Matrix3x4InvertFullFixture, ::testing::ValuesIn(MathTestData::NonOrthogonalMatrix3x4s));
#if AZ_TRAIT_DISABLE_FAILED_MATH_TESTS
TEST(MATH_Matrix3x4, DISABLED_GetInverseFullSingularMatrix)
#else
TEST(MATH_Matrix3x4, GetInverseFullSingularMatrix)
#endif // AZ_TRAIT_DISABLE_FAILED_MATH_TESTS
{
const AZ::Matrix3x4 matrix = AZ::Matrix3x4::CreateFromValue(1.4f);
const AZ::Matrix3x4 inverse = matrix.GetInverseFull();
EXPECT_THAT(inverse.GetBasisX(), IsClose(AZ::Vector3::CreateAxisX()));
EXPECT_THAT(inverse.GetBasisY(), IsClose(AZ::Vector3::CreateAxisY()));
EXPECT_THAT(inverse.GetBasisZ(), IsClose(AZ::Vector3::CreateAxisZ()));
EXPECT_THAT(inverse.GetTranslation(), IsClose(AZ::Vector3(-1.4f)));
}
using Matrix3x4InvertFastFixture = ::testing::TestWithParam<AZ::Matrix3x4>;
TEST_P(Matrix3x4InvertFastFixture, GetInverseFast)
{
const AZ::Matrix3x4 matrix = GetParam();
const AZ::Matrix3x4 inverseFast = matrix.GetInverseFast();
const AZ::Matrix3x4 inverseFull = matrix.GetInverseFull();
const AZ::Vector3 vector(0.9f, 3.2f, -1.4f);
EXPECT_THAT((inverseFast * matrix) * vector, IsClose(vector));
EXPECT_THAT((matrix * inverseFast) * vector, IsClose(vector));
EXPECT_TRUE((inverseFast * matrix).IsClose(AZ::Matrix3x4::Identity()));
EXPECT_TRUE(inverseFast.IsClose(inverseFull));
}
TEST_P(Matrix3x4InvertFastFixture, InvertFast)
{
const AZ::Matrix3x4 matrix = GetParam();
AZ::Matrix3x4 inverseFast = matrix;
inverseFast.InvertFast();
AZ::Matrix3x4 inverseFull = matrix;
inverseFull.InvertFull();
const AZ::Vector3 vector(2.8f, -1.3f, 2.6f);
EXPECT_THAT((inverseFast * matrix) * vector, IsClose(vector));
EXPECT_THAT((matrix * inverseFast) * vector, IsClose(vector));
EXPECT_TRUE((inverseFast * matrix).IsClose(AZ::Matrix3x4::Identity()));
EXPECT_TRUE(inverseFast.IsClose(inverseFull));
}
INSTANTIATE_TEST_CASE_P(MATH_Matrix3x4, Matrix3x4InvertFastFixture, ::testing::ValuesIn(MathTestData::OrthogonalMatrix3x4s));
using Matrix3x4ScaleFixture = ::testing::TestWithParam<AZ::Matrix3x4>;
TEST_P(Matrix3x4ScaleFixture, Scale)
{
const AZ::Matrix3x4 orthogonalMatrix = GetParam();
EXPECT_THAT(orthogonalMatrix.RetrieveScale(), IsClose(AZ::Vector3::CreateOne()));
AZ::Matrix3x4 unscaledMatrix = orthogonalMatrix;
unscaledMatrix.ExtractScale();
EXPECT_THAT(unscaledMatrix.RetrieveScale(), IsClose(AZ::Vector3::CreateOne()));
const AZ::Vector3 scale(2.8f, 0.7f, 1.3f);
AZ::Matrix3x4 scaledMatrix = orthogonalMatrix;
scaledMatrix.MultiplyByScale(scale);
EXPECT_THAT(scaledMatrix.RetrieveScale(), IsClose(scale));
scaledMatrix.ExtractScale();
EXPECT_THAT(scaledMatrix.RetrieveScale(), IsClose(AZ::Vector3::CreateOne()));
}
INSTANTIATE_TEST_CASE_P(MATH_Matrix3x4, Matrix3x4ScaleFixture, ::testing::ValuesIn(MathTestData::OrthogonalMatrix3x4s));
TEST(MATH_Matrix3x4, IsOrthogonal)
{
EXPECT_TRUE(AZ::Matrix3x4::CreateIdentity().IsOrthogonal());
EXPECT_TRUE(AZ::Matrix3x4::CreateRotationZ(0.3f).IsOrthogonal());
EXPECT_FALSE(AZ::Matrix3x4::CreateFromValue(1.0f).IsOrthogonal());
EXPECT_FALSE(AZ::Matrix3x4::CreateDiagonal(AZ::Vector3(0.8f, 0.3f, 1.2f)).IsOrthogonal());
EXPECT_TRUE(AZ::Matrix3x4::CreateFromQuaternion(AZ::Quaternion(-0.52f, -0.08f, 0.56f, 0.64f)).IsOrthogonal());
AZ::Matrix3x4 matrix3x4;
matrix3x4.SetFromEulerRadians(AZ::Vector3(0.2f, 0.4f, 0.1f));
EXPECT_TRUE(matrix3x4.IsOrthogonal());
// want to test each possible way the matrix could fail to be orthogonal, which we can do by testing for one
// axis, then using a rotation which cycles the axes
const AZ::Matrix3x4 axisCycle = AZ::Matrix3x4::CreateFromQuaternion(AZ::Quaternion(0.5f, 0.5f, 0.5f, 0.5f));
// a matrix which is normalized in 2 axes, but not the third
AZ::Matrix3x4 nonOrthogonalMatrix1 = AZ::Matrix3x4::CreateDiagonal(AZ::Vector3(1.0f, 1.0f, 2.0f));
// a matrix which is normalized in all 3 axes, and 2 pairs of axes are perpendicular, but not the third pair
AZ::Matrix3x4 nonOrthogonalMatrix2 = AZ::Matrix3x4::Identity();
nonOrthogonalMatrix2.SetRow(2, AZ::Vector3(0.0f, 0.8f, 0.6f), 0.0f);
for (int i = 0; i < 3; i++)
{
EXPECT_FALSE(nonOrthogonalMatrix1.IsOrthogonal());
EXPECT_FALSE(nonOrthogonalMatrix2.IsOrthogonal());
nonOrthogonalMatrix1 = axisCycle * nonOrthogonalMatrix1;
nonOrthogonalMatrix2 = axisCycle * nonOrthogonalMatrix2;
}
}
TEST(MATH_Matrix3x4, GetOrthogonalized)
{
// a matrix which is already orthogonal should be unchanged
const AZ::Matrix3x4 orthogonalMatrix = AZ::Matrix3x4::CreateRotationZ(0.7f);
EXPECT_TRUE(orthogonalMatrix.IsOrthogonal());
EXPECT_TRUE(orthogonalMatrix.GetOrthogonalized().IsClose(orthogonalMatrix));
// a matrix which isn't already orthogonal should be made orthogonal
const AZ::Matrix3x4 nonOrthogonalMatrix = AZ::Matrix3x4::CreateScale(AZ::Vector3(3.0f, 4.0f, 5.0f));
EXPECT_FALSE(nonOrthogonalMatrix.IsOrthogonal());
EXPECT_TRUE(nonOrthogonalMatrix.GetOrthogonalized().IsOrthogonal());
}
TEST(MATH_Matrix3x4, Orthogonalize)
{
// a matrix which is already orthogonal should be unchanged
AZ::Matrix3x4 orthogonalMatrix = AZ::Matrix3x4::CreateRotationY(-0.2f);
EXPECT_TRUE(orthogonalMatrix.IsOrthogonal());
orthogonalMatrix.Orthogonalize();
EXPECT_TRUE(orthogonalMatrix.IsClose(AZ::Matrix3x4::CreateRotationY(-0.2f)));
// a matrix which isn't already orthogonal should be made orthogonal
AZ::Matrix3x4 nonOrthogonalMatrix = AZ::Matrix3x4::CreateScale(AZ::Vector3(0.7f, 0.7f, 0.2f));
EXPECT_FALSE(nonOrthogonalMatrix.IsOrthogonal());
nonOrthogonalMatrix.Orthogonalize();
EXPECT_TRUE(nonOrthogonalMatrix.IsOrthogonal());
}
TEST(MATH_Matrix3x4, IsClose)
{
const AZ::Matrix3x4 matrix1 = AZ::Matrix3x4::CreateFromQuaternionAndTranslation(
AZ::Quaternion(0.12f, 0.24f, -0.72f, 0.64f),
AZ::Vector3(0.3f, 0.2f, -0.7f)
);
AZ::Matrix3x4 matrix2 = matrix1;
EXPECT_TRUE(matrix2.IsClose(matrix1, 1e-6f));
matrix2.SetElement(0, 2, matrix2(0, 2) + 1e-2f);
matrix2.SetElement(2, 3, matrix2(2, 3) + 1e-4f);
matrix2.SetElement(1, 1, matrix2(1, 1) - 1e-6f);
EXPECT_TRUE(matrix2.IsClose(matrix1, 1e-1f));
EXPECT_FALSE(matrix2.IsClose(matrix1, 1e-3f));
EXPECT_FALSE(matrix2.IsClose(matrix1, 1e-5f));
EXPECT_FALSE(matrix2.IsClose(matrix1, 1e-7f));
}
TEST(MATH_Matrix3x4, Equality)
{
const AZ::Matrix3x4 matrix1 = AZ::Matrix3x4::CreateFromQuaternionAndTranslation(
AZ::Quaternion(0.12f, 0.24f, -0.72f, 0.64f),
AZ::Vector3(0.3f, 0.2f, -0.7f)
);
AZ::Matrix3x4 matrix2 = matrix1;
EXPECT_TRUE(matrix2 == matrix1);
EXPECT_FALSE(matrix2 != matrix1);
for (int row = 0; row < 3; row++)
{
for (int col = 0; col < 3; col++)
{
matrix2 = matrix1;
matrix2.SetElement(row, col, matrix2(row, col) + 1e-4f);
EXPECT_FALSE(matrix2 == matrix1);
EXPECT_TRUE(matrix2 != matrix1);
}
}
}
using Matrix3x4SetFromEulerDegreesFixture = ::testing::TestWithParam<AZ::Vector3>;
TEST_P(Matrix3x4SetFromEulerDegreesFixture, SetFromEulerDegrees)
{
const AZ::Vector3 eulerDegrees = GetParam();
AZ::Matrix3x4 matrix;
matrix.SetFromEulerDegrees(eulerDegrees);
const AZ::Vector3 eulerRadians = AZ::Vector3DegToRad(eulerDegrees);
const AZ::Matrix3x4 rotX = AZ::Matrix3x4::CreateRotationX(eulerRadians.GetX());
const AZ::Matrix3x4 rotY = AZ::Matrix3x4::CreateRotationY(eulerRadians.GetY());
const AZ::Matrix3x4 rotZ = AZ::Matrix3x4::CreateRotationZ(eulerRadians.GetZ());
EXPECT_TRUE(matrix.IsClose(rotX * rotY * rotZ));
}
INSTANTIATE_TEST_CASE_P(MATH_Matrix3x4, Matrix3x4SetFromEulerDegreesFixture, ::testing::ValuesIn(MathTestData::EulerAnglesDegrees));
using Matrix3x4SetFromEulerRadiansFixture = ::testing::TestWithParam<AZ::Vector3>;
TEST_P(Matrix3x4SetFromEulerRadiansFixture, SetFromEulerRadians)
{
const AZ::Vector3 eulerRadians = GetParam();
AZ::Matrix3x4 matrix;
matrix.SetFromEulerRadians(eulerRadians);
const AZ::Matrix3x4 rotX = AZ::Matrix3x4::CreateRotationX(eulerRadians.GetX());
const AZ::Matrix3x4 rotY = AZ::Matrix3x4::CreateRotationY(eulerRadians.GetY());
const AZ::Matrix3x4 rotZ = AZ::Matrix3x4::CreateRotationZ(eulerRadians.GetZ());
EXPECT_TRUE(matrix.IsClose(rotX * rotY * rotZ));
}
INSTANTIATE_TEST_CASE_P(MATH_Matrix3x4, Matrix3x4SetFromEulerRadiansFixture, ::testing::ValuesIn(MathTestData::EulerAnglesRadians));
using Matrix3x4GetEulerFixture = ::testing::TestWithParam<AZ::Matrix3x4>;
TEST_P(Matrix3x4GetEulerFixture, GetEuler)
{
// there isn't a one to one mapping between matrices and Euler angles, so testing for a particular set of Euler
// angles to be returned would be fragile, but getting the Euler angles and creating a new matrix from them
// should return the original matrix
AZ::Matrix3x4 matrix = GetParam();
matrix.SetTranslation(AZ::Vector3::CreateZero());
const AZ::Vector3 eulerDegrees = matrix.GetEulerDegrees();
AZ::Matrix3x4 eulerMatrix;
eulerMatrix.SetFromEulerDegrees(eulerDegrees);
EXPECT_TRUE(eulerMatrix.IsClose(matrix));
const AZ::Vector3 eulerRadians = matrix.GetEulerRadians();
eulerMatrix = AZ::Matrix3x4::Identity();
eulerMatrix.SetFromEulerRadians(eulerRadians);
EXPECT_TRUE(eulerMatrix.IsClose(matrix));
}
INSTANTIATE_TEST_CASE_P(MATH_Matrix3x4, Matrix3x4GetEulerFixture, ::testing::ValuesIn(MathTestData::OrthogonalMatrix3x4s));
using Matrix3x4GetDeterminantFixture = ::testing::TestWithParam<AZ::Matrix3x4>;
TEST_P(Matrix3x4GetDeterminantFixture, GetDeterminantOfOrthogonalMatrices)
{
const AZ::Matrix3x4 matrix = GetParam();
EXPECT_NEAR(matrix.GetDeterminant3x3(), 1.0f, 1e-3f);
}
INSTANTIATE_TEST_CASE_P(MATH_Matrix3x4, Matrix3x4GetDeterminantFixture, ::testing::ValuesIn(MathTestData::OrthogonalMatrix3x4s));
TEST(MATH_Matrix3x4, GetDeterminantOfArbitraryMatrices)
{
const AZ::Matrix3x4 matrix1 = AZ::Matrix3x4::CreateFromValue(0.8f);
const AZ::Matrix3x4 matrix2 = AZ::Matrix3x4::CreateDiagonal(AZ::Vector3(0.2f, 1.5f, 0.6f));
const AZ::Matrix3x4 matrix3 = AZ::Matrix3x4::CreateRotationY(0.2f) * AZ::Matrix3x4::CreateScale(AZ::Vector3(2.0f, 0.5f, 1.2f));
const AZ::Matrix3x4 matrix4 = AZ::Matrix3x4::CreateFromRows(matrix3.GetRow(0), matrix3.GetRow(2), matrix3.GetRow(1));
const float expected1 = 0.0f;
const float expected2 = 0.18f;
const float expected3 = 1.2f;
const float expected4 = -expected3;
EXPECT_NEAR(matrix1.GetDeterminant3x3(), expected1, 1e-3f);
EXPECT_NEAR(matrix2.GetDeterminant3x3(), expected2, 1e-3f);
EXPECT_NEAR(matrix3.GetDeterminant3x3(), expected3, 1e-3f);
EXPECT_NEAR(matrix4.GetDeterminant3x3(), expected4, 1e-3f);
EXPECT_NEAR((matrix2 * matrix3).GetDeterminant3x3(), expected2 * expected3, 1e-3f);
EXPECT_NEAR(matrix2.GetTranspose3x3().GetDeterminant3x3(), expected2, 1e-3f);
}
#if AZ_TRAIT_DISABLE_FAILED_MATH_TESTS
TEST(MATH_Matrix3x4, DISABLED_IsFinite)
#else
TEST(MATH_Matrix3x4, IsFinite)
#endif // AZ_TRAIT_DISABLE_FAILED_MATH_TESTS
{
AZ::Matrix3x4 matrix = AZ::Matrix3x4::CreateFromQuaternionAndTranslation(
AZ::Quaternion(-0.42f, -0.46f, 0.66f, 0.42f),
AZ::Vector3(0.8f, -2.3f, 2.2f)
);
EXPECT_TRUE(matrix.IsFinite());
for (int row = 0; row < 3; row++)
{
for (int col = 0; col < 3; col++)
{
float value = matrix.GetElement(row, col);
matrix.SetElement(row, col, acosf(2.0f));
EXPECT_FALSE(matrix.IsFinite());
matrix.SetElement(row, col, value);
}
}
}
} // namespace UnitTest
@@ -0,0 +1,585 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyrightand 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.
*
*/
#if defined(HAVE_BENCHMARK)
#include <AzCore/Math/Matrix4x4.h>
#include <AzCore/Math/Quaternion.h>
#include <AzCore/UnitTest/TestTypes.h>
#include <random>
namespace Benchmark
{
const static float s_mat4x4testArray[] = { 1.f, 2.f, 3.f, 4.f, 5.f, 6.f, 7.f, 8.f, 9.f, 10.f, 11.f, 12.f, 13.f, 14.f, 15.f, 16.f };
class BM_MathMatrix4x4
: public benchmark::Fixture
{
public:
void SetUp([[maybe_unused]] const ::benchmark::State& state) override
{
m_testDataArray.resize(1000);
const unsigned int seed = 1;
std::mt19937_64 rng(seed);
std::uniform_real_distribution<float> unif;
std::generate(m_testDataArray.begin(), m_testDataArray.end(), [&unif, &rng]()
{
TestData testData;
testData.q1 = AZ::Quaternion(unif(rng), unif(rng), unif(rng), unif(rng)).GetNormalized();
testData.m1 = AZ::Matrix4x4::CreateFromQuaternionAndTranslation(testData.q1, AZ::Vector3(unif(rng), unif(rng), unif(rng)));
testData.q1 = AZ::Quaternion(unif(rng), unif(rng), unif(rng), unif(rng)).GetNormalized();
testData.m2 = AZ::Matrix4x4::CreateFromQuaternionAndTranslation(testData.q1, AZ::Vector3(unif(rng), unif(rng), unif(rng)));
testData.v1 = AZ::Vector4(unif(rng), unif(rng), unif(rng), unif(rng));
testData.v2 = AZ::Vector3(unif(rng), unif(rng), unif(rng));
return testData;
});
}
struct TestData
{
AZ::Matrix4x4 m1;
AZ::Matrix4x4 m2;
AZ::Quaternion q1;
AZ::Vector4 v1;
AZ::Vector3 v2;
};
std::vector<TestData> m_testDataArray;
};
BENCHMARK_F(BM_MathMatrix4x4, CreateIdentity)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
AZ::Matrix4x4 result = AZ::Matrix4x4::CreateIdentity();
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathMatrix4x4, CreateZero)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
AZ::Matrix4x4 result = AZ::Matrix4x4::CreateZero();
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathMatrix4x4, GetRowX4)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
AZ::Vector4 result = testData.m1.GetRow(0);
benchmark::DoNotOptimize(result);
result = testData.m1.GetRow(1);
benchmark::DoNotOptimize(result);
result = testData.m1.GetRow(2);
benchmark::DoNotOptimize(result);
result = testData.m1.GetRow(3);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathMatrix4x4, GetColumnX3)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
AZ::Vector4 result = testData.m1.GetColumn(0);
benchmark::DoNotOptimize(result);
result = testData.m1.GetColumn(1);
benchmark::DoNotOptimize(result);
result = testData.m1.GetColumn(2);
benchmark::DoNotOptimize(result);
result = testData.m1.GetColumn(3);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathMatrix4x4, CreateFromValue)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
AZ::Matrix4x4 result = AZ::Matrix4x4::CreateFromValue(1.0f);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathMatrix4x4, CreateFromRowMajorFloat16)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
AZ::Matrix4x4 result = AZ::Matrix4x4::CreateFromRowMajorFloat16(s_mat4x4testArray);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathMatrix4x4, CreateFromColumnMajorFloat9)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
AZ::Matrix4x4 result = AZ::Matrix4x4::CreateFromColumnMajorFloat16(s_mat4x4testArray);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathMatrix4x4, CreateProjection)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
AZ::Matrix4x4 result = AZ::Matrix4x4::CreateProjection(s_mat4x4testArray[0], s_mat4x4testArray[1], s_mat4x4testArray[2], s_mat4x4testArray[3]);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathMatrix4x4, CreateProjectionFov)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
AZ::Matrix4x4 result = AZ::Matrix4x4::CreateProjectionFov(s_mat4x4testArray[0], s_mat4x4testArray[1], s_mat4x4testArray[2], s_mat4x4testArray[3]);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathMatrix4x4, CreateInterpolated)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
AZ::Matrix4x4 result = AZ::Matrix4x4::CreateInterpolated(testData.m1, testData.m2, testData.v1.GetX());
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathMatrix4x4, StoreToRowMajorFloat16)(benchmark::State& state)
{
float storeValues[16];
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
testData.m1.StoreToRowMajorFloat16(storeValues);
benchmark::DoNotOptimize(storeValues);
}
}
}
BENCHMARK_F(BM_MathMatrix4x4, StoreToColumnMajorFloat16)(benchmark::State& state)
{
float storeValues[16];
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
testData.m1.StoreToColumnMajorFloat16(storeValues);
benchmark::DoNotOptimize(storeValues);
}
}
}
BENCHMARK_F(BM_MathMatrix4x4, CreateRotationX)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
AZ::Matrix4x4 result = AZ::Matrix4x4::CreateRotationX(testData.v1.GetX());
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathMatrix4x4, CreateRotationY)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
AZ::Matrix4x4 result = AZ::Matrix4x4::CreateRotationY(testData.v1.GetX());
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathMatrix4x4, CreateRotationZ)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
AZ::Matrix4x4 result = AZ::Matrix4x4::CreateRotationZ(testData.v1.GetX());
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathMatrix4x4, CreateFromQuaternion)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
AZ::Matrix4x4 result = AZ::Matrix4x4::CreateFromQuaternion(testData.q1);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathMatrix4x4, CreateScale)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
AZ::Matrix4x4 result = AZ::Matrix4x4::CreateScale(testData.v2);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathMatrix4x4, CreateDiagonal)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
AZ::Matrix4x4 result = AZ::Matrix4x4::CreateDiagonal(testData.v1);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathMatrix4x4, GetElement)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
float result = testData.m1.GetElement(1, 2);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathMatrix4x4, SetElement)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
AZ::Matrix4x4 testMatrix = testData.m2;
testMatrix.SetElement(1, 2, -5.0f);
benchmark::DoNotOptimize(testMatrix);
}
}
}
BENCHMARK_F(BM_MathMatrix4x4, SetRowX3)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
AZ::Matrix4x4 testMatrix = testData.m2;
testMatrix.SetRow(0, testData.v1);
testMatrix.SetRow(1, testData.v1);
testMatrix.SetRow(2, testData.v1);
testMatrix.SetRow(3, testData.v1);
benchmark::DoNotOptimize(testMatrix);
}
}
}
BENCHMARK_F(BM_MathMatrix4x4, SetColumnX3)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
AZ::Matrix4x4 testMatrix = testData.m2;
testMatrix.SetColumn(0, testData.v1);
testMatrix.SetColumn(1, testData.v1);
testMatrix.SetColumn(2, testData.v1);
testMatrix.SetColumn(3, testData.v1);
benchmark::DoNotOptimize(testMatrix);
}
}
}
BENCHMARK_F(BM_MathMatrix4x4, GetBasisX)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
AZ::Vector4 result = testData.m1.GetBasisX();
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathMatrix4x4, GetBasisY)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
AZ::Vector4 result = testData.m1.GetBasisY();
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathMatrix4x4, GetBasisZ)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
AZ::Vector4 result = testData.m1.GetBasisZ();
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathMatrix4x4, CreateTranslation)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
AZ::Matrix4x4 result = AZ::Matrix4x4::CreateTranslation(testData.v2);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathMatrix4x4, SetTranslation)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
AZ::Matrix4x4 testMatrix = testData.m1;
testMatrix.SetTranslation(testData.v2);
benchmark::DoNotOptimize(testMatrix);
}
}
}
BENCHMARK_F(BM_MathMatrix4x4, GetTranslation)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
AZ::Vector3 result = testData.m1.GetTranslation();
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathMatrix4x4, OperatorAssign)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
AZ::Matrix4x4 result = testData.m1;
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathMatrix4x4, OperatorMultiply)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
AZ::Matrix4x4 result = testData.m1 * testData.m2;
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathMatrix4x4, OperatorMultiplyAssign)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
AZ::Matrix4x4 testMatrix = testData.m2;
testMatrix *= testData.m1;
benchmark::DoNotOptimize(testMatrix);
}
}
}
BENCHMARK_F(BM_MathMatrix4x4, OperatorMultiplyVector3)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
AZ::Vector3 result = testData.m1 * testData.v2;
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathMatrix4x4, OperatorMultiplyVector4)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
AZ::Vector4 result = testData.m1 * testData.v1;
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathMatrix4x4, TransposedMultiply3x3)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
AZ::Vector3 result = testData.m1.TransposedMultiply3x3(testData.v2);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathMatrix4x4, Multiply3x3)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
AZ::Vector3 result = testData.m1.Multiply3x3(testData.v2);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathMatrix4x4, Transpose)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
AZ::Matrix4x4 testMatrix = testData.m1;
testMatrix.Transpose();
benchmark::DoNotOptimize(testMatrix);
}
}
}
BENCHMARK_F(BM_MathMatrix4x4, GetTranspose)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
AZ::Matrix4x4 result = testData.m1.GetTranspose();
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathMatrix4x4, GetInverseFast)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
AZ::Matrix4x4 result = testData.m1.GetInverseFast();
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathMatrix4x4, GetInverseTransform)(benchmark::State& state)
{
// use a nonsingular matrix that has (0,0,0,1) for its final row to avoid asserts
AZ::Matrix4x4 mat = AZ::Matrix4x4::CreateRotationX(1.0f);
mat.SetElement(0, 1, 23.1234f);
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
AZ::Matrix4x4 result = mat.GetInverseTransform();
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathMatrix4x4, GetInverseFull)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
AZ::Matrix4x4 result = testData.m1.GetInverseFull();
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathMatrix4x4, SetRotationPartFromQuaternion)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
testData.m2.SetRotationPartFromQuaternion(testData.q1);
}
}
}
}
#endif
@@ -0,0 +1,371 @@
/*
* 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/Matrix3x4.h>
#include <AzCore/Math/Matrix4x4.h>
#include <AzCore/Math/Quaternion.h>
#include <AzCore/Math/Transform.h>
#include <AzCore/UnitTest/TestTypes.h>
#include <AZTestShared/Math/MathTestHelpers.h>
using namespace AZ;
namespace UnitTest
{
static constexpr int testIndices4x4[] = { 0, 1, 2, 3 };
TEST(MATH_Matrix4x4, TestCreate)
{
Matrix4x4 m1 = Matrix4x4::CreateIdentity();
AZ_TEST_ASSERT(m1.GetRow(0) == Vector4(1.0f, 0.0f, 0.0f, 0.0f));
AZ_TEST_ASSERT(m1.GetRow(1) == Vector4(0.0f, 1.0f, 0.0f, 0.0f));
AZ_TEST_ASSERT(m1.GetRow(2) == Vector4(0.0f, 0.0f, 1.0f, 0.0f));
AZ_TEST_ASSERT(m1.GetRow(3) == Vector4(0.0f, 0.0f, 0.0f, 1.0f));
m1 = Matrix4x4::CreateZero();
AZ_TEST_ASSERT(m1.GetRow(0) == Vector4(0.0f));
AZ_TEST_ASSERT(m1.GetRow(1) == Vector4(0.0f));
AZ_TEST_ASSERT(m1.GetRow(2) == Vector4(0.0f));
AZ_TEST_ASSERT(m1.GetRow(3) == Vector4(0.0f));
m1 = Matrix4x4::CreateFromValue(2.0f);
AZ_TEST_ASSERT(m1.GetRow(0) == Vector4(2.0f));
AZ_TEST_ASSERT(m1.GetRow(1) == Vector4(2.0f));
AZ_TEST_ASSERT(m1.GetRow(2) == Vector4(2.0f));
AZ_TEST_ASSERT(m1.GetRow(3) == Vector4(2.0f));
m1 = Matrix4x4::CreateScale(Vector3(1.0f, 2.0f, 3.0f));
AZ_TEST_ASSERT(m1.GetRow(0) == Vector4(1.0f, 0.0f, 0.0f, 0.0f));
AZ_TEST_ASSERT(m1.GetRow(1) == Vector4(0.0f, 2.0f, 0.0f, 0.0f));
AZ_TEST_ASSERT(m1.GetRow(2) == Vector4(0.0f, 0.0f, 3.0f, 0.0f));
AZ_TEST_ASSERT(m1.GetRow(3) == Vector4(0.0f, 0.0f, 0.0f, 1.0f));
m1 = Matrix4x4::CreateDiagonal(Vector4(2.0f, 3.0f, 4.0f, 5.0f));
AZ_TEST_ASSERT(m1.GetRow(0) == Vector4(2.0f, 0.0f, 0.0f, 0.0f));
AZ_TEST_ASSERT(m1.GetRow(1) == Vector4(0.0f, 3.0f, 0.0f, 0.0f));
AZ_TEST_ASSERT(m1.GetRow(2) == Vector4(0.0f, 0.0f, 4.0f, 0.0f));
AZ_TEST_ASSERT(m1.GetRow(3) == Vector4(0.0f, 0.0f, 0.0f, 5.0f));
m1 = Matrix4x4::CreateTranslation(Vector3(1.0f, 2.0f, 3.0f));
AZ_TEST_ASSERT(m1.GetRow(0) == Vector4(1.0f, 0.0f, 0.0f, 1.0f));
AZ_TEST_ASSERT(m1.GetRow(1) == Vector4(0.0f, 1.0f, 0.0f, 2.0f));
AZ_TEST_ASSERT(m1.GetRow(2) == Vector4(0.0f, 0.0f, 1.0f, 3.0f));
AZ_TEST_ASSERT(m1.GetRow(3) == Vector4(0.0f, 0.0f, 0.0f, 1.0f));
}
TEST(MATH_Matrix4x4, TestCreateFrom)
{
float testFloats[] =
{
1.0f, 2.0f, 3.0f, 4.0f,
5.0f, 6.0f, 7.0f, 8.0f,
9.0f, 10.0f, 11.0f, 12.0f,
13.0f, 14.0f, 15.0f, 16.0f
};
float testFloatMtx[16];
Matrix4x4 m1 = Matrix4x4::CreateFromRowMajorFloat16(testFloats);
AZ_TEST_ASSERT(m1.GetRow(0) == Vector4(1.0f, 2.0f, 3.0f, 4.0f));
AZ_TEST_ASSERT(m1.GetRow(1) == Vector4(5.0f, 6.0f, 7.0f, 8.0f));
AZ_TEST_ASSERT(m1.GetRow(2) == Vector4(9.0f, 10.0f, 11.0f, 12.0f));
AZ_TEST_ASSERT(m1.GetRow(3) == Vector4(13.0f, 14.0f, 15.0f, 16.0f));
m1.StoreToRowMajorFloat16(testFloatMtx);
AZ_TEST_ASSERT(memcmp(testFloatMtx, testFloats, sizeof(testFloatMtx)) == 0);
m1 = Matrix4x4::CreateFromColumnMajorFloat16(testFloats);
AZ_TEST_ASSERT(m1.GetRow(0) == Vector4(1.0f, 5.0f, 9.0f, 13.0f));
AZ_TEST_ASSERT(m1.GetRow(1) == Vector4(2.0f, 6.0f, 10.0f, 14.0f));
AZ_TEST_ASSERT(m1.GetRow(2) == Vector4(3.0f, 7.0f, 11.0f, 15.0f));
AZ_TEST_ASSERT(m1.GetRow(3) == Vector4(4.0f, 8.0f, 12.0f, 16.0f));
m1.StoreToColumnMajorFloat16(testFloatMtx);
AZ_TEST_ASSERT(memcmp(testFloatMtx, testFloats, sizeof(testFloatMtx)) == 0);
}
TEST(MATH_Matrix4x4, TestCreateFromMatrix3x4)
{
const Vector3 translation(2.0f, 3.0f, 4.0f);
const Quaternion rotation(0.62f, 0.62f, 0.14f, 0.46f);
const Matrix3x4 matrix3x4 = Matrix3x4::CreateFromQuaternionAndTranslation(rotation, translation);
const Matrix4x4 matrix4x4 = Matrix4x4::CreateFromMatrix3x4(matrix3x4);
EXPECT_THAT(matrix4x4.GetTranslation(), IsClose(translation));
EXPECT_THAT(matrix4x4.GetRow(0), IsClose(AZ::Vector4(0.192f, 0.64f, 0.744f, 2.0f)));
EXPECT_THAT(matrix4x4.GetRow(1), IsClose(AZ::Vector4(0.8976f, 0.192f, -0.3968f, 3.0f)));
EXPECT_THAT(matrix4x4.GetRow(2), IsClose(AZ::Vector4(-0.3968f, 0.744f, -0.5376f, 4.0f)));
EXPECT_THAT(matrix4x4.GetRow(3), IsClose(AZ::Vector4::CreateAxisW()));
}
TEST(MATH_Matrix4x4, TestCreateFromTransform)
{
const Vector3 translation(2.0f, 3.0f, 4.0f);
const Quaternion rotation(0.62f, 0.62f, 0.14f, 0.46f);
const Transform transform = Transform::CreateFromQuaternionAndTranslation(rotation, translation);
const Matrix4x4 matrix4x4 = Matrix4x4::CreateFromTransform(transform);
EXPECT_THAT(matrix4x4.GetTranslation(), IsClose(translation));
EXPECT_THAT(matrix4x4.GetRow(0), IsClose(AZ::Vector4(0.192f, 0.64f, 0.744f, 2.0f)));
EXPECT_THAT(matrix4x4.GetRow(1), IsClose(AZ::Vector4(0.8976f, 0.192f, -0.3968f, 3.0f)));
EXPECT_THAT(matrix4x4.GetRow(2), IsClose(AZ::Vector4(-0.3968f, 0.744f, -0.5376f, 4.0f)));
EXPECT_THAT(matrix4x4.GetRow(3), IsClose(AZ::Vector4::CreateAxisW()));
}
TEST(MATH_Matrix4x4, TestCreateRotation)
{
Matrix4x4 m1 = Matrix4x4::CreateRotationX(DegToRad(30.0f));
AZ_TEST_ASSERT(m1.GetRow(0).IsClose(Vector4(1.0f, 0.0f, 0.0f, 0.0f)));
AZ_TEST_ASSERT(m1.GetRow(1).IsClose(Vector4(0.0f, 0.866f, -0.5f, 0.0f)));
AZ_TEST_ASSERT(m1.GetRow(2).IsClose(Vector4(0.0f, 0.5f, 0.866f, 0.0f)));
AZ_TEST_ASSERT(m1.GetRow(3).IsClose(Vector4(0.0f, 0.0f, 0.0f, 1.0f)));
m1 = Matrix4x4::CreateRotationY(DegToRad(30.0f));
AZ_TEST_ASSERT(m1.GetRow(0).IsClose(Vector4(0.866f, 0.0f, 0.5f, 0.0f)));
AZ_TEST_ASSERT(m1.GetRow(1).IsClose(Vector4(0.0f, 1.0f, 0.0f, 0.0f)));
AZ_TEST_ASSERT(m1.GetRow(2).IsClose(Vector4(-0.5f, 0.0f, 0.866f, 0.0f)));
AZ_TEST_ASSERT(m1.GetRow(3).IsClose(Vector4(0.0f, 0.0f, 0.0f, 1.0f)));
m1 = Matrix4x4::CreateRotationZ(DegToRad(30.0f));
AZ_TEST_ASSERT(m1.GetRow(0).IsClose(Vector4(0.866f, -0.5f, 0.0f, 0.0f)));
AZ_TEST_ASSERT(m1.GetRow(1).IsClose(Vector4(0.5f, 0.866f, 0.0f, 0.0f)));
AZ_TEST_ASSERT(m1.GetRow(2).IsClose(Vector4(0.0f, 0.0f, 1.0f, 0.0f)));
AZ_TEST_ASSERT(m1.GetRow(3).IsClose(Vector4(0.0f, 0.0f, 0.0f, 1.0f)));
m1 = Matrix4x4::CreateFromQuaternion(AZ::Quaternion::CreateRotationX(DegToRad(30.0f)));
AZ_TEST_ASSERT(m1.GetRow(0).IsClose(Vector4(1.0f, 0.0f, 0.0f, 0.0f)));
AZ_TEST_ASSERT(m1.GetRow(1).IsClose(Vector4(0.0f, 0.866f, -0.5f, 0.0f)));
AZ_TEST_ASSERT(m1.GetRow(2).IsClose(Vector4(0.0f, 0.5f, 0.866f, 0.0f)));
AZ_TEST_ASSERT(m1.GetRow(3).IsClose(Vector4(0.0f, 0.0f, 0.0f, 1.0f)));
m1 = Matrix4x4::CreateFromQuaternionAndTranslation(AZ::Quaternion::CreateRotationX(DegToRad(30.0f)), Vector3(1.0f, 2.0f, 3.0f));
AZ_TEST_ASSERT(m1.GetRow(0).IsClose(Vector4(1.0f, 0.0f, 0.0f, 1.0f)));
AZ_TEST_ASSERT(m1.GetRow(1).IsClose(Vector4(0.0f, 0.866f, -0.5f, 2.0f)));
AZ_TEST_ASSERT(m1.GetRow(2).IsClose(Vector4(0.0f, 0.5f, 0.866f, 3.0f)));
AZ_TEST_ASSERT(m1.GetRow(3).IsClose(Vector4(0.0f, 0.0f, 0.0f, 1.0f)));
}
TEST(MATH_Matrix4x4, TestCreateProjection)
{
Matrix4x4 m1 = Matrix4x4::CreateProjection(DegToRad(30.0f), 16.0f / 9.0f, 1.0f, 1000.0f);
AZ_TEST_ASSERT(m1.GetRow(0).IsClose(Vector4(-2.099279f, 0.0f, 0.0f, 0.0f)));
AZ_TEST_ASSERT(m1.GetRow(1).IsClose(Vector4(0.0f, 3.732f, 0.0f, 0.0f)));
AZ_TEST_ASSERT(m1.GetRow(2).IsClose(Vector4(0.0f, 0.0f, 1.002f, -2.002f)));
AZ_TEST_ASSERT(m1.GetRow(3).IsClose(Vector4(0.0f, 0.0f, 1.0f, 0.0f)));
m1 = Matrix4x4::CreateProjectionFov(DegToRad(30.0f), DegToRad(60.0f), 1.0f, 1000.0f);
AZ_TEST_ASSERT(m1.GetRow(0).IsClose(Vector4(-3.732f, 0.0f, 0.0f, 0.0f)));
AZ_TEST_ASSERT(m1.GetRow(1).IsClose(Vector4(0.0f, 1.732f, 0.0f, 0.0f)));
AZ_TEST_ASSERT(m1.GetRow(2).IsClose(Vector4(0.0f, 0.0f, 1.002f, -2.002f)));
AZ_TEST_ASSERT(m1.GetRow(3).IsClose(Vector4(0.0f, 0.0f, 1.0f, 0.0f)));
m1 = Matrix4x4::CreateProjectionOffset(0.5f, 1.0f, 0.0f, 0.5f, 1.0f, 1000.0f);
AZ_TEST_ASSERT(m1.GetRow(0).IsClose(Vector4(-4.0f, 0.0f, -3.0f, 0.0f)));
AZ_TEST_ASSERT(m1.GetRow(1).IsClose(Vector4(0.0f, 4.0f, -1.0f, 0.0f)));
AZ_TEST_ASSERT(m1.GetRow(2).IsClose(Vector4(0.0f, 0.0f, 1.002f, -2.002f)));
AZ_TEST_ASSERT(m1.GetRow(3).IsClose(Vector4(0.0f, 0.0f, 1.0f, 0.0f)));
}
TEST(MATH_Matrix4x4, TestElementAccess)
{
Matrix4x4 m1 = Matrix4x4::CreateRotationX(DegToRad(30.0f));
AZ_TEST_ASSERT_FLOAT_CLOSE(m1.GetElement(1, 2), -0.5f);
AZ_TEST_ASSERT_FLOAT_CLOSE(m1.GetElement(2, 2), 0.866f);
m1.SetElement(2, 1, 5.0f);
AZ_TEST_ASSERT(m1.GetElement(2, 1) == 5.0f);
}
TEST(MATH_Matrix4x4, TestIndexAccessors)
{
Matrix4x4 m1 = Matrix4x4::CreateRotationX(DegToRad(30.0f));
AZ_TEST_ASSERT_FLOAT_CLOSE(m1(1, 2), -0.5f);
AZ_TEST_ASSERT_FLOAT_CLOSE(m1(2, 2), 0.866f);
m1.SetElement(2, 1, 15.0f);
AZ_TEST_ASSERT(m1(2, 1) == 15.0f);
}
TEST(MATH_Matrix4x4, TestRowAccess)
{
Matrix4x4 m1 = Matrix4x4::CreateRotationX(DegToRad(30.0f));
AZ_TEST_ASSERT(m1.GetRow(2).IsClose(Vector4(0.0f, 0.5f, 0.866f, 0.0f)));
AZ_TEST_ASSERT(m1.GetRowAsVector3(2).IsClose(Vector3(0.0f, 0.5f, 0.866f)));
m1.SetRow(0, 1.0f, 2.0f, 3.0f, 4.0f);
AZ_TEST_ASSERT(m1.GetRow(0).IsClose(Vector4(1.0f, 2.0f, 3.0f, 4.0f)));
m1.SetRow(1, Vector3(5.0f, 6.0f, 7.0f), 8.0f);
AZ_TEST_ASSERT(m1.GetRow(1).IsClose(Vector4(5.0f, 6.0f, 7.0f, 8.0f)));
m1.SetRow(2, Vector4(3.0f, 4.0f, 5.0f, 6.0));
AZ_TEST_ASSERT(m1.GetRow(2).IsClose(Vector4(3.0f, 4.0f, 5.0f, 6.0f)));
m1.SetRow(3, Vector4(7.0f, 8.0f, 9.0f, 10.0));
AZ_TEST_ASSERT(m1.GetRow(3).IsClose(Vector4(7.0f, 8.0f, 9.0f, 10.0f)));
//test GetRow with non-constant, we have different implementations for constants and variables
AZ_TEST_ASSERT(m1.GetRow(testIndices4x4[0]).IsClose(Vector4(1.0f, 2.0f, 3.0f, 4.0f)));
AZ_TEST_ASSERT(m1.GetRow(testIndices4x4[1]).IsClose(Vector4(5.0f, 6.0f, 7.0f, 8.0f)));
AZ_TEST_ASSERT(m1.GetRow(testIndices4x4[2]).IsClose(Vector4(3.0f, 4.0f, 5.0f, 6.0f)));
AZ_TEST_ASSERT(m1.GetRow(testIndices4x4[3]).IsClose(Vector4(7.0f, 8.0f, 9.0f, 10.0f)));
}
TEST(MATH_Matrix4x4, TestColumnAccess)
{
Matrix4x4 m1 = Matrix4x4::CreateRotationX(DegToRad(30.0f));
AZ_TEST_ASSERT(m1.GetColumn(1).IsClose(Vector4(0.0f, 0.866f, 0.5f, 0.0f)));
m1.SetColumn(3, 1.0f, 2.0f, 3.0f, 4.0f);
AZ_TEST_ASSERT(m1.GetColumn(3).IsClose(Vector4(1.0f, 2.0f, 3.0f, 4.0f)));
AZ_TEST_ASSERT(m1.GetColumnAsVector3(3).IsClose(Vector3(1.0f, 2.0f, 3.0f)));
AZ_TEST_ASSERT(m1.GetRow(0).IsClose(Vector4(1.0f, 0.0f, 0.0f, 1.0f))); //checking all components in case others get messed up with the shuffling
AZ_TEST_ASSERT(m1.GetRow(1).IsClose(Vector4(0.0f, 0.866f, -0.5f, 2.0f)));
AZ_TEST_ASSERT(m1.GetRow(2).IsClose(Vector4(0.0f, 0.5f, 0.866f, 3.0f)));
AZ_TEST_ASSERT(m1.GetRow(3).IsClose(Vector4(0.0f, 0.0f, 0.0f, 4.0f)));
m1.SetColumn(0, Vector4(2.0f, 3.0f, 4.0f, 5.0f));
AZ_TEST_ASSERT(m1.GetColumn(0).IsClose(Vector4(2.0f, 3.0f, 4.0f, 5.0f)));
AZ_TEST_ASSERT(m1.GetRow(0).IsClose(Vector4(2.0f, 0.0f, 0.0f, 1.0f)));
AZ_TEST_ASSERT(m1.GetRow(1).IsClose(Vector4(3.0f, 0.866f, -0.5f, 2.0f)));
AZ_TEST_ASSERT(m1.GetRow(2).IsClose(Vector4(4.0f, 0.5f, 0.866f, 3.0f)));
AZ_TEST_ASSERT(m1.GetRow(3).IsClose(Vector4(5.0f, 0.0f, 0.0f, 4.0f)));
//test GetColumn with non-constant, we have different implementations for constants and variables
AZ_TEST_ASSERT(m1.GetColumn(testIndices4x4[0]).IsClose(Vector4(2.0f, 3.0f, 4.0f, 5.0f)));
AZ_TEST_ASSERT(m1.GetColumn(testIndices4x4[1]).IsClose(Vector4(0.0f, 0.866f, 0.5f, 0.0f)));
AZ_TEST_ASSERT(m1.GetColumn(testIndices4x4[2]).IsClose(Vector4(0.0f, -0.5f, 0.866f, 0.0f)));
AZ_TEST_ASSERT(m1.GetColumn(testIndices4x4[3]).IsClose(Vector4(1.0f, 2.0f, 3.0f, 4.0f)));
}
TEST(MATH_Matrix4x4, TestTranslationAccess)
{
Matrix4x4 m1 = Matrix4x4::CreateTranslation(Vector3(5.0f, 6.0f, 7.0f));
AZ_TEST_ASSERT(m1.GetTranslation().IsClose(Vector3(5.0f, 6.0f, 7.0f)));
m1.SetTranslation(1.0f, 2.0f, 3.0f);
AZ_TEST_ASSERT(m1.GetTranslation().IsClose(Vector3(1.0f, 2.0f, 3.0f)));
m1.SetTranslation(Vector3(2.0f, 3.0f, 4.0f));
AZ_TEST_ASSERT(m1.GetTranslation().IsClose(Vector3(2.0f, 3.0f, 4.0f)));
m1.SetTranslation(4.0f, 5.0f, 6.0f);
AZ_TEST_ASSERT(m1.GetTranslation().IsClose(Vector3(4.0f, 5.0f, 6.0f)));
m1.SetTranslation(Vector3(2.0f, 3.0f, 4.0f));
AZ_TEST_ASSERT(m1.GetTranslation().IsClose(Vector3(2.0f, 3.0f, 4.0f)));
}
TEST(MATH_Matrix4x4, TestMatrixMultiplication)
{
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;
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)));
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)));
}
TEST(MATH_Matrix4x4, TestVectorMultiplication)
{
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);
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)));
Vector3 v1(1.0f, 2.0f, 3.0f);
AZ_TEST_ASSERT((v1 * m1).IsClose(Vector3(51.0f, 58.0f, 65.0f)));
v1 *= m1;
AZ_TEST_ASSERT(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)));
v2 *= m1;
AZ_TEST_ASSERT(v2.IsClose(Vector4(90.0f, 100.0f, 110.0f, 120.0f)));
}
TEST(MATH_Matrix4x4, TestTranspose)
{
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 = m1.GetTranspose();
AZ_TEST_ASSERT(m2.GetRow(0).IsClose(Vector4(1.0f, 5.0f, 9.0f, 13.0f)));
AZ_TEST_ASSERT(m2.GetRow(1).IsClose(Vector4(2.0f, 6.0f, 10.0f, 14.0f)));
AZ_TEST_ASSERT(m2.GetRow(2).IsClose(Vector4(3.0f, 7.0f, 11.0f, 15.0f)));
AZ_TEST_ASSERT(m2.GetRow(3).IsClose(Vector4(4.0f, 8.0f, 12.0f, 16.0f)));
m2 = m1;
m2.Transpose();
AZ_TEST_ASSERT(m2.GetRow(0).IsClose(Vector4(1.0f, 5.0f, 9.0f, 13.0f)));
AZ_TEST_ASSERT(m2.GetRow(1).IsClose(Vector4(2.0f, 6.0f, 10.0f, 14.0f)));
AZ_TEST_ASSERT(m2.GetRow(2).IsClose(Vector4(3.0f, 7.0f, 11.0f, 15.0f)));
AZ_TEST_ASSERT(m2.GetRow(3).IsClose(Vector4(4.0f, 8.0f, 12.0f, 16.0f)));
}
TEST(MATH_Matrix4x4, TestFastInverse)
{
Matrix4x4 m1;
m1 = Matrix4x4::CreateRotationX(1.0f);
m1.SetTranslation(Vector3(10.0f, -3.0f, 5.0f));
AZ_TEST_ASSERT((m1 * m1.GetInverseFast()).IsClose(Matrix4x4::CreateIdentity(), 0.02f));
Matrix4x4 m2 = Matrix4x4::CreateRotationZ(2.0f) * Matrix4x4::CreateRotationX(1.0f);
m2.SetTranslation(Vector3(-5.0f, 4.2f, -32.0f));
Matrix4x4 m3 = m2.GetInverseFast();
// allow a little bigger threshold, because of the 2 rot matrices (sin,cos differences)
AZ_TEST_ASSERT((m2 * m3).IsClose(Matrix4x4::CreateIdentity(), 0.1f));
AZ_TEST_ASSERT(m3.GetRow(0).IsClose(Vector4(-0.420f, 0.909f, 0.0f, -5.920f), 0.06f));
AZ_TEST_ASSERT(m3.GetRow(1).IsClose(Vector4(-0.493f, -0.228f, 0.841f, 25.418f), 0.06f));
AZ_TEST_ASSERT(m3.GetRow(2).IsClose(Vector4(0.765f, 0.353f, 0.542f, 19.703f), 0.06f));
AZ_TEST_ASSERT(m3.GetRow(3).IsClose(Vector4(0.0f, 0.0f, 0.0f, 1.0f)));
}
TEST(MATH_Matrix4x4, TestTransformInverse)
{
// should handle non-orthogonal matrices, last row must still be (0,0,0,1)
Matrix4x4 m1;
m1 = Matrix4x4::CreateRotationX(1.0f);
m1.SetTranslation(Vector3(10.0f, -3.0f, 5.0f));
m1.SetElement(0, 1, 23.1234f);
AZ_TEST_ASSERT((m1 * m1.GetInverseTransform()).IsClose(Matrix4x4::CreateIdentity(), 0.01f));
}
TEST(MATH_Matrix4x4, TestFullInverse)
{
Matrix4x4 m1;
m1.SetRow(0, -1.0f, 2.0f, 3.0f, 4.0f);
m1.SetRow(1, 3.0f, 4.0f, 5.0f, 6.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 * m1.GetInverseFull()).IsClose(Matrix4x4::CreateIdentity()));
}
TEST(MATH_Matrix4x4, TestIsClose)
{
Matrix4x4 m1 = Matrix4x4::CreateRotationX(DegToRad(30.0f));
Matrix4x4 m2 = m1;
AZ_TEST_ASSERT(m1.IsClose(m2));
m2.SetElement(0, 0, 2.0f);
AZ_TEST_ASSERT(!m1.IsClose(m2));
m2 = m1;
m2.SetElement(0, 3, 2.0f);
AZ_TEST_ASSERT(!m1.IsClose(m2));
}
TEST(MATH_Matrix4x4, TestSetRotationPart)
{
Matrix4x4 m1 = Matrix4x4::CreateTranslation(Vector3(1.0f, 2.0f, 3.0f));
m1.SetRow(3, 5.0f, 6.0f, 7.0f, 8.0f);
m1.SetRotationPartFromQuaternion(AZ::Quaternion::CreateRotationX(DegToRad(30.0f)));
AZ_TEST_ASSERT(m1.GetRow(0).IsClose(Vector4(1.0f, 0.0f, 0.0f, 1.0f)));
AZ_TEST_ASSERT(m1.GetRow(1).IsClose(Vector4(0.0f, 0.866f, -0.5f, 2.0f)));
AZ_TEST_ASSERT(m1.GetRow(2).IsClose(Vector4(0.0f, 0.5f, 0.866f, 3.0f)));
AZ_TEST_ASSERT(m1.GetRow(3).IsClose(Vector4(5.0f, 6.0f, 7.0f, 8.0f)));
}
TEST(MATH_Matrix4x4, TestGetDiagonal)
{
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);
AZ_TEST_ASSERT(m1.GetDiagonal() == Vector4(1.0f, 6.0f, 11.0f, 16.0f));
}
}
@@ -0,0 +1,227 @@
/*
* 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/MatrixUtils.h>
#include <AzCore/UnitTest/TestTypes.h>
namespace UnitTest
{
using namespace AZ;
class MatrixUtilsTests : public AllocatorsTestFixture
{
protected:
const float floatEpsilon = 0.001f;
};
TEST_F(MatrixUtilsTests, PerspectiveMatrixFovRH)
{
Matrix4x4 matrix;
constexpr float near_value = 10;
constexpr float far_value = 1000;
float fovY = Constants::HalfPi;
float aspectRatio = 1.f;
auto matPtr = MakePerspectiveFovMatrixRH(matrix, fovY, aspectRatio, near_value, far_value);
EXPECT_NE(matPtr, nullptr);
EXPECT_EQ(matPtr, &matrix);
// transform some positions and valid the output
// point on near plane
Vector3 nearPos(0, 0, -near_value);
Vector3 result = MatrixTransformPosition(matrix, nearPos);
EXPECT_TRUE(AZ::IsClose(result.GetZ(), 0, floatEpsilon));
// point on far plane
Vector3 farPos(0, 0, -far_value);
result = MatrixTransformPosition(matrix, farPos);
EXPECT_TRUE(AZ::IsClose(result.GetZ(), 1, floatEpsilon));
// point further than far plane
Vector3 furtherFarPos(0, 0, -far_value - 1000);
result = MatrixTransformPosition(matrix, furtherFarPos);
EXPECT_TRUE(result.GetZ() > 1.f);
// point closer then near plane
Vector3 closerNearPos(0, 0, -near_value + 5);
result = MatrixTransformPosition(matrix, closerNearPos);
EXPECT_TRUE(result.GetZ() < 0.f);
// point between near and far
Vector3 betweenPos(0, 0, -(far_value + near_value) / 2);
result = MatrixTransformPosition(matrix, betweenPos);
EXPECT_TRUE(result.GetZ() > 0.f);
EXPECT_TRUE(result.GetZ() < 1.f);
// x and y value
Vector3 insideLeftTopPos(-900, 900, -far_value);
Vector3 insideRightBottomPos(900, -900, -far_value);
Vector3 outsideLeftTopPos(-1100, 1100, -far_value);
Vector3 outsideRightBottomPos(1100, -1100, -far_value);
result = MatrixTransformPosition(matrix, insideLeftTopPos);
EXPECT_TRUE(result.GetX() < 0);
EXPECT_TRUE(result.GetX() > -1);
EXPECT_TRUE(result.GetY() > 0);
EXPECT_TRUE(result.GetY() < 1);
result = MatrixTransformPosition(matrix, insideRightBottomPos);
EXPECT_TRUE(result.GetX() > 0);
EXPECT_TRUE(result.GetX() < 1);
EXPECT_TRUE(result.GetY() < 0);
EXPECT_TRUE(result.GetY() > -1);
result = MatrixTransformPosition(matrix, outsideLeftTopPos);
EXPECT_TRUE(result.GetX() < -1);
EXPECT_TRUE(result.GetY() > 1);
result = MatrixTransformPosition(matrix, outsideRightBottomPos);
EXPECT_TRUE(result.GetX() > 1);
EXPECT_TRUE(result.GetY() < -1);
// create a reverse depth perspective projection matrix
MakePerspectiveFovMatrixRH(matrix, fovY, aspectRatio, near_value, far_value, true);
result = MatrixTransformPosition(matrix, nearPos);
EXPECT_TRUE(AZ::IsClose(result.GetZ(), 1, floatEpsilon));
result = MatrixTransformPosition(matrix, farPos);
EXPECT_TRUE(AZ::IsClose(result.GetZ(), 0, floatEpsilon));
result = MatrixTransformPosition(matrix, furtherFarPos);
EXPECT_TRUE(result.GetZ() < 0.f);
result = MatrixTransformPosition(matrix, closerNearPos);
EXPECT_TRUE(result.GetZ() > 1.f);
result = MatrixTransformPosition(matrix, betweenPos);
EXPECT_TRUE(result.GetZ() > 0.f);
EXPECT_TRUE(result.GetZ() < 1.f);
result = MatrixTransformPosition(matrix, insideLeftTopPos);
EXPECT_TRUE(result.GetX() < 0);
EXPECT_TRUE(result.GetX() > -1);
EXPECT_TRUE(result.GetY() > 0);
EXPECT_TRUE(result.GetY() < 1);
result = MatrixTransformPosition(matrix, insideRightBottomPos);
EXPECT_TRUE(result.GetX() > 0);
EXPECT_TRUE(result.GetX() < 1);
EXPECT_TRUE(result.GetY() < 0);
EXPECT_TRUE(result.GetY() > -1);
result = MatrixTransformPosition(matrix, outsideLeftTopPos);
EXPECT_TRUE(result.GetX() < -1);
EXPECT_TRUE(result.GetY() > 1);
result = MatrixTransformPosition(matrix, outsideRightBottomPos);
EXPECT_TRUE(result.GetX() > 1);
EXPECT_TRUE(result.GetY() < -1);
// bad input
UnitTest::TestRunner::Instance().StartAssertTests();
EXPECT_FALSE(MakePerspectiveFovMatrixRH(matrix, fovY, -1, near_value, far_value));
EXPECT_EQ(1, UnitTest::TestRunner::Instance().m_numAssertsFailed);
EXPECT_FALSE(MakePerspectiveFovMatrixRH(matrix, 0, aspectRatio, near_value, far_value));
EXPECT_EQ(2, UnitTest::TestRunner::Instance().m_numAssertsFailed);
EXPECT_FALSE(MakePerspectiveFovMatrixRH(matrix, fovY, aspectRatio, -near_value, far_value));
EXPECT_EQ(3, UnitTest::TestRunner::Instance().m_numAssertsFailed);
EXPECT_FALSE(MakePerspectiveFovMatrixRH(matrix, fovY, aspectRatio, near_value, -far_value));
EXPECT_EQ(4, UnitTest::TestRunner::Instance().m_numAssertsFailed);
EXPECT_FALSE(MakePerspectiveFovMatrixRH(matrix, fovY, aspectRatio, 0, far_value));
EXPECT_EQ(5, UnitTest::TestRunner::Instance().m_numAssertsFailed);
UnitTest::TestRunner::Instance().StopAssertTests();
}
TEST_F(MatrixUtilsTests, OrthographicMatrixRH)
{
Matrix4x4 matrix;
auto ptr = MakeOrthographicMatrixRH(matrix, -100, 0, 100, 200, 0, 1000);
EXPECT_NE(ptr, nullptr);
EXPECT_EQ(ptr, &matrix);
Vector3 result;
// center position
result = MatrixTransformPosition(matrix, Vector3(-50, 150, -500));
EXPECT_TRUE(AZ::IsClose(result.GetX(), 0, floatEpsilon));
EXPECT_TRUE(AZ::IsClose(result.GetY(), 0, floatEpsilon));
EXPECT_TRUE(AZ::IsClose(result.GetZ(), 0.5f, floatEpsilon));
// left top and far
result = MatrixTransformPosition(matrix, Vector3(-100, 200, -1000));
EXPECT_TRUE(AZ::IsClose(result.GetX(), -1, floatEpsilon));
EXPECT_TRUE(AZ::IsClose(result.GetY(), 1, floatEpsilon));
EXPECT_TRUE(AZ::IsClose(result.GetZ(), 1, floatEpsilon));
// right bottom and near
result = MatrixTransformPosition(matrix, Vector3(0, 100, 0));
EXPECT_TRUE(AZ::IsClose(result.GetX(), 1, floatEpsilon));
EXPECT_TRUE(AZ::IsClose(result.GetY(), -1, floatEpsilon));
EXPECT_TRUE(AZ::IsClose(result.GetZ(), 0, floatEpsilon));
// further than far
result = MatrixTransformPosition(matrix, Vector3(-50, 150, -2000));
EXPECT_TRUE(result.GetZ() > 1);
// closer than near
result = MatrixTransformPosition(matrix, Vector3(-50, 150, 200));
EXPECT_TRUE(result.GetZ() < 0);
}
TEST_F(MatrixUtilsTests, FrustumMatrixRH)
{
Matrix4x4 matrix;
auto ptr = MakeFrustumMatrixRH(matrix, -100, 0, 100, 200, 1, 1000);
EXPECT_NE(ptr, nullptr);
EXPECT_EQ(ptr, &matrix);
Vector3 result;
// left top and near
Vector3 leftTopNear(-100, 200, -1);
result = MatrixTransformPosition(matrix, leftTopNear);
EXPECT_TRUE(AZ::IsClose(result.GetX(), -1, floatEpsilon));
EXPECT_TRUE(AZ::IsClose(result.GetY(), 1, floatEpsilon));
EXPECT_TRUE(AZ::IsClose(result.GetZ(), 0, floatEpsilon));
// right bottom and near
Vector3 rightBottomNear(0, 100, -1);
result = MatrixTransformPosition(matrix, rightBottomNear);
EXPECT_TRUE(AZ::IsClose(result.GetX(), 1, floatEpsilon));
EXPECT_TRUE(AZ::IsClose(result.GetY(), -1, floatEpsilon));
EXPECT_TRUE(AZ::IsClose(result.GetZ(), 0, floatEpsilon));
// further than far
Vector3 far_value(-50, 150, -1000);
result = MatrixTransformPosition(matrix, far_value);
EXPECT_TRUE(AZ::IsClose(result.GetZ(), 1, floatEpsilon));
Vector3 furtherFar(-50, 150, -2000);
result = MatrixTransformPosition(matrix, furtherFar);
EXPECT_TRUE(result.GetZ() > 1);
// closer than near
Vector3 closerNear(-50, 150, -0.5f);
result = MatrixTransformPosition(matrix, closerNear);
EXPECT_TRUE(result.GetZ() < 0);
// reverse depth
MakeFrustumMatrixRH(matrix, -100, 0, 100, 200, 1, 1000, true);
result = MatrixTransformPosition(matrix, leftTopNear);
EXPECT_TRUE(AZ::IsClose(result.GetX(), -1, floatEpsilon));
EXPECT_TRUE(AZ::IsClose(result.GetY(), 1, floatEpsilon));
result = MatrixTransformPosition(matrix, rightBottomNear);
EXPECT_TRUE(AZ::IsClose(result.GetX(), 1, floatEpsilon));
EXPECT_TRUE(AZ::IsClose(result.GetY(), -1, floatEpsilon));
EXPECT_TRUE(AZ::IsClose(result.GetZ(), 1, floatEpsilon));
result = MatrixTransformPosition(matrix, far_value);
EXPECT_TRUE(AZ::IsClose(result.GetZ(), 0, floatEpsilon));
// bad input
UnitTest::TestRunner::Instance().StartAssertTests();
// near = 0
EXPECT_FALSE(MakeFrustumMatrixRH(matrix, -100, 0, 100, 200, 0, 1000));
EXPECT_EQ(1, UnitTest::TestRunner::Instance().m_numAssertsFailed);
UnitTest::TestRunner::Instance().StopAssertTests();
}
}
@@ -0,0 +1,289 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyrightand 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.
*
*/
#if defined(HAVE_BENCHMARK)
#include <AzCore/Math/Obb.h>
#include <AzCore/Math/Aabb.h>
#include <AzCore/Math/Vector3.h>
#include <AzCore/Math/Transform.h>
#include <AzCore/UnitTest/TestTypes.h>
namespace Benchmark
{
class BM_MathObb
: public benchmark::Fixture
{
public:
void SetUp([[maybe_unused]] const ::benchmark::State& state) override
{
m_position.Set(1.0f, 2.0f, 3.0f);
m_rotation = AZ::Quaternion::CreateRotationZ(AZ::Constants::QuarterPi);
m_halfLengths = AZ::Vector3(0.5f);
m_obb = AZ::Obb::CreateFromPositionRotationAndHalfLengths(m_position, m_rotation, m_halfLengths);
}
AZ::Obb m_obb;
AZ::Vector3 m_position;
AZ::Quaternion m_rotation;
AZ::Vector3 m_halfLengths;
const int numIters = 1000;
};
BENCHMARK_F(BM_MathObb, CreateFromPositionRotationAndHalfLengths)(benchmark::State& state)
{
for (auto _ : state)
{
for (int i = 0; i < numIters; ++i)
{
AZ::Obb result =
AZ::Obb::CreateFromPositionRotationAndHalfLengths(m_position, m_rotation, m_halfLengths);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathObb, SetPosition)(benchmark::State& state)
{
for (auto _ : state)
{
for (int i = 0; i < numIters; ++i)
{
m_obb.SetPosition(m_position);
benchmark::DoNotOptimize(m_obb);
}
}
}
BENCHMARK_F(BM_MathObb, GetPosition)(benchmark::State& state)
{
for (auto _ : state)
{
for (int i = 0; i < numIters; ++i)
{
AZ::Vector3 result = m_obb.GetPosition();
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathObb, GetAxisX)(benchmark::State& state)
{
for (auto _ : state)
{
for (int i = 0; i < numIters; ++i)
{
AZ::Vector3 result = m_obb.GetAxisX();
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathObb, GetAxisY)(benchmark::State& state)
{
for (auto _ : state)
{
for (int i = 0; i < numIters; ++i)
{
AZ::Vector3 result = m_obb.GetAxisY();
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathObb, GetAxisZ)(benchmark::State& state)
{
for (auto _ : state)
{
for (int i = 0; i < numIters; ++i)
{
AZ::Vector3 result = m_obb.GetAxisZ();
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathObb, GetAxisIndex3)(benchmark::State& state)
{
for (auto _ : state)
{
for (int i = 0; i < numIters; ++i)
{
AZ::Vector3 result = m_obb.GetAxis(0);
benchmark::DoNotOptimize(result);
result = m_obb.GetAxis(1);
benchmark::DoNotOptimize(result);
result = m_obb.GetAxis(2);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathObb, SetHalfLengthX)(benchmark::State& state)
{
for (auto _ : state)
{
for (int i = 0; i < numIters; ++i)
{
m_obb.SetHalfLengthX(2.0f);
benchmark::DoNotOptimize(m_obb);
}
}
}
BENCHMARK_F(BM_MathObb, GetHalfLengthX)(benchmark::State& state)
{
for (auto _ : state)
{
for (int i = 0; i < numIters; ++i)
{
float result = m_obb.GetHalfLengthX();
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathObb, SetHalfLengthY)(benchmark::State& state)
{
for (auto _ : state)
{
for (int i = 0; i < numIters; ++i)
{
m_obb.SetHalfLengthY(2.0f);
benchmark::DoNotOptimize(m_obb);
}
}
}
BENCHMARK_F(BM_MathObb, GetHalfLengthY)(benchmark::State& state)
{
for (auto _ : state)
{
for (int i = 0; i < numIters; ++i)
{
float result = m_obb.GetHalfLengthY();
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathObb, SetHalfLengthZ)(benchmark::State& state)
{
for (auto _ : state)
{
for (int i = 0; i < numIters; ++i)
{
m_obb.SetHalfLengthZ(2.0f);
benchmark::DoNotOptimize(m_obb);
}
}
}
BENCHMARK_F(BM_MathObb, GetHalfLengthZ)(benchmark::State& state)
{
for (auto _ : state)
{
for (int i = 0; i < numIters; ++i)
{
float result = m_obb.GetHalfLengthZ();
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathObb, SetHalfLengthIndex3)(benchmark::State& state)
{
for (auto _ : state)
{
for (int i = 0; i < numIters; ++i)
{
m_obb.SetHalfLength(0, 2.0f);
m_obb.SetHalfLength(1, 2.0f);
m_obb.SetHalfLength(2, 2.0f);
benchmark::DoNotOptimize(m_obb);
}
}
}
BENCHMARK_F(BM_MathObb, GetHalfLengthIndex3)(benchmark::State& state)
{
for (auto _ : state)
{
for (int i = 0; i < numIters; ++i)
{
float result = m_obb.GetHalfLength(0);
benchmark::DoNotOptimize(result);
result = m_obb.GetHalfLength(1);
benchmark::DoNotOptimize(result);
result = m_obb.GetHalfLength(2);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathObb, CreateFromAabb)(benchmark::State& state)
{
AZ::Vector3 min(-100.0f, 50.0f, 0.0f);
AZ::Vector3 max(120.0f, 300.0f, 50.0f);
AZ::Aabb aabb = AZ::Aabb::CreateFromMinMax(min, max);
for (auto _ : state)
{
for (int i = 0; i < numIters; ++i)
{
AZ::Obb result = AZ::Obb::CreateFromAabb(aabb);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathObb, TransformMultiply)(benchmark::State& state)
{
AZ::Transform transform = AZ::Transform::CreateRotationY(AZ::DegToRad(90.0f));
for (auto _ : state)
{
for (int i = 0; i < numIters; ++i)
{
AZ::Obb result = transform * m_obb;
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathObb, Equal)(benchmark::State& state)
{
for (auto _ : state)
{
for (int i = 0; i < numIters; ++i)
{
AZ::Obb result = m_obb;
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathObb, IsFinite)(benchmark::State& state)
{
for (auto _ : state)
{
for (int i = 0; i < numIters; ++i)
{
bool result = m_obb.IsFinite();
benchmark::DoNotOptimize(result);
}
}
}
}
#endif
@@ -0,0 +1,148 @@
/*
* 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/Obb.h>
#include <AzCore/Math/Aabb.h>
#include <AzCore/Math/Vector3.h>
#include <AzCore/Math/Transform.h>
#include <AzCore/UnitTest/TestTypes.h>
#include <AZTestShared/Math/MathTestHelpers.h>
using namespace AZ;
namespace UnitTest
{
const Vector3 position(1.0f, 2.0f, 3.0f);
const Quaternion rotation = Quaternion::CreateRotationZ(Constants::QuarterPi);
const Vector3 halfLengths(0.5f);
TEST(MATH_Obb, TestCreateFromPositionRotationAndHalfLengths)
{
const Obb obb = Obb::CreateFromPositionRotationAndHalfLengths(position, rotation, halfLengths);
EXPECT_THAT(obb.GetPosition(), IsClose(position));
EXPECT_THAT(obb.GetRotation(), IsClose(rotation));
EXPECT_THAT(obb.GetHalfLengths(), IsClose(halfLengths));
}
TEST(MATH_Obb, TestCreateFromAabb)
{
const Vector3 min(-100.0f, 50.0f, 0.0f);
const Vector3 max(120.0f, 300.0f, 50.0f);
const Aabb aabb = Aabb::CreateFromMinMax(min, max);
const Obb obb = Obb::CreateFromAabb(aabb);
EXPECT_THAT(obb.GetPosition(), IsClose(Vector3(10.0f, 175.0f, 25.0f)));
EXPECT_THAT(obb.GetAxisX(), IsClose(Vector3(1.0f, 0.0f, 0.0f)));
EXPECT_THAT(obb.GetAxisY(), IsClose(Vector3(0.0f, 1.0f, 0.0f)));
EXPECT_THAT(obb.GetAxisZ(), IsClose(Vector3(0.0f, 0.0f, 1.0f)));
}
TEST(MATH_Obb, TestTransform)
{
Obb obb = Obb::CreateFromPositionRotationAndHalfLengths(position, rotation, halfLengths);
Transform transform = Transform::CreateRotationY(DegToRad(90.0f));
obb = transform * obb;
EXPECT_THAT(obb.GetPosition(), IsClose(Vector3(3.0f, 2.0f, -1.0f)));
EXPECT_THAT(obb.GetAxisX(), IsClose(Vector3(0.0f, 0.707f, -0.707f)));
EXPECT_THAT(obb.GetAxisY(), IsClose(Vector3(0.0f, 0.707f, 0.707f)));
EXPECT_THAT(obb.GetAxisZ(), IsClose(Vector3(1.0f, 0.0f, 0.0f)));
}
TEST(MATH_Obb, TestSetPosition)
{
Obb obb;
obb.SetPosition(position);
EXPECT_THAT(obb.GetPosition(), IsClose(position));
}
TEST(MATH_Obb, TestSetHalfLengthX)
{
Obb obb;
obb.SetHalfLengthX(2.0f);
EXPECT_NEAR(obb.GetHalfLengthX(), 2.0f, AZ::Constants::Tolerance);
}
TEST(MATH_Obb, TestSetHalfLengthY)
{
Obb obb;
obb.SetHalfLengthY(3.0f);
EXPECT_NEAR(obb.GetHalfLengthY(), 3.0f, AZ::Constants::Tolerance);
}
TEST(MATH_Obb, TestSetHalfLengthZ)
{
Obb obb;
obb.SetHalfLengthZ(4.0f);
EXPECT_NEAR(obb.GetHalfLengthZ(), 4.0f, AZ::Constants::Tolerance);
}
TEST(MATH_Obb, TestSetHalfLengthIndex)
{
Obb obb;
obb.SetHalfLength(2, 5.0f);
EXPECT_NEAR(obb.GetHalfLength(2), 5.0f, AZ::Constants::Tolerance);
}
TEST(MATH_Obb, TestIsFinite)
{
Obb obb = Obb::CreateFromPositionRotationAndHalfLengths(position, rotation, halfLengths);
EXPECT_TRUE(obb.IsFinite());
const float infinity = std::numeric_limits<float>::infinity();
const Vector3 infiniteV3 = Vector3(infinity);
// Test to make sure that setting properties of the bounding box
// properly mark it as infinite, and when reset it becomes finite again.
obb.SetPosition(infiniteV3);
EXPECT_FALSE(obb.IsFinite());
obb.SetPosition(position);
EXPECT_TRUE(obb.IsFinite());
obb.SetHalfLengthY(infinity);
EXPECT_FALSE(obb.IsFinite());
}
TEST(MATH_Obb, Contains)
{
const Vector3 position(1.0f, 2.0f, 3.0f);
const Quaternion rotation = Quaternion::CreateRotationZ(DegToRad(30.0f));
const Vector3 halfLengths(2.0f, 1.0f, 2.5f);
const Obb obb = Obb::CreateFromPositionRotationAndHalfLengths(position, rotation, halfLengths);
// test some pairs of points which should be just either side of the Obb boundary
EXPECT_TRUE(obb.Contains(Vector3(1.35f, 3.35f, 3.5f)));
EXPECT_FALSE(obb.Contains(Vector3(1.35f, 3.4f, 3.5f)));
EXPECT_TRUE(obb.Contains(Vector3(-1.1f, 1.7f, 2.0f)));
EXPECT_FALSE(obb.Contains(Vector3(-1.2f, 1.7f, 2.0f)));
EXPECT_TRUE(obb.Contains(Vector3(1.7f, 1.8f, 5.45f)));
EXPECT_FALSE(obb.Contains(Vector3(1.7f, 1.8f, 5.55f)));
}
TEST(MATH_Obb, GetDistance)
{
const Vector3 position(5.0f, 3.0f, 2.0f);
const Quaternion rotation = Quaternion::CreateRotationX(DegToRad(60.0f));
const Vector3 halfLengths(0.5f, 2.0f, 1.5f);
const Obb obb = Obb::CreateFromPositionRotationAndHalfLengths(position, rotation, halfLengths);
EXPECT_NEAR(obb.GetDistance(Vector3(5.3f, 3.2f, 1.8f)), 0.0f, 1e-3f);
EXPECT_NEAR(obb.GetDistance(Vector3(5.1f, 1.1f, 3.7f)), 0.9955f, 1e-3f);
EXPECT_NEAR(obb.GetDistance(Vector3(4.7f, 4.5f, 4.2f)), 0.6553f, 1e-3f);
EXPECT_NEAR(obb.GetDistance(Vector3(3.9f, 3.6f, -3.0f)), 2.6059f, 1e-3f);
}
TEST(MATH_Obb, GetDistanceSq)
{
const Vector3 position(1.0f, 4.0f, 3.0f);
const Quaternion rotation = Quaternion::CreateRotationY(DegToRad(45.0f));
const Vector3 halfLengths(1.5f, 3.0f, 1.0f);
const Obb obb = Obb::CreateFromPositionRotationAndHalfLengths(position, rotation, halfLengths);
EXPECT_NEAR(obb.GetDistanceSq(Vector3(1.1f, 4.3f, 2.7f)), 0.0f, 1e-3f);
EXPECT_NEAR(obb.GetDistanceSq(Vector3(-0.7f, 3.5f, 2.0f)), 0.8266f, 1e-3f);
EXPECT_NEAR(obb.GetDistanceSq(Vector3(2.4f, 0.5f, 1.5f)), 0.5532f, 1e-3f);
EXPECT_NEAR(obb.GetDistanceSq(Vector3(1.1f, 7.3f, 5.8f)), 1.3612f, 1e-3f);
}
}
@@ -0,0 +1,355 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyrightand 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.
*
*/
#if defined(HAVE_BENCHMARK)
#include <AzCore/Math/Plane.h>
#include <AzCore/Math/MathUtils.h>
#include <AzCore/UnitTest/TestTypes.h>
#include <random>
namespace Benchmark
{
class BM_MathPlane
: public benchmark::Fixture
{
public:
BM_MathPlane()
{
const unsigned int seed = 1;
rng = std::mt19937_64(seed);
}
void SetUp([[maybe_unused]] const ::benchmark::State& state) override
{
for (int i = 0; i < m_numIters; ++i)
{
m_normal = AZ::Vector3(unif(rng), unif(rng), unif(rng));
m_normal.Normalize();
m_normals.push_back(m_normal);
m_point = AZ::Vector3(unif(rng), unif(rng), unif(rng));
m_points.push_back(m_point);
m_distance = unif(rng);
m_dists.push_back(m_distance);
//set these differently so they don't overlap with same values as other vectors
m_normal = AZ::Vector3(unif(rng), unif(rng), unif(rng));
m_normal.Normalize();
m_distance = unif(rng);
m_plane = AZ::Plane::CreateFromNormalAndDistance(m_normal, m_distance);
m_planes.push_back(m_plane);
}
}
AZ::Plane m_plane;
AZ::Vector3 m_normal;
AZ::Vector3 m_point;
float m_distance;
const int m_numIters = 1000;
//rng
std::mt19937_64 rng;
std::uniform_real_distribution<float> unif;
std::vector<AZ::Vector3> m_normals;
std::vector<AZ::Vector3> m_points;
std::vector<float> m_dists;
std::vector<AZ::Plane> m_planes;
};
BENCHMARK_F(BM_MathPlane, CreateFromNormalAndDistance)(benchmark::State& state)
{
for (auto _ : state)
{
for (int i = 0; i < m_numIters; ++i)
{
AZ::Plane result = AZ::Plane::CreateFromNormalAndDistance(m_normals[i], m_dists[i]);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathPlane, GetDistance)(benchmark::State& state)
{
for (auto _ : state)
{
for (int i = 0; i < m_numIters; ++i)
{
float result = m_planes[i].GetDistance();
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathPlane, GetNormal)(benchmark::State& state)
{
for (auto _ : state)
{
for (int i = 0; i < m_numIters; ++i)
{
AZ::Vector3 result = m_planes[i].GetNormal();
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathPlane, CreateFromNormalAndPoint)(benchmark::State& state)
{
for (auto _ : state)
{
for (int i = 0; i < m_numIters; ++i)
{
AZ::Plane result = AZ::Plane::CreateFromNormalAndPoint(m_normals[i], m_points[i]);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathPlane, CreateFromCoefficients)(benchmark::State& state)
{
AZStd::vector<float> coeff;
const int numCoeffPerCall = 4;
for (int i = 0; i < m_numIters * numCoeffPerCall; ++i)
{
coeff.push_back(unif(rng));
}
for (auto _ : state)
{
for (int i = 0; i < m_numIters; ++i)
{
int index = i * numCoeffPerCall;
AZ::Plane result = AZ::Plane::CreateFromCoefficients(coeff[index], coeff[index + 1], coeff[index + 2], coeff[index + 3]);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathPlane, SetCoefficients)(benchmark::State& state)
{
AZStd::vector<float> coeff;
const int numCoeffPerCall = 4;
for (int i = 0; i < m_numIters * numCoeffPerCall; ++i)
{
coeff.push_back(unif(rng));
}
for (auto _ : state)
{
for (int i = 0; i < m_numIters; ++i)
{
int index = i * numCoeffPerCall;
m_planes[i].Set(coeff[index], coeff[index + 1], coeff[index + 2], coeff[index + 3]);
}
}
}
BENCHMARK_F(BM_MathPlane, SetVector3)(benchmark::State& state)
{
for (auto _ : state)
{
for (int i = 0; i < m_numIters; ++i)
{
AZ::Plane result;
result.Set(m_normals[i], m_dists[i]);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathPlane, SetVector4)(benchmark::State& state)
{
AZStd::vector<AZ::Vector4> vecs;
for (int i = 0; i < m_numIters; ++i)
{
vecs.push_back(AZ::Vector4(unif(rng), unif(rng), unif(rng), unif(rng)));
}
for (auto _ : state)
{
for (int i = 0; i < m_numIters; ++i)
{
AZ::Plane result;
result.Set(vecs[i]);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathPlane, SetNormal)(benchmark::State& state)
{
for (auto _ : state)
{
for (int i = 0; i < m_numIters; ++i)
{
AZ::Plane result = m_planes[i];
result.SetNormal(m_normals[i]);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathPlane, SetDistance)(benchmark::State& state)
{
for (auto _ : state)
{
for (int i = 0; i < m_numIters; ++i)
{
AZ::Plane result = m_planes[i];
result.SetDistance(m_dists[i]);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathPlane, GetPlaneEquationCoefficients)(benchmark::State& state)
{
for (auto _ : state)
{
for (int i = 0; i < m_numIters; ++i)
{
AZ::Vector4 result = m_planes[i].GetPlaneEquationCoefficients();
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathPlane, GetTransform)(benchmark::State& state)
{
AZStd::vector<AZ::Transform> trans;
for (int i = 0; i < m_numIters; ++i)
{
trans.push_back(AZ::Transform::CreateRotationY(AZ::DegToRad(unif(rng))));
}
for (auto _ : state)
{
for (int i = 0; i < m_numIters; ++i)
{
AZ::Plane result = m_planes[i].GetTransform(trans[i]);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathPlane, ApplyTransform)(benchmark::State& state)
{
AZStd::vector<AZ::Transform> trans;
for (int i = 0; i < m_numIters; ++i)
{
trans.push_back(AZ::Transform::CreateRotationY(AZ::DegToRad(unif(rng))));
}
for (auto _ : state)
{
for (int i = 0; i < m_numIters; ++i)
{
m_planes[i].ApplyTransform(trans[i]);
}
}
}
BENCHMARK_F(BM_MathPlane, GetPointDist)(benchmark::State& state)
{
for (auto _ : state)
{
for (int i = 0; i < m_numIters; ++i)
{
float result = m_planes[i].GetPointDist(m_points[i]);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathPlane, GetProjected)(benchmark::State& state)
{
for (auto _ : state)
{
for (int i = 0; i < m_numIters; ++i)
{
AZ::Vector3 result = m_planes[i].GetProjected(m_points[i]);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathPlane, CastRay)(benchmark::State& state)
{
AZ::Vector3 rayResult;
for (auto _ : state)
{
for (int i = 0; i < m_numIters; ++i)
{
bool result = m_plane.CastRay(m_points[i], m_normals[i], rayResult);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathPlane, CastRayTime)(benchmark::State& state)
{
float rayResult;
for (auto _ : state)
{
for (int i = 0; i < m_numIters; ++i)
{
bool result = m_plane.CastRay(m_points[i], m_normals[i], rayResult);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathPlane, IntersectSegment)(benchmark::State& state)
{
AZ::Vector3 rayResult;
for (auto _ : state)
{
for (int i = 0; i < m_numIters - 1; ++i)
{
bool result = m_plane.IntersectSegment(m_points[i], m_points[i + 1], rayResult);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathPlane, IntersectSegmentTime)(benchmark::State& state)
{
float rayResult;
for (auto _ : state)
{
for (int i = 0; i < m_numIters - 1; ++i)
{
bool result = m_plane.IntersectSegment(m_points[i], m_points[i + 1], rayResult);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathPlane, IsFinite)(benchmark::State& state)
{
for (auto _ : state)
{
for (int i = 0; i < m_numIters; ++i)
{
bool result = m_planes[i].IsFinite();
benchmark::DoNotOptimize(result);
}
}
}
}
#endif
@@ -0,0 +1,197 @@
/*
* 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/Plane.h>
#include <AzCore/Math/Transform.h>
#include <AzCore/Math/Vector2.h>
#include <AzCore/Math/Vector3.h>
#include <AzCore/Math/Vector4.h>
#include <AzCore/UnitTest/TestTypes.h>
using namespace AZ;
namespace UnitTest
{
TEST(MATH_Plane, TestCreateFromNormalAndDistance)
{
Plane pl = Plane::CreateFromNormalAndDistance(Vector3(1.0f, 0.0f, 0.0f), -100.0f);
AZ_TEST_ASSERT_FLOAT_CLOSE(pl.GetDistance(), -100.0f);
AZ_TEST_ASSERT_FLOAT_CLOSE(pl.GetNormal().GetX(), 1.0f);
AZ_TEST_ASSERT_FLOAT_CLOSE(pl.GetNormal().GetY(), 0.0f);
AZ_TEST_ASSERT_FLOAT_CLOSE(pl.GetNormal().GetZ(), 0.0f);
}
TEST(MATH_Plane, TestCreateFromNormalAndPoint)
{
Plane pl = Plane::CreateFromNormalAndPoint(Vector3(0.0f, 1.0f, 0.0f), Vector3(10, 10, 10));
AZ_TEST_ASSERT_FLOAT_CLOSE(pl.GetDistance(), -10.0f);
AZ_TEST_ASSERT_FLOAT_CLOSE(pl.GetNormal().GetX(), 0.0f);
AZ_TEST_ASSERT_FLOAT_CLOSE(pl.GetNormal().GetY(), 1.0f);
AZ_TEST_ASSERT_FLOAT_CLOSE(pl.GetNormal().GetZ(), 0.0f);
}
TEST(MATH_Plane, TestCreateFromCoefficients)
{
Plane pl = Plane::CreateFromCoefficients(0.0f, -1.0f, 0.0f, -5.0f);
AZ_TEST_ASSERT_FLOAT_CLOSE(pl.GetDistance(), -5.0f);
AZ_TEST_ASSERT_FLOAT_CLOSE(pl.GetNormal().GetX(), 0.0f);
AZ_TEST_ASSERT_FLOAT_CLOSE(pl.GetNormal().GetY(), -1.0f);
AZ_TEST_ASSERT_FLOAT_CLOSE(pl.GetNormal().GetZ(), 0.0f);
}
TEST(MATH_Plane, TestSet)
{
Plane pl;
pl.Set(12.0f, 13.0f, 14.0f, 15.0f);
AZ_TEST_ASSERT_FLOAT_CLOSE(pl.GetDistance(), 15.0f);
AZ_TEST_ASSERT_FLOAT_CLOSE(pl.GetNormal().GetX(), 12.0f);
AZ_TEST_ASSERT_FLOAT_CLOSE(pl.GetNormal().GetY(), 13.0f);
AZ_TEST_ASSERT_FLOAT_CLOSE(pl.GetNormal().GetZ(), 14.0f);
}
TEST(MATH_Plane, TestSetVector3)
{
Plane pl;
pl.Set(Vector3(22.0f, 23.0f, 24.0f), 25.0f);
AZ_TEST_ASSERT_FLOAT_CLOSE(pl.GetDistance(), 25.0f);
AZ_TEST_ASSERT_FLOAT_CLOSE(pl.GetNormal().GetX(), 22.0f);
AZ_TEST_ASSERT_FLOAT_CLOSE(pl.GetNormal().GetY(), 23.0f);
AZ_TEST_ASSERT_FLOAT_CLOSE(pl.GetNormal().GetZ(), 24.0f);
}
TEST(MATH_Plane, TestSetVector4)
{
Plane pl;
pl.Set(Vector4(32.0f, 33.0f, 34.0f, 35.0f));
AZ_TEST_ASSERT_FLOAT_CLOSE(pl.GetDistance(), 35.0f);
AZ_TEST_ASSERT_FLOAT_CLOSE(pl.GetNormal().GetX(), 32.0f);
AZ_TEST_ASSERT_FLOAT_CLOSE(pl.GetNormal().GetY(), 33.0f);
AZ_TEST_ASSERT_FLOAT_CLOSE(pl.GetNormal().GetZ(), 34.0f);
}
TEST(MATH_Plane, TestSetNormalAndDistance)
{
Plane pl;
pl.SetNormal(Vector3(0.0f, 0.0f, 1.0f));
AZ_TEST_ASSERT_FLOAT_CLOSE(pl.GetNormal().GetX(), 0.0f);
AZ_TEST_ASSERT_FLOAT_CLOSE(pl.GetNormal().GetY(), 0.0f);
AZ_TEST_ASSERT_FLOAT_CLOSE(pl.GetNormal().GetZ(), 1.0f);
pl.SetDistance(55.0f);
AZ_TEST_ASSERT_FLOAT_CLOSE(pl.GetDistance(), 55.0f);
AZ_TEST_ASSERT_FLOAT_CLOSE(pl.GetPlaneEquationCoefficients().GetW(), 55.0f);
AZ_TEST_ASSERT_FLOAT_CLOSE(pl.GetPlaneEquationCoefficients().GetX(), 0.0f);
AZ_TEST_ASSERT_FLOAT_CLOSE(pl.GetPlaneEquationCoefficients().GetY(), 0.0f);
AZ_TEST_ASSERT_FLOAT_CLOSE(pl.GetPlaneEquationCoefficients().GetZ(), 1.0f);
}
TEST(MATH_Plane, TestGetTransform)
{
Plane pl;
pl.Set(Vector3(0.0f, 0.0f, 1.0f), 55.0f);
Transform tm = Transform::CreateRotationY(DegToRad(90.0f));
pl = pl.GetTransform(tm);
AZ_TEST_ASSERT_FLOAT_CLOSE(pl.GetPlaneEquationCoefficients().GetW(), 55.0f);
AZ_TEST_ASSERT_FLOAT_CLOSE(pl.GetPlaneEquationCoefficients().GetX(), 1.0f);
AZ_TEST_ASSERT_FLOAT_CLOSE(pl.GetPlaneEquationCoefficients().GetY(), 0.0f);
AZ_TEST_ASSERT_FLOAT_CLOSE(pl.GetPlaneEquationCoefficients().GetZ(), 0.0f);
float dist = pl.GetPointDist(Vector3(10.0f, 0.0f, 0.0f));
AZ_TEST_ASSERT_FLOAT_CLOSE(dist, 65.0f); // 55 + 10
}
TEST(MATH_Plane, TestApplyTransform)
{
Plane pl;
pl.Set(Vector3(0.0f, 0.0f, 1.0f), 55.0f);
Transform tm1 = Transform::CreateRotationY(DegToRad(90.0f));
Transform tm2 = Transform::CreateRotationZ(DegToRad(45.0f));
pl.ApplyTransform(tm1);
pl.ApplyTransform(tm2);
AZ_TEST_ASSERT_FLOAT_CLOSE(pl.GetPlaneEquationCoefficients().GetW(), 55.0f);
AZ_TEST_ASSERT_FLOAT_CLOSE(pl.GetPlaneEquationCoefficients().GetX(), 0.707f);
AZ_TEST_ASSERT_FLOAT_CLOSE(pl.GetPlaneEquationCoefficients().GetY(), 0.707f);
AZ_TEST_ASSERT_FLOAT_CLOSE(pl.GetPlaneEquationCoefficients().GetZ(), 0.0f);
}
TEST(MATH_Plane, TestGetProjected)
{
Plane pl;
pl.Set(Vector3(0.0f, 0.0f, 1.0f), 55.0f);
Vector3 v1 = pl.GetProjected(Vector3(10.0f, 15.0f, 20.0f));
AZ_TEST_ASSERT(v1 == Vector3(10.0f, 15.0f, 0.0f));
}
TEST(MATH_Plane, TestCastRay)
{
Vector3 hitPoint;
Plane pl;
pl.Set(0.0f, 0.0f, 1.0f, 10.0f);
bool hit = pl.CastRay(Vector3(0.0f, 0.0f, 0.0f), Vector3(0.0f, 0.0f, 1.0f), hitPoint);
AZ_TEST_ASSERT(hit == true);
AZ_TEST_ASSERT(hitPoint.IsClose(Vector3(0.0f, 0.0f, -10.0f)));
pl.Set(0.0f, 1.0f, 0.0f, 10.0f);
float time;
hit = pl.CastRay(Vector3(0.0f, 1.0f, 0.0f), Vector3(0.0f, -1.0f, 0.0f), time);
AZ_TEST_ASSERT(hit == true);
AZ_TEST_ASSERT_FLOAT_CLOSE(time, 10.999f);
pl.Set(1.0f, 0.0f, 0.0f, 5.0f);
hit = pl.CastRay(Vector3(0.0f, 1.0f, 0.0f), Vector3(0.0f, -1.0f, 0.0f), time);
AZ_TEST_ASSERT(hit == false);
}
TEST(MATH_Plane, TestIntersectSegment)
{
Vector3 hitPoint;
Plane pl;
pl.Set(1.0f, 0.0f, 0.0f, 0.0f);
bool hit = pl.IntersectSegment(Vector3(-1.0f, 0.0f, 0.0f), Vector3(1.0f, 0.0f, 0.0f), hitPoint);
AZ_TEST_ASSERT(hit == true);
AZ_TEST_ASSERT(hitPoint.IsClose(Vector3(0.0f, 0.0f, 0.0f)));
float time;
pl.Set(0.0f, 1.0f, 0.0f, 0.0f);
hit = pl.IntersectSegment(Vector3(0.0f, -10.0f, 0.0f), Vector3(0.0f, 10.0f, 0.0f), time);
AZ_TEST_ASSERT(hit == true);
AZ_TEST_ASSERT_FLOAT_CLOSE(time, 0.5f);
pl.Set(0.0f, 1.0f, 0.0f, 20.0f);
hit = pl.IntersectSegment(Vector3(-1.0f, 0.0f, 0.0f), Vector3(1.0f, 0.0f, 0.0f), time);
AZ_TEST_ASSERT(hit == false);
}
TEST(MATH_Plane, TestIsFinite)
{
Plane pl;
pl.Set(1.0f, 0.0f, 0.0f, 0.0f);
AZ_TEST_ASSERT(pl.IsFinite());
const float infinity = std::numeric_limits<float>::infinity();
pl.Set(infinity, infinity, infinity, infinity);
AZ_TEST_ASSERT(!pl.IsFinite());
}
TEST(MATH_Plane, CreateFromVectorCoefficients_IsEquivalentToCreateFromCoefficients)
{
Plane planeFromCoefficients = Plane::CreateFromCoefficients(1.0, 2.0, 3.0, 4.0);
Vector4 coefficients(1.0, 2.0, 3.0, 4.0);
Plane planeFromVectorCoefficients = Plane::CreateFromVectorCoefficients(coefficients);
EXPECT_EQ(planeFromVectorCoefficients, planeFromCoefficients);
}
}
@@ -0,0 +1,759 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyrightand 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.
*
*/
#if defined(HAVE_BENCHMARK)
#include <AzCore/Math/Quaternion.h>
#include <AzCore/UnitTest/TestTypes.h>
#include <random>
namespace Benchmark
{
class BM_MathQuaternion
: public benchmark::Fixture
{
public:
void SetUp([[maybe_unused]] const ::benchmark::State& state) override
{
m_quatDataArray.resize(1000);
const unsigned int seed = 1;
std::mt19937_64 rng(seed);
std::uniform_real_distribution<float> unif;
std::generate(m_quatDataArray.begin(), m_quatDataArray.end(), [&unif, &rng]()
{
QuatData quatData;
float angle = unif(rng);
quatData.q1 = AZ::Quaternion::CreateFromAxisAngle(AZ::Vector3(unif(rng), unif(rng), unif(rng)).GetNormalized(), angle);
angle = unif(rng);
quatData.q2 = AZ::Quaternion::CreateFromAxisAngle(AZ::Vector3(unif(rng), unif(rng), unif(rng)).GetNormalized(), angle);
angle = unif(rng);
quatData.q3 = AZ::Quaternion::CreateFromAxisAngle(AZ::Vector3(unif(rng), unif(rng), unif(rng)).GetNormalized(), angle);
quatData.x = unif(rng);
quatData.y = unif(rng);
quatData.z = unif(rng);
quatData.w = unif(rng);
return quatData;
});
}
struct QuatData
{
AZ::Quaternion q1;
AZ::Quaternion q2;
AZ::Quaternion q3;
float x;
float y;
float z;
float w;
};
std::vector<QuatData> m_quatDataArray;
};
BENCHMARK_F(BM_MathQuaternion, SplatFloatConstruction)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& quatData : m_quatDataArray)
{
AZ::Quaternion result(quatData.x);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathQuaternion, NonNormalized4FloatsConstruction)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& quatData : m_quatDataArray)
{
AZ::Quaternion result(quatData.x, quatData.y, quatData.z, quatData.w);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathQuaternion, CreateFromIdentity)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& quatData : m_quatDataArray)
{
AZ::Quaternion result = AZ::Quaternion::CreateIdentity();
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathQuaternion, CreateZero)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& quatData : m_quatDataArray)
{
AZ::Quaternion result = AZ::Quaternion::CreateZero();
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathQuaternion, CreateRotationX)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& quatData : m_quatDataArray)
{
AZ::Quaternion result = AZ::Quaternion::CreateRotationX(quatData.x);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathQuaternion, CreateRotationY)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& quatData : m_quatDataArray)
{
AZ::Quaternion result = AZ::Quaternion::CreateRotationY(quatData.x);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathQuaternion, CreateRotationZ)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& quatData : m_quatDataArray)
{
AZ::Quaternion result = AZ::Quaternion::CreateRotationZ(quatData.x);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathQuaternion, CreateShortestArcSameHemisphere)(benchmark::State& state)
{
const AZ::Vector3 vec1 = AZ::Vector3(1.0f, 2.0f, 3.0f).GetNormalized();
const AZ::Vector3 vec2 = AZ::Vector3(-2.0f, 7.0f, -1.0f).GetNormalized();
for (auto _ : state)
{
for (auto& quatData : m_quatDataArray)
{
AZ::Quaternion result = AZ::Quaternion::CreateShortestArc(vec1, vec2); //result should transform vec1 into vec2
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathQuaternion, CreateShortestArcDifferentHemisphere)(benchmark::State& state)
{
const AZ::Vector3 vec1 = AZ::Vector3(1.0f, 2.0f, 3.0f).GetNormalized();
const AZ::Vector3 vec2 = AZ::Vector3(-1.0f, -2.0f, -3.0f).GetNormalized();
for (auto _ : state)
{
for (auto& quatData : m_quatDataArray)
{
AZ::Quaternion result = AZ::Quaternion::CreateShortestArc(vec1, vec2); //result should transform vec1 into vec2
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathQuaternion, GetX)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& quatData : m_quatDataArray)
{
float result = quatData.q1.GetX();
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathQuaternion, GetY)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& quatData : m_quatDataArray)
{
float result = quatData.q1.GetY();
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathQuaternion, GetZ)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& quatData : m_quatDataArray)
{
float result = quatData.q1.GetZ();
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathQuaternion, GetW)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& quatData : m_quatDataArray)
{
float result = quatData.q1.GetW();
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathQuaternion, SetX)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& quatData : m_quatDataArray)
{
AZ::Quaternion quat;
quat.SetX(quatData.x);
benchmark::DoNotOptimize(quat);
}
}
}
BENCHMARK_F(BM_MathQuaternion, SetY)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& quatData : m_quatDataArray)
{
AZ::Quaternion quat;
quat.SetY(quatData.y);
benchmark::DoNotOptimize(quat);
}
}
}
BENCHMARK_F(BM_MathQuaternion, SetZ)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& quatData : m_quatDataArray)
{
AZ::Quaternion quat;
quat.SetZ(quatData.z);
benchmark::DoNotOptimize(quat);
}
}
}
BENCHMARK_F(BM_MathQuaternion, SetW)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& quatData : m_quatDataArray)
{
AZ::Quaternion quat;
quat.SetW(quatData.w);
benchmark::DoNotOptimize(quat);
}
}
}
BENCHMARK_F(BM_MathQuaternion, SetSplat)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& quatData : m_quatDataArray)
{
AZ::Quaternion quat;
quat.Set(quatData.x);
benchmark::DoNotOptimize(quat);
}
}
}
BENCHMARK_F(BM_MathQuaternion, SetAll)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& quatData : m_quatDataArray)
{
AZ::Quaternion quat;
quat.Set(quatData.x, quatData.y, quatData.z, quatData.w);
benchmark::DoNotOptimize(quat);
}
}
}
BENCHMARK_F(BM_MathQuaternion, SetVectorAndReal)(benchmark::State& state)
{
AZ::Vector3 vec(5.0f, 6.0f, 7.0f);
for (auto _ : state)
{
for (auto& quatData : m_quatDataArray)
{
AZ::Quaternion quat;
quat.Set(vec, 8.0f);
benchmark::DoNotOptimize(quat);
}
}
}
BENCHMARK_F(BM_MathQuaternion, SetArray)(benchmark::State& state)
{
const float quatArray[4] = { 5.0f, 6.0f, 7.0f, 8.0f };
for (auto _ : state)
{
for (auto& quatData : m_quatDataArray)
{
AZ::Quaternion quat;
quat.Set(quatArray);
benchmark::DoNotOptimize(quat);
}
}
}
BENCHMARK_F(BM_MathQuaternion, SetElements)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& quatData : m_quatDataArray)
{
AZ::Quaternion quat;
quat.SetElement(0, quatData.x);
quat.SetElement(1, quatData.y);
quat.SetElement(2, quatData.z);
quat.SetElement(3, quatData.w);
benchmark::DoNotOptimize(quat);
}
}
}
BENCHMARK_F(BM_MathQuaternion, GetElement)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& quatData : m_quatDataArray)
{
float result = quatData.q1.GetElement(0);
benchmark::DoNotOptimize(result);
result = quatData.q1.GetElement(1);
benchmark::DoNotOptimize(result);
result = quatData.q1.GetElement(2);
benchmark::DoNotOptimize(result);
result = quatData.q1.GetElement(3);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathQuaternion, GetConjugate)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& quatData : m_quatDataArray)
{
AZ::Quaternion result = quatData.q1.GetConjugate();
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathQuaternion, GetInverseFast)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& quatData : m_quatDataArray)
{
AZ::Quaternion result = quatData.q1.GetInverseFast();
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathQuaternion, GetInverseFull)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& quatData : m_quatDataArray)
{
AZ::Quaternion result = quatData.q1.GetInverseFull();
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathQuaternion, Dot)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& quatData : m_quatDataArray)
{
float result = quatData.q1.Dot(quatData.q2);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathQuaternion, GetLength)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& quatData : m_quatDataArray)
{
float result = quatData.q1.GetLength();
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathQuaternion, GetLengthEstimate)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& quatData : m_quatDataArray)
{
float result = quatData.q1.GetLengthEstimate();
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathQuaternion, GetLengthReciprocal)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& quatData : m_quatDataArray)
{
float result = quatData.q1.GetLengthReciprocal();
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathQuaternion, GetLengthReciprocalEstimate)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& quatData : m_quatDataArray)
{
float result = quatData.q1.GetLengthReciprocalEstimate();
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathQuaternion, GetNormalized)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& quatData : m_quatDataArray)
{
AZ::Quaternion result = quatData.q1.GetNormalized();
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathQuaternion, GetNormalizedEstimate)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& quatData : m_quatDataArray)
{
AZ::Quaternion result = quatData.q1.GetNormalizedEstimate();
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathQuaternion, NormalizeWithLength)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& quatData : m_quatDataArray)
{
float result = quatData.q1.NormalizeWithLength();
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathQuaternion, NormalizeWithLengthEstimate)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& quatData : m_quatDataArray)
{
float result = quatData.q1.NormalizeWithLengthEstimate();
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathQuaternion, Lerp)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& quatData : m_quatDataArray)
{
AZ::Quaternion result = quatData.q1.Lerp(quatData.q2, quatData.x);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathQuaternion, NLerp)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& quatData : m_quatDataArray)
{
AZ::Quaternion result = quatData.q1.NLerp(quatData.q2, quatData.x);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathQuaternion, Slerp)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& quatData : m_quatDataArray)
{
AZ::Quaternion result = quatData.q1.Slerp(quatData.q2, quatData.x);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathQuaternion, OperatorEquality)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& quatData : m_quatDataArray)
{
bool result = (quatData.q1 == quatData.q2);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathQuaternion, OperatorInequality)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& quatData : m_quatDataArray)
{
bool result = (quatData.q1 != quatData.q2);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathQuaternion, OperatorNegate)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& quatData : m_quatDataArray)
{
AZ::Quaternion result = -quatData.q1;
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathQuaternion, OperatorSum)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& quatData : m_quatDataArray)
{
AZ::Quaternion result = quatData.q1 + quatData.q2;
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathQuaternion, OperatorSub)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& quatData : m_quatDataArray)
{
AZ::Quaternion result = quatData.q1 - quatData.q2;
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathQuaternion, OperatorMultiply)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& quatData : m_quatDataArray)
{
AZ::Quaternion result = quatData.q1 * quatData.q2;
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathQuaternion, OperatorMultiplyScalar)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& quatData : m_quatDataArray)
{
AZ::Quaternion result = quatData.q1 * quatData.x;
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathQuaternion, TransformVector)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& quatData : m_quatDataArray)
{
AZ::Vector3 vector(quatData.x, quatData.y, quatData.z);
AZ::Vector3 result1 = quatData.q1.TransformVector(vector);
AZ::Vector3 result2 = quatData.q2.TransformVector(vector);
benchmark::DoNotOptimize(result1);
benchmark::DoNotOptimize(result2);
}
}
}
BENCHMARK_F(BM_MathQuaternion, IsClose)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& quatData : m_quatDataArray)
{
bool result = quatData.q1.IsClose(quatData.q2);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathQuaternion, IsIdentity)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& quatData : m_quatDataArray)
{
bool result = quatData.q1.IsIdentity();
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathQuaternion, GetEulerDegrees)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& quatData : m_quatDataArray)
{
AZ::Vector3 result = quatData.q1.GetEulerDegrees();
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathQuaternion, GetEulerRadians)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& quatData : m_quatDataArray)
{
AZ::Vector3 result = quatData.q1.GetEulerRadians();
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathQuaternion, SetFromEulerRadians)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& quatData : m_quatDataArray)
{
AZ::Vector3 value = AZ::Vector3(quatData.x, quatData.y, quatData.z);
AZ::Quaternion result;
result.SetFromEulerRadians(value);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathQuaternion, SetFromEulerDegrees)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& quatData : m_quatDataArray)
{
AZ::Vector3 value = AZ::Vector3(quatData.x, quatData.y, quatData.z);
AZ::Quaternion result;
result.SetFromEulerDegrees(value);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathQuaternion, ConvertToAxisAngle)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& quatData : m_quatDataArray)
{
AZ::Vector3 axis;
float angle;
quatData.q1.ConvertToAxisAngle(axis, angle);
benchmark::DoNotOptimize(axis);
benchmark::DoNotOptimize(angle);
}
}
}
BENCHMARK_F(BM_MathQuaternion, AggregateMultiply)(::benchmark::State& state)
{
for (auto _ : state)
{
AZ::Vector3 position = AZ::Vector3::CreateZero();
for (auto& quatData : m_quatDataArray)
{
AZ::Quaternion result = quatData.q3 * quatData.q2 * quatData.q1;
AZ::Quaternion normalizedResult = result.GetNormalized();
AZ::Vector3 direction = normalizedResult.TransformVector(AZ::Vector3::CreateAxisX());
position += direction;
}
benchmark::DoNotOptimize(position);
}
}
}
#endif
@@ -0,0 +1,415 @@
/*
* 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/Quaternion.h>
#include <AzCore/Math/Vector3.h>
#include <AzCore/Math/Matrix3x3.h>
#include <AzCore/Math/Transform.h>
#include <AzCore/UnitTest/TestTypes.h>
using namespace AZ;
namespace UnitTest
{
constexpr float normalizeEpsilon = 0.002f;
constexpr float values[4] = { 10.0f, 20.0f, 30.0f, 40.0f };
TEST(MATH_Quaternion, TestHandedness)
{
//test to make sure our rotations follow the right hand rule,
// a positive rotation around z should transform the x axis to the y axis
Matrix4x4 matrix = Matrix4x4::CreateRotationZ(DegToRad(90.0f));
Vector3 v = matrix * Vector3(1.0f, 0.0f, 0.0f);
AZ_TEST_ASSERT(v.IsClose(Vector3(0.0f, 1.0f, 0.0f)));
AZ::Quaternion quat = AZ::Quaternion::CreateRotationZ(DegToRad(90.0f));
v = quat.TransformVector(Vector3(1.0f, 0.0f, 0.0f));
AZ_TEST_ASSERT(v.IsClose(Vector3(0.0f, 1.0f, 0.0f)));
}
TEST(MATH_Quaternion, TestConstruction)
{
AZ::Quaternion q1(0.0f, 0.0f, 0.0f, 1.0f);
EXPECT_TRUE((q1.GetX() == 0.0f) && (q1.GetY() == 0.0f) && (q1.GetZ() == 0.0f) && (q1.GetW() == 1.0f));
AZ::Quaternion q2(5.0f);
EXPECT_TRUE((q2.GetX() == 5.0f) && (q2.GetY() == 5.0f) && (q2.GetZ() == 5.0f) && (q2.GetW() == 5.0f));
AZ::Quaternion q3(1.0f, 2.0f, 3.0f, 4.0f);
EXPECT_TRUE((q3.GetX() == 1.0f) && (q3.GetY() == 2.0f) && (q3.GetZ() == 3.0f) && (q3.GetW() == 4.0f));
AZ::Quaternion q4 = AZ::Quaternion::CreateFromVector3AndValue(Vector3(1.0f, 2.0f, 3.0f), 4.0f);
EXPECT_TRUE((q4.GetX() == 1.0f) && (q4.GetY() == 2.0f) && (q4.GetZ() == 3.0f) && (q4.GetW() == 4.0f));
AZ::Quaternion q5 = AZ::Quaternion::CreateFromFloat4(values);
EXPECT_TRUE((q5.GetX() == 10.0f) && (q5.GetY() == 20.0f) && (q5.GetZ() == 30.0f) && (q5.GetW() == 40.0f));
AZ::Quaternion q6 = AZ::Quaternion::CreateFromVector3(Vector3(1.0f, 2.0f, 3.0f));
EXPECT_TRUE((q6.GetX() == 1.0f) && (q6.GetY() == 2.0f) && (q6.GetZ() == 3.0f) && (q6.GetW() == 0.0f));
AZ::Quaternion q7 = AZ::Quaternion::CreateFromAxisAngle(Vector3::CreateAxisZ(), DegToRad(45.0f));
EXPECT_TRUE(q7.IsClose(AZ::Quaternion::CreateRotationZ(DegToRad(45.0f))));
AZ::Quaternion q8 = Transform::CreateRotationX(DegToRad(60.0f)).GetRotation();
EXPECT_TRUE(q8.IsClose(AZ::Quaternion(0.5f, 0.0f, 0.0f, 0.866f)));
AZ::Quaternion q9 = AZ::Quaternion::CreateFromMatrix3x3(Matrix3x3::CreateRotationX(DegToRad(120.0f)));
EXPECT_TRUE(q9.IsClose(AZ::Quaternion(0.866f, 0.0f, 0.0f, 0.5f)));
AZ::Quaternion q10 = AZ::Quaternion::CreateFromMatrix4x4(Matrix4x4::CreateRotationX(DegToRad(120.0f)));
EXPECT_TRUE(q10.IsClose(AZ::Quaternion(0.866f, 0.0f, 0.0f, 0.5f)));
AZ::Quaternion q11 = AZ::Quaternion::CreateFromMatrix3x3(Matrix3x3::CreateRotationX(DegToRad(-60.0f)));
EXPECT_TRUE(q11.IsClose(AZ::Quaternion(-0.5f, 0.0f, 0.0f, 0.866f)));
AZ::Quaternion q12 = AZ::Quaternion::CreateFromMatrix4x4(Matrix4x4::CreateRotationX(DegToRad(-60.0f)));
EXPECT_TRUE(q12.IsClose(AZ::Quaternion(-0.5f, 0.0f, 0.0f, 0.866f)));
AZ::Quaternion q13 = AZ::Quaternion::CreateFromMatrix3x3(Matrix3x3::CreateRotationY(DegToRad(120.0f)));
EXPECT_TRUE(q13.IsClose(AZ::Quaternion(0.0f, 0.866f, 0.0f, 0.5f)));
AZ::Quaternion q14 = AZ::Quaternion::CreateFromMatrix4x4(Matrix4x4::CreateRotationY(DegToRad(120.0f)));
EXPECT_TRUE(q14.IsClose(AZ::Quaternion(0.0f, 0.866f, 0.0f, 0.5f)));
AZ::Quaternion q15 = AZ::Quaternion::CreateFromMatrix3x3(Matrix3x3::CreateRotationY(DegToRad(-60.0f)));
EXPECT_TRUE(q15.IsClose(AZ::Quaternion(0.0f, -0.5f, 0.0f, 0.866f)));
AZ::Quaternion q16 = AZ::Quaternion::CreateFromMatrix4x4(Matrix4x4::CreateRotationY(DegToRad(-60.0f)));
EXPECT_TRUE(q16.IsClose(AZ::Quaternion(0.0f, -0.5f, 0.0f, 0.866f)));
AZ::Quaternion q17 = AZ::Quaternion::CreateFromMatrix3x3(Matrix3x3::CreateRotationZ(DegToRad(120.0f)));
EXPECT_TRUE(q17.IsClose(AZ::Quaternion(0.0f, 0.0f, 0.866f, 0.5f)));
AZ::Quaternion q18 = AZ::Quaternion::CreateFromMatrix4x4(Matrix4x4::CreateRotationZ(DegToRad(120.0f)));
EXPECT_TRUE(q18.IsClose(AZ::Quaternion(0.0f, 0.0f, 0.866f, 0.5f)));
AZ::Quaternion q19 = AZ::Quaternion::CreateFromMatrix3x3(Matrix3x3::CreateRotationZ(DegToRad(-60.0f)));
EXPECT_TRUE(q19.IsClose(AZ::Quaternion(0.0f, 0.0f, -0.5f, 0.866f)));
AZ::Quaternion q20 = AZ::Quaternion::CreateFromMatrix4x4(Matrix4x4::CreateRotationZ(DegToRad(-60.0f)));
EXPECT_TRUE(q20.IsClose(AZ::Quaternion(0.0f, 0.0f, -0.5f, 0.866f)));
}
TEST(MATH_Quaternion, TestCreate)
{
EXPECT_TRUE(AZ::Quaternion::CreateIdentity() == AZ::Quaternion(0.0f, 0.0f, 0.0f, 1.0f));
EXPECT_TRUE(AZ::Quaternion::CreateZero() == AZ::Quaternion(0.0f));
EXPECT_TRUE(AZ::Quaternion::CreateRotationX(DegToRad(60.0f)).IsClose(AZ::Quaternion(0.5f, 0.0f, 0.0f, 0.866f)));
EXPECT_TRUE(AZ::Quaternion::CreateRotationY(DegToRad(60.0f)).IsClose(AZ::Quaternion(0.0f, 0.5f, 0.0f, 0.866f)));
EXPECT_TRUE(AZ::Quaternion::CreateRotationZ(DegToRad(60.0f)).IsClose(AZ::Quaternion(0.0f, 0.0f, 0.5f, 0.866f)));
}
TEST(MATH_Quaternion, TestConcatenate)
{
Quaternion q1(1.0f, 2.0f, 3.0f, 4.0f);
Quaternion q2(-1.0f, -2.0f, -3.0f, -4.0f);
Quaternion result = q1 * q2;
EXPECT_TRUE(result.IsClose(Quaternion(-8.0f, -16.0f, -24.0f, -2.0f)));
}
TEST(MATH_Quaternion, TestShortestArc)
{
Vector3 v1 = Vector3(1.0f, 2.0f, 3.0f).GetNormalized();
Vector3 v2 = Vector3(-2.0f, 7.0f, -1.0f).GetNormalized();
Quaternion q3 = AZ::Quaternion::CreateShortestArc(v1, v2); //q3 should transform v1 into v2
EXPECT_TRUE(v2.IsClose(q3.TransformVector(v1), 1e-3f));
Quaternion q4 = AZ::Quaternion::CreateShortestArc(Vector3(1.0f, 0.0f, 0.0f), Vector3(0.0f, 1.0f, 0.0f));
EXPECT_TRUE((q4.TransformVector(Vector3(0.0f, 0.0f, 1.0f))).IsClose(Vector3(0.0f, 0.0f, 1.0f), 1e-3f)); //perpendicular vector should be unaffected
EXPECT_TRUE((q4.TransformVector(Vector3(0.0f, -1.0f, 0.0f))).IsClose(Vector3(1.0f, 0.0f, 0.0f), 1e-3f)); //make sure we rotate the right direction, i.e. actually shortest arc
v2 = (v1 + Vector3(1e-5f, 1e-5f, 1e-5f)).GetNormalized(); // test almost parallel vectors
Quaternion q5 = AZ::Quaternion::CreateShortestArc(v1, v2);
EXPECT_TRUE(v2.IsClose(q5.TransformVector(v1), 1e-3f));
v2 = (-v1 + Vector3(1e-5f, 1e-5f, 1e-5f)).GetNormalized(); // test almost anti-parallel vectors
Quaternion q6 = AZ::Quaternion::CreateShortestArc(v1, v2);
EXPECT_TRUE(v2.IsClose(q6.TransformVector(v1), 1e-3f));
}
TEST(MATH_Quaternion, TestGetSet)
{
Quaternion q1;
q1.SetX(10.0f);
EXPECT_TRUE(q1.GetX() == 10.0f);
q1.SetY(11.0f);
EXPECT_TRUE(q1.GetY() == 11.0f);
q1.SetZ(12.0f);
EXPECT_TRUE(q1.GetZ() == 12.0f);
q1.SetW(13.0f);
EXPECT_TRUE(q1.GetW() == 13.0f);
q1.Set(15.0f);
EXPECT_TRUE(q1 == AZ::Quaternion(15.0f));
q1.Set(2.0f, 3.0f, 4.0f, 5.0f);
EXPECT_TRUE(q1 == AZ::Quaternion(2.0f, 3.0f, 4.0f, 5.0f));
q1.Set(Vector3(5.0f, 6.0f, 7.0f), 8.0f);
EXPECT_TRUE(q1 == AZ::Quaternion(5.0f, 6.0f, 7.0f, 8.0f));
q1.Set(values);
EXPECT_TRUE((q1.GetX() == 10.0f) && (q1.GetY() == 20.0f) && (q1.GetZ() == 30.0f) && (q1.GetW() == 40.0f));
}
TEST(MATH_Quaternion, TestGetElementSetElement)
{
Quaternion q1;
q1.SetElement(0, 1.0f);
q1.SetElement(1, 2.0f);
q1.SetElement(2, 3.0f);
q1.SetElement(3, 4.0f);
EXPECT_TRUE(q1.GetElement(0) == 1.0f);
EXPECT_TRUE(q1.GetElement(1) == 2.0f);
EXPECT_TRUE(q1.GetElement(2) == 3.0f);
EXPECT_TRUE(q1.GetElement(3) == 4.0f);
}
TEST(MATH_Quaternion, TestIndexOperators)
{
Quaternion q1(1.0f, 2.0f, 3.0f, 4.0f);
EXPECT_TRUE(q1(0) == 1.0f);
EXPECT_TRUE(q1(1) == 2.0f);
EXPECT_TRUE(q1(2) == 3.0f);
EXPECT_TRUE(q1(3) == 4.0f);
}
TEST(MATH_Quaternion, TestIsIdentity)
{
Quaternion q1(0.0f, 0.0f, 0.0f, 1.0f);
EXPECT_TRUE(q1.IsIdentity());
}
TEST(MATH_Quaternion, TestConjugate)
{
Quaternion q1(1.0f, 2.0f, 3.0f, 4.0f);
EXPECT_TRUE(q1.GetConjugate() == AZ::Quaternion(-1.0f, -2.0f, -3.0f, 4.0f));
}
TEST(MATH_Quaternion, TestInverse)
{
Quaternion q1 = AZ::Quaternion::CreateRotationX(DegToRad(25.0f)) * AZ::Quaternion::CreateRotationY(DegToRad(70.0f));
EXPECT_TRUE((q1 * q1.GetInverseFast()).IsClose(AZ::Quaternion::CreateIdentity()));
Quaternion q2 = q1;
q2.InvertFast();
EXPECT_TRUE(q1.GetX() == -q2.GetX());
EXPECT_TRUE(q1.GetY() == -q2.GetY());
EXPECT_TRUE(q1.GetZ() == -q2.GetZ());
EXPECT_TRUE(q1.GetW() == q2.GetW());
EXPECT_TRUE((q1 * q2).IsClose(AZ::Quaternion::CreateIdentity()));
}
TEST(MATH_Quaternion, TestGetInverseFull)
{
Quaternion q1(1.0f, 2.0f, 3.0f, 4.0f);
EXPECT_TRUE((q1 * q1.GetInverseFull()).IsClose(AZ::Quaternion::CreateIdentity()));
}
TEST(MATH_Quaternion, TestDot)
{
EXPECT_NEAR(AZ::Quaternion(1.0f, 2.0f, 3.0f, 4.0f).Dot(AZ::Quaternion(-1.0f, 5.0f, 3.0f, 2.0f)), 26.0f, normalizeEpsilon);
}
TEST(MATH_Quaternion, TestLength)
{
EXPECT_NEAR(AZ::Quaternion(-1.0f, 2.0f, 1.0f, 3.0f).GetLengthSq(), 15.0f, normalizeEpsilon);
EXPECT_NEAR(AZ::Quaternion(-4.0f, 2.0f, 0.0f, 4.0f).GetLength(), 6.0f, normalizeEpsilon);
}
TEST(MATH_Quaternion, TestNormalize)
{
EXPECT_TRUE(AZ::Quaternion(0.0f, -4.0f, 2.0f, 4.0f).GetNormalized().IsClose(AZ::Quaternion(0.0f, -0.66666f, 0.33333f, 0.66666f)));
Quaternion q1(2.0f, 0.0f, 4.0f, -4.0f);
q1.Normalize();
EXPECT_TRUE(q1.IsClose(AZ::Quaternion(0.33333f, 0.0f, 0.66666f, -0.66666f)));
q1.Set(2.0f, 0.0f, 4.0f, -4.0f);
float length = q1.NormalizeWithLength();
EXPECT_NEAR(length, 6.0f, normalizeEpsilon);
EXPECT_TRUE(q1.IsClose(AZ::Quaternion(0.33333f, 0.0f, 0.66666f, -0.66666f)));
}
TEST(MATH_Quaternion, TestInterpolation)
{
EXPECT_TRUE(AZ::Quaternion(1.0f, 2.0f, 3.0f, 4.0f).Lerp(AZ::Quaternion(2.0f, 3.0f, 4.0f, 5.0f), 0.5f).IsClose(AZ::Quaternion(1.5f, 2.5f, 3.5f, 4.5f)));
EXPECT_TRUE(AZ::Quaternion::CreateRotationX(DegToRad(10.0f)).Slerp(AZ::Quaternion::CreateRotationY(DegToRad(60.0f)), 0.5f).IsClose(AZ::Quaternion(0.045f, 0.259f, 0.0f, 0.965f), 1e-3f));
EXPECT_TRUE(AZ::Quaternion::CreateRotationX(DegToRad(10.0f)).Squad(AZ::Quaternion::CreateRotationY(DegToRad(60.0f)), AZ::Quaternion::CreateRotationZ(DegToRad(35.0f)), AZ::Quaternion::CreateRotationX(DegToRad(80.0f)), 0.5f).IsClose(AZ::Quaternion(0.2f, 0.132f, 0.083f, 0.967f), 1e-3f));
}
TEST(MATH_Quaternion, TestClose)
{
EXPECT_TRUE(AZ::Quaternion(1.0f, 2.0f, 3.0f, 4.0f).IsClose(AZ::Quaternion(1.0f, 2.0f, 3.0f, 4.0f)));
EXPECT_TRUE(!AZ::Quaternion(1.0f, 2.0f, 3.0f, 4.0f).IsClose(AZ::Quaternion(1.0f, 2.0f, 3.0f, 5.0f)));
EXPECT_TRUE(AZ::Quaternion(1.0f, 2.0f, 3.0f, 4.0f).IsClose(AZ::Quaternion(1.0f, 2.0f, 3.0f, 4.4f), 0.5f));
}
TEST(MATH_Quaternion, TestOperators)
{
EXPECT_TRUE((-AZ::Quaternion(1.0f, 2.0f, 3.0f, 4.0f)) == AZ::Quaternion(-1.0f, -2.0f, -3.0f, -4.0f));
EXPECT_TRUE((AZ::Quaternion(1.0f, 2.0f, 3.0f, 4.0f) + AZ::Quaternion(2.0f, 3.0f, 5.0f, -1.0f)).IsClose(AZ::Quaternion(3.0f, 5.0f, 8.0f, 3.0f)));
EXPECT_TRUE((AZ::Quaternion(1.0f, 2.0f, 3.0f, 4.0f) - AZ::Quaternion(2.0f, 3.0f, 5.0f, -1.0f)).IsClose(AZ::Quaternion(-1.0f, -1.0f, -2.0f, 5.0f)));
EXPECT_TRUE((AZ::Quaternion(1.0f, 2.0f, 3.0f, 4.0f) * AZ::Quaternion(2.0f, 3.0f, 5.0f, -1.0f)).IsClose(AZ::Quaternion(8.0f, 11.0f, 16.0f, -27.0f)));
EXPECT_TRUE((AZ::Quaternion(1.0f, 2.0f, 3.0f, 4.0f) * 2.0f).IsClose(AZ::Quaternion(2.0f, 4.0f, 6.0f, 8.0f)));
EXPECT_TRUE((2.0f * AZ::Quaternion(1.0f, 2.0f, 3.0f, 4.0f)).IsClose(AZ::Quaternion(2.0f, 4.0f, 6.0f, 8.0f)));
EXPECT_TRUE((AZ::Quaternion(1.0f, 2.0f, 3.0f, 4.0f) / 2.0f).IsClose(AZ::Quaternion(0.5f, 1.0f, 1.5f, 2.0f)));
Quaternion q1(1.0f, 2.0f, 3.0f, 4.0f);
q1 += AZ::Quaternion(5.0f, 6.0f, 7.0f, 8.0f);
EXPECT_TRUE(q1.IsClose(AZ::Quaternion(6.0f, 8.0f, 10.0f, 12.0f)));
q1 -= AZ::Quaternion(3.0f, -1.0f, 5.0f, 7.0f);
EXPECT_TRUE(q1.IsClose(AZ::Quaternion(3.0f, 9.0f, 5.0f, 5.0f)));
q1.Set(1.0f, 2.0f, 3.0f, 4.0f);
q1 *= AZ::Quaternion(2.0f, 3.0f, 5.0f, -1.0f);
EXPECT_TRUE(q1.IsClose(AZ::Quaternion(8.0f, 11.0f, 16.0f, -27.0f)));
q1 *= 2.0f;
EXPECT_TRUE(q1.IsClose(AZ::Quaternion(16.0f, 22.0f, 32.0f, -54.0f)));
q1 /= 4.0f;
EXPECT_TRUE(q1.IsClose(AZ::Quaternion(4.0f, 5.5f, 8.0f, -13.5f)));
}
TEST(MATH_Quaternion, TestEquality)
{
Quaternion q3(1.0f, 2.0f, 3.0f, 4.0f);
EXPECT_TRUE(q3 == AZ::Quaternion(1.0f, 2.0f, 3.0f, 4.0));
EXPECT_TRUE(!(q3 == AZ::Quaternion(1.0f, 2.0f, 3.0f, 5.0f)));
EXPECT_TRUE(q3 != AZ::Quaternion(1.0f, 2.0f, 3.0f, 5.0f));
EXPECT_TRUE(!(q3 != AZ::Quaternion(1.0f, 2.0f, 3.0f, 4.0f)));
}
TEST(MATH_Quaternion, TestVectorTransform)
{
EXPECT_TRUE((AZ::Quaternion::CreateRotationX(DegToRad(45.0f)).TransformVector(Vector3(4.0f, 1.0f, 0.0f))).IsClose(Vector3(4.0f, 0.7071f, 0.7071f)));
}
TEST(MATH_Quaternion, TestGetImaginary)
{
Quaternion q1(21.0f, 22.0f, 23.0f, 24.0f);
EXPECT_TRUE(q1.GetImaginary() == Vector3(21.0f, 22.0f, 23.0f));
}
TEST(MATH_Quaternion, TestGetAngle)
{
Quaternion q1 = AZ::Quaternion::CreateRotationX(DegToRad(35.0f));
EXPECT_TRUE(AZ::IsClose(q1.GetAngle(), DegToRad(35.0f)));
}
TEST(MATH_Quaternion, TestConcatenation)
{
Quaternion q1 = AZ::Quaternion::CreateRotationZ(DegToRad(90.0f));
Quaternion q2 = AZ::Quaternion::CreateRotationX(DegToRad(90.0f));
Vector3 v = (q2 * q1).TransformVector(Vector3(1.0f, 0.0f, 0.0f));
EXPECT_TRUE(v.IsClose(Vector3(0.0f, 0.0f, 1.0f)));
}
TEST(MATH_Quaternion, ToEulerDegrees)
{
float halfAngle = 0.5f * Constants::QuarterPi;
float sin = sinf(halfAngle);
float cos = cosf(halfAngle);
AZ::Quaternion testQuat = AZ::Quaternion::CreateFromVector3AndValue(sin * AZ::Vector3::CreateAxisX(), cos);
AZ::Vector3 resultVector = testQuat.GetEulerDegrees();
EXPECT_TRUE(resultVector.IsClose(AZ::Vector3(45.0f, 0.0f, 0.0f)));
resultVector = ConvertQuaternionToEulerDegrees(testQuat);
EXPECT_TRUE(resultVector.IsClose(AZ::Vector3(45.0f, 0.0f, 0.0f)));
}
TEST(MATH_Quaternion, ToEulerRadians)
{
constexpr float getEulerRadiansEpsilon = 0.001f;
float halfAngle = 0.5f * Constants::HalfPi;
float sin = sinf(halfAngle);
float cos = cosf(halfAngle);
AZ::Quaternion testQuat = AZ::Quaternion::CreateFromVector3AndValue(sin * AZ::Vector3::CreateAxisY(), cos);
AZ::Vector3 resultVector = testQuat.GetEulerRadians();
EXPECT_NEAR(Constants::HalfPi, static_cast<float>(resultVector.GetY()), getEulerRadiansEpsilon);
resultVector = ConvertQuaternionToEulerRadians(testQuat);
EXPECT_NEAR(Constants::HalfPi, static_cast<float>(resultVector.GetY()), getEulerRadiansEpsilon);
}
using QuaternionEulerFixture = ::testing::TestWithParam<AZ::Quaternion>;
static const AZ::Quaternion TestUnitQuaternions[] = {
AZ::Quaternion(0.64f, 0.36f, 0.48f, 0.48f),
AZ::Quaternion(0.70f, -0.34f, 0.10f, 0.62f),
AZ::Quaternion(-0.38f, 0.34f, 0.70f, -0.50f),
AZ::Quaternion(0.70f, -0.34f, -0.38f, 0.50f),
AZ::Quaternion(0.00f, 0.00f, -0.28f, 0.96f),
AZ::Quaternion(0.24f, -0.64f, 0.72f, 0.12f),
AZ::Quaternion(-0.66f, 0.62f, 0.42f, 0.06f)
};
TEST_P(QuaternionEulerFixture, EulerOrderCorrect)
{
// the quaternion should be equivalent to a series of rotations in the order z, then y, then x
const AZ::Quaternion quaternion = GetParam();
const AZ::Vector3 euler = quaternion.GetEulerRadians();
const AZ::Quaternion productOfRotations =
AZ::Quaternion::CreateRotationX(euler.GetX()) *
AZ::Quaternion::CreateRotationY(euler.GetY()) *
AZ::Quaternion::CreateRotationZ(euler.GetZ());
EXPECT_TRUE(productOfRotations.IsClose(quaternion) || productOfRotations.IsClose(-quaternion));
}
TEST_P(QuaternionEulerFixture, QuaternionEulerQuaternionCycle)
{
// converting a quaternion to Euler angles and back again should recover the original quaternion
// note that because the Euler angle representation is not unique, the same is not necessarily true
// for Euler -> quaternion -> Euler
const AZ::Quaternion originalQuaternion = GetParam();
const AZ::Vector3 euler = originalQuaternion.GetEulerRadians();
const AZ::Quaternion recoveredQuaternion = AZ::ConvertEulerRadiansToQuaternion(euler);
EXPECT_TRUE(recoveredQuaternion.IsClose(originalQuaternion) || recoveredQuaternion.IsClose(-originalQuaternion));
}
TEST_P(QuaternionEulerFixture, EulerViaTransformEquivalentToDirectEuler)
{
// quaternion -> transform -> Euler -> quaternion should give an equivalent result to
// quaternion -> Euler -> quaternion
const AZ::Quaternion originalQuaternion = GetParam();
const AZ::Vector3 euler1 = originalQuaternion.GetEulerRadians();
const AZ::Vector3 euler2 = AZ::Transform::CreateFromQuaternion(originalQuaternion).GetEulerRadians();
const AZ::Quaternion recoveredQuaternion1 = AZ::ConvertEulerRadiansToQuaternion(euler1);
const AZ::Quaternion recoveredQuaternion2 = AZ::ConvertEulerRadiansToQuaternion(euler2);
EXPECT_TRUE(recoveredQuaternion1.IsClose(recoveredQuaternion2) || recoveredQuaternion1.IsClose(-recoveredQuaternion2));
}
INSTANTIATE_TEST_CASE_P(MATH_Quaternion, QuaternionEulerFixture, ::testing::ValuesIn(TestUnitQuaternions));
TEST(MATH_Quaternion, FromEulerDegrees)
{
const AZ::Vector3 testDegrees(45.0f, 45.0f, 45.0f);
AZ::Quaternion testQuat;
testQuat.SetFromEulerDegrees(testDegrees);
EXPECT_TRUE(testQuat.IsClose(AZ::Quaternion(0.46193981170654296875f, 0.1913417130708694458f, 0.46193981170654296875f, 0.73253774642944335938f), 0.0000001f));
}
TEST(MATH_Quaternion, FromEulerRadians)
{
const AZ::Vector3 testRadians(Constants::QuarterPi, Constants::QuarterPi, Constants::QuarterPi);
AZ::Quaternion testQuat;
testQuat.SetFromEulerRadians(testRadians);
EXPECT_TRUE(testQuat.IsClose(AZ::Quaternion(0.46193981170654296875f, 0.1913417130708694458f, 0.46193981170654296875f, 0.73253774642944335938f), 0.0000001f));
}
TEST(MATH_Quaternion, FromAxisAngle)
{
AZ::Quaternion q10 = AZ::Quaternion::CreateFromAxisAngle(AZ::Vector3::CreateAxisZ(), Constants::QuarterPi);
EXPECT_TRUE(q10.IsClose(AZ::Quaternion::CreateRotationZ(Constants::QuarterPi)));
q10 = AZ::Quaternion::CreateFromAxisAngle(AZ::Vector3::CreateAxisY(), Constants::HalfPi);
EXPECT_TRUE(q10.IsClose(AZ::Quaternion::CreateRotationY(Constants::HalfPi)));
q10 = AZ::Quaternion::CreateFromAxisAngle(AZ::Vector3::CreateAxisX(), Constants::TwoPi / 3.0f);
EXPECT_TRUE(q10.IsClose(AZ::Quaternion::CreateRotationX(Constants::TwoPi / 3.0f)));
}
TEST(MATH_Quaternion, ToAxisAngle)
{
constexpr float axisAngleEpsilon = 0.002f;
AZ::Quaternion testQuat = AZ::Quaternion::CreateIdentity();
Vector3 resultAxis = Vector3::CreateZero();
float resultAngle{};
testQuat.ConvertToAxisAngle(resultAxis, resultAngle);
EXPECT_TRUE(resultAxis.IsClose(Vector3::CreateAxisY()));
EXPECT_NEAR(0.0f, resultAngle, axisAngleEpsilon);
}
TEST(MATH_Quaternion, MatrixConversionTest)
{
Matrix4x4 rotMatrix = Matrix4x4::CreateRotationZ(DegToRad(90.0f));
AZ::Quaternion rotQuat = AZ::Quaternion::CreateRotationZ(DegToRad(90.0f));
AZ::Quaternion q = AZ::Quaternion::CreateFromMatrix4x4(rotMatrix);
AZ_TEST_ASSERT(q.IsClose(rotQuat));
Matrix4x4 m = Matrix4x4::CreateFromQuaternion(rotQuat);
AZ_TEST_ASSERT(m.IsClose(rotMatrix));
}
}
@@ -0,0 +1,198 @@
/*
* 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/Sfmt.h>
#include <AzCore/UnitTest/TestTypes.h>
using namespace AZ;
namespace UnitTest
{
class MATH_SfmtTest
: public AllocatorsFixture
{
static const int BLOCK_SIZE = 100000;
static const int BLOCK_SIZE64 = 50000;
static const int COUNT = 1000;
AZ::u64* array1;
AZ::u64* array2;
public:
void SetUp() override
{
AllocatorsFixture::SetUp();
array1 = (AZ::u64*)azmalloc(sizeof(AZ::u64) * 2 * (BLOCK_SIZE / 4), AZStd::alignment_of<AZ::u64>::value);
array2 = (AZ::u64*)azmalloc(sizeof(AZ::u64) * 2 * (10000 / 4), AZStd::alignment_of<AZ::u64>::value);
}
void TearDown() override
{
azfree(array1);
azfree(array2);
AllocatorsFixture::TearDown();
}
void check32()
{
int i;
AZ::u32* array32 = (AZ::u32*)array1;
AZ::u32* array32_2 = (AZ::u32*)array2;
AZ::u32 ini[] = { 0x1234, 0x5678, 0x9abc, 0xdef0 };
Sfmt g;
EXPECT_LT(g.GetMinArray32Size(), 10000);
g.FillArray32(array32, 10000);
g.FillArray32(array32_2, 10000);
g.Seed();
for (i = 0; i < 10000; i++)
{
EXPECT_NE(array32[i], g.Rand32());
}
for (i = 0; i < 700; i++)
{
EXPECT_NE(array32_2[i], g.Rand32());
}
g.Seed(ini, 4);
g.FillArray32(array32, 10000);
g.FillArray32(array32_2, 10000);
g.Seed(ini, 4);
for (i = 0; i < 10000; i++)
{
EXPECT_EQ(array32[i], g.Rand32());
}
for (i = 0; i < 700; i++)
{
EXPECT_EQ(array32_2[i], g.Rand32());
}
}
void check64()
{
int i;
AZ::u64* array64 = (AZ::u64*)array1;
AZ::u64* array64_2 = (AZ::u64*)array2;
AZ::u32 ini[] = { 5, 4, 3, 2, 1 };
Sfmt g;
EXPECT_LT(g.GetMinArray64Size(), 5000);
g.FillArray64(array64, 5000);
g.FillArray64(array64_2, 5000);
g.Seed();
for (i = 0; i < 5000; i++)
{
EXPECT_NE(array64[i], g.Rand64());
}
for (i = 0; i < 700; i++)
{
EXPECT_NE(array64_2[i], g.Rand64());
}
g.Seed(ini, 5);
g.FillArray64(array64, 5000);
g.FillArray64(array64_2, 5000);
g.Seed(ini, 5);
for (i = 0; i < 5000; i++)
{
EXPECT_EQ(array64[i], g.Rand64());
}
for (i = 0; i < 700; i++)
{
EXPECT_EQ(array64_2[i], g.Rand64());
}
}
};
TEST_F(MATH_SfmtTest, Test32Bit)
{
check32();
}
TEST_F(MATH_SfmtTest, Test64Bit)
{
check64();
}
TEST_F(MATH_SfmtTest, TestParallel32)
{
Sfmt sfmt;
auto threadFunc = [&sfmt]()
{
for (int i = 0; i < 10000; ++i)
{
sfmt.Rand32();
}
};
AZStd::thread threads[8];
for (size_t threadIdx = 0; threadIdx < AZ_ARRAY_SIZE(threads); ++threadIdx)
{
threads[threadIdx] = AZStd::thread(threadFunc);
}
for (auto& thread : threads)
{
thread.join();
}
}
TEST_F(MATH_SfmtTest, TestParallel64)
{
Sfmt sfmt;
auto threadFunc = [&sfmt]()
{
for (int i = 0; i < 10000; ++i)
{
sfmt.Rand64();
}
};
AZStd::thread threads[8];
for (size_t threadIdx = 0; threadIdx < AZ_ARRAY_SIZE(threads); ++threadIdx)
{
threads[threadIdx] = AZStd::thread(threadFunc);
}
for (auto& thread : threads)
{
thread.join();
}
}
TEST_F(MATH_SfmtTest, TestParallelInterleaved)
{
Sfmt sfmt;
auto threadFunc = [&sfmt]()
{
for (int i = 0; i < 10000; ++i)
{
AZ::u64 roll = sfmt.Rand64();
if (roll % 2 == 0)
{
sfmt.Rand32();
}
else
{
sfmt.Rand64();
}
}
};
AZStd::thread threads[8];
for (size_t threadIdx = 0; threadIdx < AZ_ARRAY_SIZE(threads); ++threadIdx)
{
threads[threadIdx] = AZStd::thread(threadFunc);
}
for (auto& thread : threads)
{
thread.join();
}
}
}
@@ -0,0 +1,169 @@
/*
* 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.
*
*/
#if defined(HAVE_BENCHMARK)
#include <AzCore/Math/Frustum.h>
#include <AzCore/Math/Sphere.h>
#include <AzCore/Math/ShapeIntersection.h>
#include <AzCore/UnitTest/TestTypes.h>
#include <random>
namespace Benchmark
{
AZ::Plane near1 = AZ::Plane::CreateFromNormalAndPoint(AZ::Vector3(0.f, 1.f, 0.f), AZ::Vector3(0.f, -5.f, 0.f));
AZ::Plane far1 = AZ::Plane::CreateFromNormalAndPoint(AZ::Vector3(0.f, -1.f, 0.f), AZ::Vector3(0.f, 5.f, 0.f));
AZ::Plane left1 = AZ::Plane::CreateFromNormalAndPoint(AZ::Vector3(1.f, 0.f, 0.f), AZ::Vector3(-5.f, 0.f, 0.f));
AZ::Plane right1 = AZ::Plane::CreateFromNormalAndPoint(AZ::Vector3(-1.f, 0.f, 0.f), AZ::Vector3(5.f, 0.f, 0.f));
AZ::Plane top1 = AZ::Plane::CreateFromNormalAndPoint(AZ::Vector3(0.f, 0.f, -1.f), AZ::Vector3(0.f, 0.f, 5.f));
AZ::Plane bottom1 = AZ::Plane::CreateFromNormalAndPoint(AZ::Vector3(0.f, 0.f, 1.f), AZ::Vector3(0.f, 0.f, -5.f));
AZ::Frustum frustum1 = AZ::Frustum(near1, far1, left1, right1, top1, bottom1);
AZ::Plane near2 = AZ::Plane::CreateFromNormalAndPoint(AZ::Vector3(0.f, 1.f, 0.f), AZ::Vector3(0.f, -2.f, 0.f));
AZ::Plane far2 = AZ::Plane::CreateFromNormalAndPoint(AZ::Vector3(0.f, -1.f, 0.f), AZ::Vector3(0.f, 2.f, 0.f));
AZ::Plane left2 = AZ::Plane::CreateFromNormalAndPoint(AZ::Vector3(1.f, 0.f, 0.f), AZ::Vector3(-2.f, 0.f, 0.f));
AZ::Plane right2 = AZ::Plane::CreateFromNormalAndPoint(AZ::Vector3(-1.f, 0.f, 0.f), AZ::Vector3(2.f, 0.f, 0.f));
AZ::Plane top2 = AZ::Plane::CreateFromNormalAndPoint(AZ::Vector3(0.f, 0.f, -1.f), AZ::Vector3(0.f, 0.f, 2.f));
AZ::Plane bottom2 = AZ::Plane::CreateFromNormalAndPoint(AZ::Vector3(0.f, 0.f, 1.f), AZ::Vector3(0.f, 0.f, -2.f));
AZ::Frustum frustum2 = AZ::Frustum(near2, far2, left2, right2, top2, bottom2);
class BM_MathShapeIntersection
: public benchmark::Fixture
{
public:
void SetUp([[maybe_unused]] const ::benchmark::State& state) override
{
m_testDataArray.resize(1000);
const unsigned int seed = 1;
std::mt19937_64 rng(seed);
std::uniform_real_distribution<float> unif(-100.0f, 100.0f);
std::generate(m_testDataArray.begin(), m_testDataArray.end(), [&unif, &rng]()
{
AZ::Vector3 vector1 = AZ::Vector3(unif(rng), unif(rng), unif(rng));
AZ::Vector3 vector2 = AZ::Vector3(unif(rng), unif(rng), unif(rng));
AZ::Vector3 vector3 = AZ::Vector3(unif(rng), unif(rng), unif(rng));
TestData testData;
testData.sphere = AZ::Sphere(vector1, unif(rng));
testData.aabb = AZ::Aabb::CreateCenterHalfExtents(vector2, vector3.GetAbs());
testData.vector1 = AZ::Vector3(unif(rng), unif(rng), unif(rng));
testData.vector2 = AZ::Vector3(unif(rng), unif(rng), unif(rng));
return testData;
});
}
struct TestData
{
AZ::Sphere sphere;
AZ::Aabb aabb;
AZ::Vector3 vector1;
AZ::Vector3 vector2;
};
std::vector<TestData> m_testDataArray;
};
BENCHMARK_F(BM_MathShapeIntersection, ContainsFrustumPoint)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
bool result;
result = AZ::ShapeIntersection::Contains(frustum1, testData.vector1);
benchmark::DoNotOptimize(result);
result = AZ::ShapeIntersection::Contains(frustum1, testData.vector2);
benchmark::DoNotOptimize(result);
result = AZ::ShapeIntersection::Contains(frustum2, testData.vector1);
benchmark::DoNotOptimize(result);
result = AZ::ShapeIntersection::Contains(frustum2, testData.vector2);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathShapeIntersection, OverlapsFrustumSphere)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
bool result;
result = AZ::ShapeIntersection::Overlaps(frustum1, testData.sphere);
benchmark::DoNotOptimize(result);
result = AZ::ShapeIntersection::Overlaps(frustum2, testData.sphere);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathShapeIntersection, ContainsFrustumSphere)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
bool result;
result = AZ::ShapeIntersection::Contains(frustum1, testData.sphere);
benchmark::DoNotOptimize(result);
result = AZ::ShapeIntersection::Contains(frustum2, testData.sphere);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathShapeIntersection, OverlapsFrustumAabb)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
bool result;
result = AZ::ShapeIntersection::Overlaps(frustum1, testData.aabb);
benchmark::DoNotOptimize(result);
result = AZ::ShapeIntersection::Overlaps(frustum2, testData.aabb);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathShapeIntersection, ContainsFrustumAabb)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
bool result;
result = AZ::ShapeIntersection::Contains(frustum1, testData.aabb);
benchmark::DoNotOptimize(result);
result = AZ::ShapeIntersection::Contains(frustum2, testData.aabb);
benchmark::DoNotOptimize(result);
}
}
}
}
#endif
@@ -0,0 +1,150 @@
/*
* 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/UnitTest/TestTypes.h>
#include <AzCore/Math/Quaternion.h>
#include <AzCore/Math/Frustum.h>
#include <AzCore/Math/Sphere.h>
#include <AzCore/Math/ShapeIntersection.h>
namespace UnitTest
{
TEST(MATH_ShapeIntersection, Test)
{
//Assumes +x runs to the 'right', +y runs 'out' and +z points 'up'
//A frustum is defined by 6 planes. In this case a box shape.
AZ::Plane near_value = AZ::Plane::CreateFromNormalAndPoint(AZ::Vector3(0.f, 1.f, 0.f), AZ::Vector3(0.f, -5.f, 0.f));
AZ::Plane far_value = AZ::Plane::CreateFromNormalAndPoint(AZ::Vector3(0.f, -1.f, 0.f), AZ::Vector3(0.f, 5.f, 0.f));
AZ::Plane left = AZ::Plane::CreateFromNormalAndPoint(AZ::Vector3(1.f, 0.f, 0.f), AZ::Vector3(-5.f, 0.f, 0.f));
AZ::Plane right = AZ::Plane::CreateFromNormalAndPoint(AZ::Vector3(-1.f, 0.f, 0.f), AZ::Vector3(5.f, 0.f, 0.f));
AZ::Plane top = AZ::Plane::CreateFromNormalAndPoint(AZ::Vector3(0.f, 0.f, -1.f), AZ::Vector3(0.f, 0.f, 5.f));
AZ::Plane bottom = AZ::Plane::CreateFromNormalAndPoint(AZ::Vector3(0.f, 0.f, 1.f), AZ::Vector3(0.f, 0.f, -5.f));
AZ::Frustum frustum(near_value, far_value, left, right, top, bottom);
AZ::Plane near1 = AZ::Plane::CreateFromNormalAndPoint(AZ::Vector3(0.f, 1.f, 0.f), AZ::Vector3(0.f, -2.f, 0.f));
AZ::Plane far1 = AZ::Plane::CreateFromNormalAndPoint(AZ::Vector3(0.f, -1.f, 0.f), AZ::Vector3(0.f, 2.f, 0.f));
AZ::Plane left1 = AZ::Plane::CreateFromNormalAndPoint(AZ::Vector3(1.f, 0.f, 0.f), AZ::Vector3(-2.f, 0.f, 0.f));
AZ::Plane right1 = AZ::Plane::CreateFromNormalAndPoint(AZ::Vector3(-1.f, 0.f, 0.f), AZ::Vector3(2.f, 0.f, 0.f));
AZ::Plane top1 = AZ::Plane::CreateFromNormalAndPoint(AZ::Vector3(0.f, 0.f, -1.f), AZ::Vector3(0.f, 0.f, 2.f));
AZ::Plane bottom1 = AZ::Plane::CreateFromNormalAndPoint(AZ::Vector3(0.f, 0.f, 1.f), AZ::Vector3(0.f, 0.f, -2.f));
AZ::Frustum frustum1(near1, far1, left1, right1, top1, bottom1);
AZ::Sphere unitSphere = AZ::Sphere::CreateUnitSphere();
AZ::Sphere sphere1(AZ::Vector3(10.f, 10.f, 10.f), 20.f);
AZ::Sphere sphere2(AZ::Vector3(12.f, 12.f, 12.f), 13.f);
AZ::Aabb unitBox = AZ::Aabb::CreateCenterHalfExtents(AZ::Vector3::CreateZero(), AZ::Vector3(1.f, 1.f, 1.f));
AZ::Aabb aabb = AZ::Aabb::CreateCenterHalfExtents(AZ::Vector3(10.f, 10.f, 10.f), AZ::Vector3(1.f, 1.f, 1.f));
AZ::Aabb aabb1 = AZ::Aabb::CreateCenterHalfExtents(AZ::Vector3(10.f, 10.f, 10.f), AZ::Vector3(100.f, 100.f, 100.f));
AZ::Vector3 point(0.f, 0.f, 0.f);
AZ::Vector3 point1(10.f, 10.f, 10.f);
{
AZ::Vector3 intersectionPoint;
EXPECT_TRUE(AZ::ShapeIntersection::IntersectThreePlanes(near_value, left, bottom, intersectionPoint));
EXPECT_TRUE(intersectionPoint.IsClose(AZ::Vector3(-5.f, -5.f, -5.f)));
EXPECT_FALSE(AZ::ShapeIntersection::IntersectThreePlanes(near_value, far_value, bottom, intersectionPoint));
}
{
AZ::Vector3 intersectionPoint;
EXPECT_TRUE(AZ::ShapeIntersection::IntersectThreePlanes(near_value, left, bottom, intersectionPoint));
EXPECT_TRUE(intersectionPoint.IsClose(AZ::Vector3(-5.f, -5.f, -5.f)));
EXPECT_FALSE(AZ::ShapeIntersection::IntersectThreePlanes(near_value, far_value, bottom, intersectionPoint));
}
EXPECT_TRUE(AZ::ShapeIntersection::Overlaps(aabb, aabb1));
EXPECT_TRUE(AZ::ShapeIntersection::Overlaps(aabb1, aabb));
EXPECT_TRUE(AZ::ShapeIntersection::Overlaps(unitSphere, unitBox));
EXPECT_TRUE(AZ::ShapeIntersection::Overlaps(unitSphere, unitBox));
EXPECT_TRUE(AZ::ShapeIntersection::Overlaps(unitSphere, aabb1));
EXPECT_TRUE(AZ::ShapeIntersection::Overlaps(unitSphere, frustum));
EXPECT_TRUE(AZ::ShapeIntersection::Overlaps(sphere1, frustum));
EXPECT_TRUE(AZ::ShapeIntersection::Overlaps(unitSphere, sphere1));
EXPECT_TRUE(AZ::ShapeIntersection::Overlaps(sphere1, unitSphere));
EXPECT_TRUE(AZ::ShapeIntersection::Overlaps(frustum, unitBox));
EXPECT_TRUE(AZ::ShapeIntersection::Overlaps(frustum, aabb1));
EXPECT_TRUE(AZ::ShapeIntersection::Overlaps(sphere1, far_value));
EXPECT_FALSE(AZ::ShapeIntersection::Overlaps(frustum, aabb));
EXPECT_FALSE(AZ::ShapeIntersection::Overlaps(unitSphere, aabb));
EXPECT_FALSE(AZ::ShapeIntersection::Overlaps(unitSphere, sphere2));
EXPECT_FALSE(AZ::ShapeIntersection::Overlaps(unitSphere, near_value));
EXPECT_FALSE(AZ::ShapeIntersection::Contains(aabb, aabb1));
EXPECT_TRUE(AZ::ShapeIntersection::Contains(aabb1, aabb));
EXPECT_TRUE(AZ::ShapeIntersection::Contains(unitBox, unitSphere));
EXPECT_FALSE(AZ::ShapeIntersection::Contains(aabb, unitSphere));
EXPECT_TRUE(AZ::ShapeIntersection::Contains(unitBox, unitSphere));
EXPECT_TRUE(AZ::ShapeIntersection::Contains(aabb1, unitSphere));
EXPECT_TRUE(AZ::ShapeIntersection::Contains(frustum, unitSphere));
EXPECT_TRUE(AZ::ShapeIntersection::Contains(unitSphere, unitSphere));
EXPECT_TRUE(AZ::ShapeIntersection::Contains(sphere1, aabb));
EXPECT_TRUE(AZ::ShapeIntersection::Contains(frustum, unitBox));
EXPECT_TRUE(AZ::ShapeIntersection::Contains(unitSphere, point));
EXPECT_TRUE(AZ::ShapeIntersection::Contains(sphere1, point1));
EXPECT_TRUE(AZ::ShapeIntersection::Contains(frustum, point));
EXPECT_FALSE(AZ::ShapeIntersection::Contains(frustum, sphere1));
EXPECT_FALSE(AZ::ShapeIntersection::Contains(unitSphere, unitBox));
EXPECT_FALSE(AZ::ShapeIntersection::Contains(frustum, aabb));
EXPECT_FALSE(AZ::ShapeIntersection::Contains(unitSphere, point1));
EXPECT_FALSE(AZ::ShapeIntersection::Contains(frustum, point1));
{
AZ::Sphere s(AZ::Vector3(0.0f, -4.4f, 0.0f), 0.5f);
AZ::Sphere s1(AZ::Vector3(0.0f, -5.1f, 0.0f), 0.5f);
AZ::Sphere s2(AZ::Vector3(0.0f, -5.6f, 0.0f), 0.5f);
EXPECT_TRUE(AZ::ShapeIntersection::Classify(near_value, s) == AZ::IntersectResult::Interior);
EXPECT_TRUE(AZ::ShapeIntersection::Classify(near_value, s1) == AZ::IntersectResult::Overlaps);
EXPECT_TRUE(AZ::ShapeIntersection::Classify(near_value, s2) == AZ::IntersectResult::Exterior);
EXPECT_TRUE(AZ::ShapeIntersection::Classify(frustum, s) == AZ::IntersectResult::Interior);
EXPECT_TRUE(AZ::ShapeIntersection::Classify(frustum, s1) == AZ::IntersectResult::Overlaps);
EXPECT_TRUE(AZ::ShapeIntersection::Classify(frustum, s2) == AZ::IntersectResult::Exterior);
}
AZ::Vector3 axisX = AZ::Vector3::CreateAxisX();
AZ::Vector3 axisY = AZ::Vector3::CreateAxisY();
AZ::Vector3 axisZ = AZ::Vector3::CreateAxisZ();
{
AZ::Obb obb = AZ::Obb::CreateFromPositionRotationAndHalfLengths(
AZ::Vector3(0.0f, -3.9f, 0.0f), AZ::Quaternion::CreateIdentity(), AZ::Vector3::CreateOne());
AZ::Obb obb1 = AZ::Obb::CreateFromPositionRotationAndHalfLengths(
AZ::Vector3(0.0f, -5.1f, 0.0f), AZ::Quaternion::CreateIdentity(), AZ::Vector3::CreateOne());
AZ::Obb obb2 = AZ::Obb::CreateFromPositionRotationAndHalfLengths(
AZ::Vector3(0.0f, -6.1f, 0.0f), AZ::Quaternion::CreateIdentity(), AZ::Vector3::CreateOne());
EXPECT_TRUE(AZ::ShapeIntersection::Classify(near_value, obb) == AZ::IntersectResult::Interior);
EXPECT_TRUE(AZ::ShapeIntersection::Classify(near_value, obb1) == AZ::IntersectResult::Overlaps);
EXPECT_TRUE(AZ::ShapeIntersection::Classify(near_value, obb2) == AZ::IntersectResult::Exterior);
}
//Test a bunch of different rotations with the OBBs
AZ::Vector3 rotationAxis = AZ::Vector3(1.0f, 1.0f, 1.0f);
rotationAxis.Normalize();
float rotation = 0.0f;
for(int i = 0; i < 50; ++i, rotation += (2.0f*3.1415/50.0f))
{
AZ::Quaternion rot = AZ::Quaternion::CreateFromAxisAngle(rotationAxis, rotation);
AZ::Obb obb = AZ::Obb::CreateFromPositionRotationAndHalfLengths(
AZ::Vector3(0.7f, 0.7f, 0.7f), rot, AZ::Vector3::CreateOne());
AZ::Obb obb1 = AZ::Obb::CreateFromPositionRotationAndHalfLengths(
AZ::Vector3(6.8f, 6.8f, 6.8f), rot, AZ::Vector3::CreateOne());
EXPECT_TRUE(AZ::ShapeIntersection::Overlaps(frustum, obb));
EXPECT_FALSE(AZ::ShapeIntersection::Overlaps(frustum, obb1));
}
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,66 @@
/*
* 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/Sphere.h>
#include <AzCore/UnitTest/TestTypes.h>
namespace UnitTest
{
TEST(MATH_Sphere, TestCreateUnitSphere)
{
AZ::Sphere unitSphere = AZ::Sphere::CreateUnitSphere();
EXPECT_TRUE(unitSphere.GetCenter() == AZ::Vector3::CreateZero());
EXPECT_TRUE(unitSphere.GetRadius() == 1.f);
}
TEST(MATH_Sphere, TestCreateFromAabb)
{
AZ::Aabb testBox = AZ::Aabb::CreateFromMinMax(AZ::Vector3(-1.0f), AZ::Vector3(1.0f));
AZ::Sphere testSphere = AZ::Sphere::CreateFromAabb(testBox);
EXPECT_TRUE(testSphere.GetCenter().IsClose(AZ::Vector3::CreateZero()));
EXPECT_NEAR(testSphere.GetRadius(), 1.f, 0.0001f);
}
TEST(MATH_Sphere, TestConstructFromVec3AndRadius)
{
AZ::Sphere sphere1(AZ::Vector3(10.f, 10.f, 10.f), 15.f);
EXPECT_TRUE(sphere1.GetCenter() == AZ::Vector3(10.f, 10.f, 10.f));
EXPECT_TRUE(sphere1.GetRadius() == 15.f);
}
TEST(MATH_Sphere, TestSet)
{
AZ::Sphere sphere1(AZ::Vector3(10.f, 10.f, 10.f), 15.f);
AZ::Sphere sphere2(AZ::Vector3(12.f, 12.f, 12.f), 13.f);
EXPECT_TRUE(sphere2 != sphere1);
sphere1.Set(sphere2);
EXPECT_TRUE(sphere2 == sphere1);
}
TEST(MATH_Sphere, TestSetCenterAndRadius)
{
AZ::Sphere sphere2(AZ::Vector3(12.f, 12.f, 12.f), 13.f);
AZ::Sphere sphere3(AZ::Vector3(10.f, 10.f, 10.f), 15.f);
sphere3.SetCenter(AZ::Vector3(12.f, 12.f, 12.f));
sphere3.SetRadius(13.f);
EXPECT_TRUE(sphere2 == sphere3);
}
TEST(MATH_Sphere, TestAssignment)
{
AZ::Sphere unitSphere = AZ::Sphere::CreateUnitSphere();
AZ::Sphere sphere2(AZ::Vector3(12.f, 12.f, 12.f), 13.f);
sphere2 = unitSphere;
EXPECT_TRUE(sphere2 == unitSphere);
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,610 @@
/*
* 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.
*
*/
#if defined(HAVE_BENCHMARK)
#include <AzCore/Math/Matrix3x3.h>
#include <AzCore/Math/Transform.h>
#include <AzCore/Math/Vector3.h>
#include <AzCore/Math/Quaternion.h>
#include <AzCore/UnitTest/TestTypes.h>
#include <random>
#include <benchmark/benchmark.h>
namespace Benchmark
{
class BM_MathTransform
: public benchmark::Fixture
{
public:
void SetUp([[maybe_unused]] const ::benchmark::State& state) override
{
m_testDataArray.resize(1000);
const unsigned int seed = 1;
std::mt19937_64 rng(seed);
std::uniform_real_distribution<float> distFloat;
std::uniform_int_distribution<AZ::u32> distInt(0, std::numeric_limits<AZ::u32>::max());
std::generate(m_testDataArray.begin(), m_testDataArray.end(), [&distFloat, &distInt, &rng]()
{
TestData testData;
const AZ::Quaternion q1 = AZ::Quaternion(distFloat(rng), distFloat(rng), distFloat(rng), distFloat(rng)).GetNormalized();
testData.t1 = AZ::Transform::CreateFromQuaternionAndTranslation(q1, AZ::Vector3(distFloat(rng), distFloat(rng), distFloat(rng)));
const AZ::Quaternion q2 = AZ::Quaternion(distFloat(rng), distFloat(rng), distFloat(rng), distFloat(rng)).GetNormalized();
testData.t2 = AZ::Transform::CreateFromQuaternionAndTranslation(q2, AZ::Vector3(distFloat(rng), distFloat(rng), distFloat(rng)));
testData.m3x3 = AZ::Matrix3x3::CreateFromQuaternion(q1);
testData.m3x4 = AZ::Matrix3x4::CreateFromQuaternionAndTranslation(q1, AZ::Vector3(distFloat(rng), distFloat(rng), distFloat(rng)));
testData.q = q1;
testData.v3 = AZ::Vector3(distFloat(rng), distFloat(rng), distFloat(rng));
testData.v4 = AZ::Vector4(distFloat(rng), distFloat(rng), distFloat(rng), distFloat(rng));
testData.value[0] = distFloat(rng);
testData.value[1] = distFloat(rng);
testData.value[2] = distFloat(rng);
testData.index = distInt(rng) % 3;
return testData;
});
}
struct TestData
{
AZ::Transform t1;
AZ::Transform t2;
AZ::Matrix3x3 m3x3;
AZ::Matrix3x4 m3x4;
AZ::Quaternion q;
AZ::Vector3 v3;
AZ::Vector4 v4;
float value[3];
AZ::u32 index;
};
std::vector<TestData> m_testDataArray;
};
BENCHMARK_F(BM_MathTransform, CreateIdentity)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
AZ::Transform result = AZ::Transform::CreateIdentity();
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathTransform, CreateRotationX)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
AZ::Transform result = AZ::Transform::CreateRotationX(testData.value[0]);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathTransform, CreateRotationY)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
AZ::Transform result = AZ::Transform::CreateRotationY(testData.value[0]);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathTransform, CreateRotationZ)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
AZ::Transform result = AZ::Transform::CreateRotationZ(testData.value[0]);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathTransform, CreateFromQuaternion)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
AZ::Transform result = AZ::Transform::CreateFromQuaternion(testData.q);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathTransform, CreateFromQuaternionAndTranslation)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
AZ::Transform result = AZ::Transform::CreateFromQuaternionAndTranslation(testData.q, testData.v3);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathTransform, CreateFromMatrix3x3)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
AZ::Transform result = AZ::Transform::CreateFromMatrix3x3(testData.m3x3);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathTransform, CreateFromMatrix3x3AndTranslation)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
AZ::Transform result = AZ::Transform::CreateFromMatrix3x3AndTranslation(testData.m3x3, testData.v3);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathTransform, CreateFromMatrix3x4)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
AZ::Transform result = AZ::Transform::CreateFromMatrix3x4(testData.m3x4);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathTransform, CreateScale)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
AZ::Transform result = AZ::Transform::CreateScale(testData.v3);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathTransform, CreateFromTranslation)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
AZ::Transform result = AZ::Transform::CreateTranslation(testData.v3);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathTransform, CreateLookAt)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
AZ::Vector3 vector = AZ::Vector3(testData.value[0], testData.value[1], testData.value[2]);
AZ::Transform result = AZ::Transform::CreateLookAt(vector, -vector);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathTransform, GetBasis)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
AZ::Vector3 result = testData.t1.GetBasis(testData.index);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathTransform, GetBasisX)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
AZ::Vector3 result = testData.t1.GetBasisX();
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathTransform, GetBasisY)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
AZ::Vector3 result = testData.t1.GetBasisY();
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathTransform, GetBasisZ)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
AZ::Vector3 result = testData.t1.GetBasisZ();
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathTransform, GetBasisAndTranslation)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
AZ::Vector3 basis[4];
testData.t1.GetBasisAndTranslation(&basis[0], &basis[1], &basis[2], &basis[3]);
benchmark::DoNotOptimize(basis[0]);
benchmark::DoNotOptimize(basis[1]);
benchmark::DoNotOptimize(basis[2]);
benchmark::DoNotOptimize(basis[3]);
}
}
}
BENCHMARK_F(BM_MathTransform, GetTranslation)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
AZ::Vector3 result = testData.t1.GetTranslation();
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathTransform, SetTranslationWithFloats)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
AZ::Transform testTransform = testData.t2;
testTransform.SetTranslation(testData.value[0], testData.value[1], testData.value[2]);
benchmark::DoNotOptimize(testTransform);
}
}
}
BENCHMARK_F(BM_MathTransform, SetTranslationWithVector3)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
AZ::Transform testTransform = testData.t2;
testTransform.SetTranslation(testData.v3);
benchmark::DoNotOptimize(testTransform);
}
}
}
BENCHMARK_F(BM_MathTransform, GetRotation)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
const AZ::Quaternion& result = testData.t1.GetRotation();
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathTransform, SetRotation)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
AZ::Transform testTransform = testData.t1;
testTransform.SetRotation(testData.q);
benchmark::DoNotOptimize(testTransform);
}
}
}
BENCHMARK_F(BM_MathTransform, GetScale)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
AZ::Vector3 result = testData.t1.GetScale();
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathTransform, SetScale)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
AZ::Transform testTransform = testData.t2;
testTransform.SetScale(testData.v3);
benchmark::DoNotOptimize(testTransform);
}
}
}
BENCHMARK_F(BM_MathTransform, ExtractScale)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
AZ::Transform testTransform = testData.t2;
AZ::Vector3 result = testTransform.ExtractScale();
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathTransform, OperatorMultiplyTransform)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
AZ::Transform result = testData.t1 * testData.t2;
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathTransform, OperatorMultiplyEqualsTransform)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
AZ::Transform testTransform = testData.t2;
testTransform *= testData.t1;
benchmark::DoNotOptimize(testTransform);
}
}
}
BENCHMARK_F(BM_MathTransform, TransformPointVector3)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
AZ::Vector3 result = testData.t1.TransformPoint(testData.v3);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathTransform, TransformPointVector4)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
AZ::Vector4 result = testData.t1.TransformPoint(testData.v4);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathTransform, TransformVector)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
AZ::Vector3 result = testData.t1.TransformVector(testData.v3);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathTransform, GetInverse)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
AZ::Transform result = testData.t1.GetInverse();
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathTransform, Invert)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
AZ::Transform testTransform = testData.t2;
testTransform.Invert();
benchmark::DoNotOptimize(testTransform);
}
}
}
BENCHMARK_F(BM_MathTransform, IsOrthogonal)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
bool result = testData.t1.IsOrthogonal();
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathTransform, GetOrthogonalized)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
AZ::Transform result = testData.t1.GetOrthogonalized();
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathTransform, Orthogonalize)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
AZ::Transform testTransform = testData.t1;
testTransform.Orthogonalize();
benchmark::DoNotOptimize(testTransform);
}
}
}
BENCHMARK_F(BM_MathTransform, IsCloseExactAndDifferent)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
bool result = testData.t1.IsClose(testData.t1);
benchmark::DoNotOptimize(result);
result = testData.t2.IsClose(testData.t1);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathTransform, OperatorEqualEqual)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
bool result = (testData.t1 == testData.t2);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathTransform, OperatorNotEqual)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
bool result = (testData.t1 != testData.t2);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathTransform, GetEulerDegrees)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
AZ::Vector3 result = testData.t1.GetEulerDegrees();
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathTransform, GetEulerRadians)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
AZ::Vector3 result = testData.t1.GetEulerRadians();
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathTransform, SetFromEulerDegrees)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
AZ::Transform testTransform = testData.t1;
testTransform.SetFromEulerDegrees(testData.v3);
benchmark::DoNotOptimize(testTransform);
}
}
}
BENCHMARK_F(BM_MathTransform, SetFromEulerRadians)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
AZ::Transform testTransform = testData.t1;
testTransform.SetFromEulerRadians(testData.v3);
benchmark::DoNotOptimize(testTransform);
}
}
}
BENCHMARK_F(BM_MathTransform, IsFinite)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& testData : m_testDataArray)
{
bool result = testData.t1.IsFinite();
benchmark::DoNotOptimize(result);
}
}
}
}
#endif
@@ -0,0 +1,489 @@
/*
* 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/UnitTest/TestTypes.h>
#include <AzCore/Serialization/Utils.h>
#include <AzCore/Component/ComponentApplication.h>
#include <AzCore/Math/Transform.h>
#include <AzCore/Math/Matrix3x3.h>
#include <AzCore/Math/Quaternion.h>
#include <AZTestShared/Math/MathTestHelpers.h>
#include "MathTestData.h"
namespace UnitTest
{
using TransformCreateFixture = ::testing::TestWithParam<AZ::Vector3>;
TEST_P(TransformCreateFixture, CreateIdentity)
{
const AZ::Transform transform = AZ::Transform::CreateIdentity();
const AZ::Vector3 vector = GetParam();
EXPECT_THAT(transform.TransformPoint(vector), IsClose(vector));
}
TEST_P(TransformCreateFixture, Identity)
{
const AZ::Transform transform = AZ::Transform::Identity();
const AZ::Vector3 vector = GetParam();
EXPECT_THAT(transform.TransformPoint(vector), IsClose(vector));
}
TEST_P(TransformCreateFixture, CreateTranslation)
{
const AZ::Vector3 translation(0.8f, 2.3f, -1.9f);
const AZ::Transform transform = AZ::Transform::CreateTranslation(translation);
const AZ::Vector3 vector = GetParam();
EXPECT_THAT(transform.TransformPoint(vector), IsClose(vector + translation));
}
INSTANTIATE_TEST_CASE_P(MATH_Transform, TransformCreateFixture, ::testing::ValuesIn(MathTestData::Vector3s));
using TransformCreateRotationFixture = ::testing::TestWithParam<float>;
TEST_P(TransformCreateRotationFixture, CreateRotationX)
{
const float angle = GetParam();
AZ::Transform transform = AZ::Transform::CreateRotationX(angle);
EXPECT_TRUE(transform.IsOrthogonal());
const AZ::Vector3 vector(1.5f, -0.2f, 2.7f);
const AZ::Vector3 rotatedVector = transform.TransformPoint(vector);
// rotating a vector should not affect its length
EXPECT_TRUE(AZ::IsClose(rotatedVector.GetLengthSq(), vector.GetLengthSq(), AZ::Constants::Tolerance * vector.GetLengthSq()));
// rotating about the X axis should not affect the X component
EXPECT_NEAR(rotatedVector.GetX(), vector.GetX(), AZ::Constants::Tolerance);
// when projected into the Y-Z plane, the angle between the rotated vector and the original vector
// should wrap to the same as the input angle parameter
const float xSquared = vector.GetX() * vector.GetX();
const float projectedDotProduct = rotatedVector.Dot(vector) - xSquared;
const float projectedMagnitudeSq = vector.Dot(vector) - xSquared;
EXPECT_NEAR(projectedDotProduct, projectedMagnitudeSq * cosf(angle), 1e-2f * projectedMagnitudeSq);
}
TEST_P(TransformCreateRotationFixture, CreateRotationY)
{
const float angle = GetParam();
AZ::Transform transform = AZ::Transform::CreateRotationY(angle);
EXPECT_TRUE(transform.IsOrthogonal());
const AZ::Vector3 vector(1.5f, -0.2f, 2.7f);
const AZ::Vector3 rotatedVector = transform.TransformPoint(vector);
// rotating a vector should not affect its length
EXPECT_TRUE(AZ::IsClose(rotatedVector.GetLengthSq(), vector.GetLengthSq(), AZ::Constants::Tolerance * vector.GetLengthSq()));
// rotating about the Y axis should not affect the Y component
EXPECT_NEAR(rotatedVector.GetY(), vector.GetY(), AZ::Constants::Tolerance);
// when projected into the X-Z plane, the angle between the rotated vector and the original vector
// should wrap to the same as the input angle parameter
const float ySquared = vector.GetY() * vector.GetY();
const float projectedDotProduct = rotatedVector.Dot(vector) - ySquared;
const float projectedMagnitudeSq = vector.Dot(vector) - ySquared;
EXPECT_NEAR(projectedDotProduct, projectedMagnitudeSq * cosf(angle), 1e-2f * projectedMagnitudeSq);
}
TEST_P(TransformCreateRotationFixture, CreateRotationZ)
{
const float angle = GetParam();
AZ::Transform transform = AZ::Transform::CreateRotationZ(angle);
EXPECT_TRUE(transform.IsOrthogonal());
const AZ::Vector3 vector(1.5f, -0.2f, 2.7f);
const AZ::Vector3 rotatedVector = transform.TransformPoint(vector);
// rotating a vector should not affect its length
EXPECT_TRUE(AZ::IsClose(rotatedVector.GetLengthSq(), vector.GetLengthSq(), AZ::Constants::Tolerance * vector.GetLengthSq()));
// rotating about the Z axis should not affect the Z component
EXPECT_NEAR(rotatedVector.GetZ(), vector.GetZ(), AZ::Constants::Tolerance);
// when projected into the X-Y plane, the angle between the rotated vector and the original vector
// should wrap to the same as the input angle parameter
const float zSquared = vector.GetZ() * vector.GetZ();
const float projectedDotProduct = rotatedVector.Dot(vector) - zSquared;
const float projectedMagnitudeSq = vector.Dot(vector) - zSquared;
EXPECT_NEAR(projectedDotProduct, projectedMagnitudeSq * cosf(angle), 1e-2f * projectedMagnitudeSq);
}
INSTANTIATE_TEST_CASE_P(MATH_Transform, TransformCreateRotationFixture, ::testing::ValuesIn(MathTestData::Angles));
TEST(MATH_Transform, GetSetTranslation)
{
const AZ::Vector3 inputTranslation(-1.2f, -0.2f, 1.9f);
const float x = -2.7f;
const float y = -0.7f;
const float z = -1.2f;
AZ::Transform transform;
transform.SetTranslation(inputTranslation);
EXPECT_THAT(transform.GetTranslation(), IsClose(inputTranslation));
transform.SetTranslation(x, y, z);
EXPECT_THAT(transform.GetTranslation(), IsClose(AZ::Vector3(x, y, z)));
}
using TransformCreateFromQuaternionFixture = ::testing::TestWithParam<AZ::Quaternion>;
TEST_P(TransformCreateFromQuaternionFixture, CreateFromQuaternion)
{
const AZ::Quaternion quaternion = GetParam();
const AZ::Transform transform = AZ::Transform::CreateFromQuaternion(quaternion);
EXPECT_THAT(transform.GetTranslation(), IsClose(AZ::Vector3::CreateZero()));
const AZ::Vector3 vector(2.3f, -0.6, 1.8f);
EXPECT_THAT(transform.TransformPoint(vector), IsClose(quaternion.TransformVector(vector)));
}
TEST_P(TransformCreateFromQuaternionFixture, CreateFromQuaternionAndTranslation)
{
const AZ::Quaternion quaternion = GetParam();
const AZ::Vector3 translation(-2.6f, 1.7f, 0.8f);
const AZ::Transform transform = AZ::Transform::CreateFromQuaternionAndTranslation(quaternion, translation);
EXPECT_THAT(transform.GetTranslation(), IsClose(translation));
const AZ::Vector3 vector(2.3f, -0.6, 1.8f);
EXPECT_THAT(transform.TransformPoint(vector), IsClose(quaternion.TransformVector(vector) + translation));
}
TEST_P(TransformCreateFromQuaternionFixture, SetRotation)
{
const AZ::Quaternion quaternion = GetParam();
AZ::Transform transform = AZ::Transform::CreateIdentity();
transform.SetRotation(quaternion);
const AZ::Vector3 vector(2.3f, -0.6, 1.8f);
EXPECT_THAT(transform.TransformPoint(vector), IsClose(quaternion.TransformVector(vector)));
}
INSTANTIATE_TEST_CASE_P(MATH_Transform, TransformCreateFromQuaternionFixture, ::testing::ValuesIn(MathTestData::UnitQuaternions));
using TransformCreateFromMatrix3x3Fixture = ::testing::TestWithParam<AZ::Matrix3x3>;
TEST_P(TransformCreateFromMatrix3x3Fixture, CreateFromMatrix3x3)
{
const AZ::Matrix3x3 matrix3x3 = GetParam();
const AZ::Transform transform = AZ::Transform::CreateFromMatrix3x3(matrix3x3);
EXPECT_THAT(transform.GetTranslation(), IsClose(AZ::Vector3::CreateZero()));
const AZ::Vector3 vector(2.3f, -0.6, 1.8f);
EXPECT_THAT(transform.TransformPoint(vector), IsClose(matrix3x3 * vector));
}
TEST_P(TransformCreateFromMatrix3x3Fixture, CreateFromMatrix3x3AndTranslation)
{
const AZ::Matrix3x3 matrix3x3 = GetParam();
const AZ::Vector3 translation(-2.6f, 1.7f, 0.8f);
const AZ::Transform transform = AZ::Transform::CreateFromMatrix3x3AndTranslation(matrix3x3, translation);
EXPECT_THAT(transform.GetTranslation(), IsClose(translation));
const AZ::Vector3 vector(2.3f, -0.6, 1.8f);
EXPECT_THAT(transform.TransformPoint(vector), IsClose(matrix3x3 * vector + translation));
}
INSTANTIATE_TEST_CASE_P(MATH_Transform, TransformCreateFromMatrix3x3Fixture, ::testing::ValuesIn(MathTestData::Matrix3x3s));
TEST(MATH_Transform, CreateScale)
{
const AZ::Vector3 scale(1.7f, 0.3f, 2.4f);
const AZ::Transform transform = AZ::Transform::CreateScale(scale);
const AZ::Vector3 vector(0.2f, -1.6f, 0.4f);
EXPECT_THAT(transform.GetTranslation(), IsClose(AZ::Vector3::CreateZero()));
const AZ::Vector3 transformedVector = transform.TransformPoint(vector);
const AZ::Vector3 expected(0.34f, -0.48f, 0.96f);
EXPECT_THAT(transformedVector, IsClose(expected));
}
using TransformCreateLookAtFixture = ::testing::TestWithParam<MathTestData::AxisPair>;
TEST_P(TransformCreateLookAtFixture, CreateLookAt)
{
const AZ::Transform::Axis axis = GetParam().first;
const AZ::Vector3 axisDirection = GetParam().second;
const AZ::Vector3 from(2.5f, 0.2f, 3.6f);
const AZ::Vector3 to(1.3f, 0.5f, 3.2f);
const AZ::Vector3 expectedForward = to - from;
const AZ::Transform transform = AZ::Transform::CreateLookAt(from, to, axis);
EXPECT_TRUE(transform.IsOrthogonal());
EXPECT_THAT(transform.GetBasisX().Cross(transform.GetBasisY()), IsClose(transform.GetBasisZ()));
EXPECT_THAT(transform.GetTranslation(), IsClose(from));
// the column of the transform corresponding to the axis direction should be parallel to the expected forward direction
const AZ::Vector3 forward = transform.TransformVector(axisDirection);
EXPECT_THAT(forward, IsClose(expectedForward.GetNormalized()));
}
INSTANTIATE_TEST_CASE_P(MATH_Transform, TransformCreateLookAtFixture, ::testing::ValuesIn(MathTestData::Axes));
TEST(MATH_Transform, CreateLookAtDegenerateCases)
{
const AZ::Vector3 from(2.5f, 0.2f, 3.6f);
// to and from are the same, should generate an error
AZ_TEST_START_TRACE_SUPPRESSION;
EXPECT_TRUE(AZ::Transform::CreateLookAt(from, from).IsClose(AZ::Transform::Identity()));
AZ_TEST_STOP_TRACE_SUPPRESSION(1);
// to - from is parallel to usual up direction
const AZ::Vector3 to(2.5f, 0.2f, 5.2f);
const AZ::Transform transform = AZ::Transform::CreateLookAt(from, to);
EXPECT_TRUE(transform.IsOrthogonal());
EXPECT_THAT(transform.GetTranslation(), IsClose(from));
// the default is for the Y basis of the look at transform to be the forward direction
const AZ::Vector3 axisDirection = AZ::Vector3::CreateAxisY();
const AZ::Vector3 forwardDirection = (to - from).GetNormalized();
EXPECT_THAT(transform.TransformVector(axisDirection), IsClose(forwardDirection));
}
TEST(MATH_Transform, MultiplyByTransform)
{
const AZ::Transform transform1 = AZ::Transform::CreateRotationY(0.3f);
const AZ::Transform transform2 = AZ::Transform::CreateScale(AZ::Vector3(1.3f, 1.5f, 0.4f));
const AZ::Transform transform3 = AZ::Transform::CreateFromQuaternionAndTranslation(
AZ::Quaternion(0.42f, 0.46f, -0.66f, 0.42f), AZ::Vector3(2.8f, -3.7f, 1.6f));
const AZ::Transform transform4 = AZ::Transform::CreateRotationX(-0.7f) * AZ::Transform::CreateScale(AZ::Vector3(0.6f, 1.3f, 0.7f));
AZ::Transform transform5 = transform1;
transform5 *= transform4;
const AZ::Vector3 vector(1.9f, 2.3f, 0.2f);
EXPECT_TRUE((transform1 * (transform2 * transform3)).IsClose((transform1 * transform2) * transform3));
EXPECT_THAT((transform3 * transform4).TransformPoint(vector), IsClose(transform3.TransformPoint((transform4.TransformPoint(vector)))));
EXPECT_TRUE((transform2 * AZ::Transform::Identity()).IsClose(transform2));
EXPECT_TRUE((transform3 * AZ::Transform::Identity()).IsClose(AZ::Transform::Identity() * transform3));
EXPECT_TRUE(transform5.IsClose(transform1 * transform4));
}
TEST(MATH_Transform, TranslationCorrectInTransformHierarchy)
{
AZ::Transform parent = AZ::Transform::CreateRotationZ(AZ::DegToRad(45.0f));
parent.SetScale(AZ::Vector3(3.0f, 2.0f, 1.0f));
parent.SetTranslation(AZ::Vector3(0.2f, 0.3f, 0.4f));
AZ::Transform child = AZ::Transform::CreateRotationZ(AZ::DegToRad(90.0f));
child.SetTranslation(AZ::Vector3(0.5f, 0.6f, 0.7f));
const AZ::Transform overallTransform = parent * child;
const AZ::Vector3 overallTranslation = overallTransform.GetTranslation();
const AZ::Vector3 expectedTranslation(0.412132f, 2.20919f, 1.1f);
EXPECT_THAT(overallTranslation, IsClose(AZ::Vector3(0.412132f, 2.20919f, 1.1f)));
}
TEST(MATH_Transform, TransformPointVector3)
{
AZ::Transform transform = AZ::Transform::CreateIdentity();
transform.SetFromEulerDegrees(AZ::Vector3(10.0f, 20.0f, 30.0f));
const AZ::Vector3 translation1 = AZ::Vector3(1.0f, 2.0f, 3.0f);
transform.SetTranslation(translation1);
const AZ::Vector3 vector(0.2f, 0.1f, -0.3f);
const AZ::Vector3 expected1(1.01317f, 2.24004f, 2.71328f);
EXPECT_THAT(transform.TransformPoint(vector), IsClose(expected1));
// MultiplyByVector3 should be affected by the translation part of the Transform
const AZ::Vector3 translation2 = AZ::Vector3(-4.0f, -5.0f, 2.0f);
transform.SetTranslation(translation2);
const AZ::Vector3 expected2 = expected1 + translation2 - translation1;
EXPECT_THAT(transform.TransformPoint(vector), IsClose(expected2));
}
TEST(MATH_Transform, TransformVector)
{
AZ::Transform transform = AZ::Transform::CreateIdentity();
transform.SetFromEulerDegrees(AZ::Vector3(10.0f, 20.0f, 30.0f));
const AZ::Vector3 translation1 = AZ::Vector3(1.0f, 2.0f, 3.0f);
transform.SetTranslation(translation1);
const AZ::Vector3 vector(0.2f, 0.1f, -0.3f);
const AZ::Vector3 expected(0.01317f, 0.24004f, -0.28672f);
EXPECT_THAT(transform.TransformVector(vector), IsClose(expected));
// the result of TransformVector should not be affected by translation
const AZ::Vector3 translation2 = AZ::Vector3(-4.0f, -5.0f, 2.0f);
transform.SetTranslation(translation2);
EXPECT_THAT(transform.TransformVector(vector), IsClose(expected));
}
TEST(MATH_Transform, TransformPointVector4)
{
AZ::Transform transform = AZ::Transform::CreateIdentity();
transform.SetFromEulerDegrees(AZ::Vector3(10.0f, 20.0f, 30.0f));
const AZ::Vector3 translation = AZ::Vector3(1.0f, 2.0f, 3.0f);
transform.SetTranslation(translation);
const AZ::Vector4 vector(0.4f, -1.0f, -0.2f, 0.3f);
const AZ::Vector4 product = transform.TransformPoint(vector);
const AZ::Vector4 expected(1.02696f, 0.02700f, 0.31417f, 0.3f);
EXPECT_THAT(product, IsClose(expected));
}
using TransformInvertFixture = ::testing::TestWithParam<AZ::Transform>;
TEST_P(TransformInvertFixture, GetInverse)
{
const AZ::Transform transform = GetParam();
const AZ::Transform inverse = transform.GetInverse();
const AZ::Vector3 vector(0.9f, 3.2f, -1.4f);
EXPECT_THAT((inverse * transform).TransformPoint(vector), IsClose(vector));
EXPECT_THAT((transform * inverse).TransformPoint(vector), IsClose(vector));
EXPECT_TRUE((inverse * transform).IsClose(AZ::Transform::Identity()));
}
TEST_P(TransformInvertFixture, Invert)
{
const AZ::Transform transform = GetParam();
AZ::Transform inverse = transform;
inverse.Invert();
const AZ::Vector3 vector(2.8f, -1.3f, 2.6f);
EXPECT_THAT((inverse * transform).TransformPoint(vector), IsClose(vector));
EXPECT_THAT((transform * inverse).TransformPoint(vector), IsClose(vector));
EXPECT_TRUE((inverse * transform).IsClose(AZ::Transform::Identity()));
}
INSTANTIATE_TEST_CASE_P(MATH_Transform, TransformInvertFixture, ::testing::ValuesIn(MathTestData::OrthogonalTransforms));
using TransformScaleFixture = ::testing::TestWithParam<AZ::Transform>;
TEST_P(TransformScaleFixture, Scale)
{
const AZ::Transform orthogonalTransform = GetParam();
EXPECT_THAT(orthogonalTransform.GetScale(), IsClose(AZ::Vector3::CreateOne()));
AZ::Transform unscaledTransform = orthogonalTransform;
unscaledTransform.ExtractScale();
EXPECT_THAT(unscaledTransform.GetScale(), IsClose(AZ::Vector3::CreateOne()));
const AZ::Vector3 scale(2.8f, 0.7f, 1.3f);
AZ::Transform scaledTransform = orthogonalTransform;
scaledTransform.MultiplyByScale(scale);
EXPECT_THAT(scaledTransform.GetScale(), IsClose(scale));
}
INSTANTIATE_TEST_CASE_P(MATH_Transform, TransformScaleFixture, ::testing::ValuesIn(MathTestData::OrthogonalTransforms));
TEST(MATH_Transform, IsOrthogonal)
{
EXPECT_TRUE(AZ::Transform::CreateIdentity().IsOrthogonal());
EXPECT_TRUE(AZ::Transform::CreateRotationZ(0.3f).IsOrthogonal());
EXPECT_FALSE(AZ::Transform::CreateScale(AZ::Vector3(0.8f, 0.3f, 1.2f)).IsOrthogonal());
EXPECT_TRUE(AZ::Transform::CreateFromQuaternion(AZ::Quaternion(-0.52f, -0.08f, 0.56f, 0.64f)).IsOrthogonal());
AZ::Transform transform;
transform.SetFromEulerRadians(AZ::Vector3(0.2f, 0.4f, 0.1f));
EXPECT_TRUE(transform.IsOrthogonal());
// want to test each possible way the transform could fail to be orthogonal, which we can do by testing for one
// axis, then using a rotation which cycles the axes
const AZ::Transform axisCycle = AZ::Transform::CreateFromQuaternion(AZ::Quaternion(0.5f, 0.5f, 0.5f, 0.5f));
// a transform which is normalized in 2 axes, but not the third
AZ::Transform nonOrthogonalTransform1 = AZ::Transform::CreateScale(AZ::Vector3(1.0f, 1.0f, 2.0f));
for (int i = 0; i < 3; i++)
{
EXPECT_FALSE(nonOrthogonalTransform1.IsOrthogonal());
nonOrthogonalTransform1 = axisCycle * nonOrthogonalTransform1;
}
}
using TransformSetFromEulerDegreesFixture = ::testing::TestWithParam<AZ::Vector3>;
TEST_P(TransformSetFromEulerDegreesFixture, SetFromEulerDegrees)
{
const AZ::Vector3 eulerDegrees = GetParam();
AZ::Transform transform;
transform.SetFromEulerDegrees(eulerDegrees);
const AZ::Vector3 eulerRadians = AZ::Vector3DegToRad(eulerDegrees);
const AZ::Transform rotX = AZ::Transform::CreateRotationX(eulerRadians.GetX());
const AZ::Transform rotY = AZ::Transform::CreateRotationY(eulerRadians.GetY());
const AZ::Transform rotZ = AZ::Transform::CreateRotationZ(eulerRadians.GetZ());
EXPECT_TRUE(transform.IsClose(rotX * rotY * rotZ));
transform.SetFromEulerDegrees(eulerDegrees);
EXPECT_TRUE(transform.IsClose(rotX * rotY * rotZ));
}
INSTANTIATE_TEST_CASE_P(MATH_Transform, TransformSetFromEulerDegreesFixture, ::testing::ValuesIn(MathTestData::EulerAnglesDegrees));
using TransformSetFromEulerRadiansFixture = ::testing::TestWithParam<AZ::Vector3>;
TEST_P(TransformSetFromEulerRadiansFixture, SetFromEulerRadians)
{
const AZ::Vector3 eulerRadians = GetParam();
AZ::Transform transform;
transform.SetFromEulerRadians(eulerRadians);
const AZ::Transform rotX = AZ::Transform::CreateRotationX(eulerRadians.GetX());
const AZ::Transform rotY = AZ::Transform::CreateRotationY(eulerRadians.GetY());
const AZ::Transform rotZ = AZ::Transform::CreateRotationZ(eulerRadians.GetZ());
EXPECT_TRUE(transform.IsClose(rotX * rotY * rotZ));
}
INSTANTIATE_TEST_CASE_P(MATH_Transform, TransformSetFromEulerRadiansFixture, ::testing::ValuesIn(MathTestData::EulerAnglesRadians));
using TransformGetEulerFixture = ::testing::TestWithParam<AZ::Transform>;
TEST_P(TransformGetEulerFixture, GetEuler)
{
// there isn't a one to one mapping between matrices and Euler angles, so testing for a particular set of Euler
// angles to be returned would be fragile, but getting the Euler angles and creating a new transform from them
// should return the original transform
AZ::Transform transform = GetParam();
transform.SetTranslation(AZ::Vector3::CreateZero());
const AZ::Vector3 eulerDegrees = transform.GetEulerDegrees();
AZ::Transform eulerTransform;
eulerTransform.SetFromEulerDegrees(eulerDegrees);
EXPECT_TRUE(eulerTransform.IsClose(transform));
const AZ::Vector3 eulerRadians = transform.GetEulerRadians();
eulerTransform = AZ::Transform::Identity();
eulerTransform.SetFromEulerRadians(eulerRadians);
EXPECT_TRUE(eulerTransform.IsClose(transform));
}
INSTANTIATE_TEST_CASE_P(MATH_Transform, TransformGetEulerFixture, ::testing::ValuesIn(MathTestData::OrthogonalTransforms));
class MATH_TransformApplicationFixture
: public AllocatorsFixture
{
public:
MATH_TransformApplicationFixture()
: AllocatorsFixture()
{
}
protected:
void SetUp() override
{
AllocatorsFixture::SetUp();
AZ::ComponentApplication::Descriptor desc;
desc.m_useExistingAllocator = true;
desc.m_enableDrilling = false; // we already created a memory driller for the test (AllocatorsFixture)
m_app.Create(desc);
}
void TearDown() override
{
m_app.Destroy();
AllocatorsFixture::TearDown();
}
AZ::ComponentApplication m_app;
};
TEST_F(MATH_TransformApplicationFixture, DeserializingOldFormat)
{
const char* objectStreamBuffer =
R"DELIMITER(<ObjectStream version="3">
<Class name="Transform" field="m_data" value="0.79429845 0.8545947 -0.94273965 -0.05367075 0.3899708 0.30828915 1.0097652 -0.31084164 0.56899188 513.7845459 492.5420837 32.0000000" type="{5D9958E9-9F1E-4985-B532-FFFDE75FEDFD}"/>
</ObjectStream>)DELIMITER";
AZ::Transform* deserializedTransform = AZ::Utils::LoadObjectFromBuffer<AZ::Transform>(objectStreamBuffer, strlen(objectStreamBuffer) + 1);
const AZ::Vector3 expectedTranslation(513.7845459f, 492.5420837f, 32.0000000f);
const AZ::Vector3 expectedScale(1.5f, 0.5f, 1.2f);
const AZ::Quaternion expectedRotation(0.2624075f, 0.4405251f, 0.2029076f, 0.8342113f);
const AZ::Transform expectedTransform =
AZ::Transform::CreateFromQuaternionAndTranslation(expectedRotation, expectedTranslation) * AZ::Transform::CreateScale(expectedScale);
EXPECT_TRUE(deserializedTransform->IsClose(expectedTransform));
azfree(deserializedTransform);
}
TEST(MATH_Transform, IsCloseRespectsTranslationTolerance)
{
AZ::Transform transformA = AZ::Transform::CreateTranslation(AZ::Vector3(0.001f, 0.0f, 0.0f));
AZ::Transform transformB = AZ::Transform::CreateTranslation(AZ::Vector3(0.003f, 0.0f, 0.0f));
// default tolerance fails (using Constants::Tolerance)
EXPECT_FALSE(transformA.IsClose(transformB));
// precise custom tolerance fails
EXPECT_FALSE(transformA.IsClose(transformB, 0.001f));
// relaxed custom tolerance passes
EXPECT_TRUE(transformA.IsClose(transformB, 0.01f));
}
} // namespace UnitTest
@@ -0,0 +1,639 @@
/*
* 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/Vector2.h>
#include <AzCore/UnitTest/TestTypes.h>
#if defined(HAVE_BENCHMARK)
#include <random>
#include <benchmark/benchmark.h>
namespace Benchmark
{
class BM_MathVector2
: public benchmark::Fixture
{
public:
void SetUp([[maybe_unused]] const ::benchmark::State& state) override
{
m_vecDataArray.resize(1000);
const unsigned int seed = 1;
std::mt19937_64 rng(seed);
std::uniform_real_distribution<float> unif;
std::generate(m_vecDataArray.begin(), m_vecDataArray.end(), [&unif, &rng]()
{
VecData vecData;
vecData.v1 = AZ::Vector2(unif(rng), unif(rng));
vecData.v2 = AZ::Vector2(unif(rng), unif(rng));
vecData.v3 = AZ::Vector2(unif(rng), unif(rng));
return vecData;
});
}
struct VecData
{
AZ::Vector2 v1;
AZ::Vector2 v2;
AZ::Vector2 v3;
};
std::vector<VecData> m_vecDataArray;
};
BENCHMARK_F(BM_MathVector2, GetSet)(benchmark::State& state)
{
for (auto _ : state)
{
AZ::Vector2 v1, v2;
float x = 0.0f, y = 0.0f;
for (auto& vecData : m_vecDataArray)
{
x += vecData.v1.GetX();
y += vecData.v2.GetY();
x += vecData.v2.GetX();
y += vecData.v1.GetY();
v1.SetX(x);
v2.SetX(x);
v1.SetY(y);
v2.SetY(y);
}
benchmark::DoNotOptimize(v1);
benchmark::DoNotOptimize(v2);
}
}
BENCHMARK_F(BM_MathVector2, ElementAccess)(benchmark::State& state)
{
for (auto _ : state)
{
AZ::Vector2 v1, v2;
float x = 0.0f, y = 0.0f;
for (auto& vecData : m_vecDataArray)
{
x += vecData.v1.GetElement(0);
y += vecData.v2.GetElement(1);
x += vecData.v2.GetElement(0);
y += vecData.v1.GetElement(1);
v1.SetElement(0, x);
v2.SetElement(0, x);
v1.SetElement(1, y);
v2.SetElement(1, y);
}
benchmark::DoNotOptimize(v1);
benchmark::DoNotOptimize(v2);
}
}
BENCHMARK_F(BM_MathVector2, CreateSelectCmpEqual)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& vecData : m_vecDataArray)
{
AZ::Vector2 result = AZ::Vector2::CreateSelectCmpEqual(vecData.v1, vecData.v2, vecData.v2, vecData.v3);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathVector2, CreateSelectCmpGreaterEqual)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& vecData : m_vecDataArray)
{
AZ::Vector2 result = AZ::Vector2::CreateSelectCmpGreaterEqual(vecData.v1, vecData.v2, vecData.v2, vecData.v3);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathVector2, CreateSelectCmpGreater)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& vecData : m_vecDataArray)
{
AZ::Vector2 result = AZ::Vector2::CreateSelectCmpGreater(vecData.v1, vecData.v2, vecData.v2, vecData.v3);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathVector2, GetNormalized)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& vecData : m_vecDataArray)
{
AZ::Vector2 result = vecData.v1.GetNormalized();
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathVector2, GetNormalizedEstimate)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& vecData : m_vecDataArray)
{
AZ::Vector2 result = vecData.v1.GetNormalizedEstimate();
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathVector2, NormalizeWithLength)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& vecData : m_vecDataArray)
{
float result = vecData.v1.NormalizeWithLength();
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathVector2, NormalizeWithLengthEstimate)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& vecData : m_vecDataArray)
{
float result = vecData.v1.NormalizeWithLengthEstimate();
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathVector2, GetNormalizedSafe)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& vecData : m_vecDataArray)
{
AZ::Vector2 result = vecData.v1.GetNormalizedSafe();
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathVector2, GetNormalizedSafeEstimate)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& vecData : m_vecDataArray)
{
AZ::Vector2 result = vecData.v1.GetNormalizedSafeEstimate();
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathVector2, GetDistance)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& vecData : m_vecDataArray)
{
float result = vecData.v2.GetDistance(vecData.v1);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathVector2, GetDistanceEstimate)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& vecData : m_vecDataArray)
{
float result = vecData.v2.GetDistanceEstimate(vecData.v1);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathVector2, Lerp)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& vecData : m_vecDataArray)
{
AZ::Vector2 result = vecData.v2.Lerp(vecData.v1, 0.0f);
benchmark::DoNotOptimize(result);
result = vecData.v2.Lerp(vecData.v1, 0.25f);
benchmark::DoNotOptimize(result);
result = vecData.v2.Lerp(vecData.v1, 0.5f);
benchmark::DoNotOptimize(result);
result = vecData.v2.Lerp(vecData.v1, 0.75f);
benchmark::DoNotOptimize(result);
result = vecData.v2.Lerp(vecData.v1, 1.0f);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathVector2, Slerp)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& vecData : m_vecDataArray)
{
AZ::Vector2 result = vecData.v2.Slerp(vecData.v1, 0.0f);
benchmark::DoNotOptimize(result);
result = vecData.v2.Slerp(vecData.v1, 0.25f);
benchmark::DoNotOptimize(result);
result = vecData.v2.Slerp(vecData.v1, 0.5f);
benchmark::DoNotOptimize(result);
result = vecData.v2.Slerp(vecData.v1, 0.75f);
benchmark::DoNotOptimize(result);
result = vecData.v2.Slerp(vecData.v1, 1.0f);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathVector2, Nlerp)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& vecData : m_vecDataArray)
{
AZ::Vector2 result = vecData.v2.Nlerp(vecData.v1, 0.0f);
benchmark::DoNotOptimize(result);
result = vecData.v2.Nlerp(vecData.v1, 0.25f);
benchmark::DoNotOptimize(result);
result = vecData.v2.Nlerp(vecData.v1, 0.5f);
benchmark::DoNotOptimize(result);
result = vecData.v2.Nlerp(vecData.v1, 0.75f);
benchmark::DoNotOptimize(result);
result = vecData.v2.Nlerp(vecData.v1, 1.0f);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathVector2, Dot)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& vecData : m_vecDataArray)
{
float result = vecData.v1.Dot(vecData.v2);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathVector2, Equality)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& vecData : m_vecDataArray)
{
bool result = vecData.v1 == vecData.v2;
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathVector2, Inequality)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& vecData : m_vecDataArray)
{
bool result = vecData.v1 != vecData.v2;
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathVector2, IsLessThan)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& vecData : m_vecDataArray)
{
bool result = vecData.v1.IsLessThan(vecData.v2);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathVector2, IsLessEqualThan)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& vecData : m_vecDataArray)
{
bool result = vecData.v1.IsLessEqualThan(vecData.v2);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathVector2, IsGreaterThan)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& vecData : m_vecDataArray)
{
bool result = vecData.v1.IsGreaterThan(vecData.v2);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathVector2, IsGreaterEqualThan)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& vecData : m_vecDataArray)
{
bool result = vecData.v1.IsGreaterEqualThan(vecData.v2);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathVector2, GetMin)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& vecData : m_vecDataArray)
{
AZ::Vector2 result = vecData.v1.GetMin(vecData.v2);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathVector2, GetMax)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& vecData : m_vecDataArray)
{
AZ::Vector2 result = vecData.v1.GetMax(vecData.v2);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathVector2, GetClamp)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& vecData : m_vecDataArray)
{
AZ::Vector2 result = vecData.v1.GetClamp(vecData.v2, vecData.v3);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathVector2, Sub)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& vecData : m_vecDataArray)
{
AZ::Vector2 result = vecData.v1 - vecData.v2;
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathVector2, Sum)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& vecData : m_vecDataArray)
{
AZ::Vector2 result = vecData.v1 + vecData.v2;
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathVector2, Mul)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& vecData : m_vecDataArray)
{
AZ::Vector2 result = vecData.v1 * vecData.v2;
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathVector2, Div)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& vecData : m_vecDataArray)
{
AZ::Vector2 result = vecData.v1 / vecData.v2;
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathVector2, GetSin)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& vecData : m_vecDataArray)
{
AZ::Vector2 result = vecData.v1.GetSin();
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathVector2, GetCos)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& vecData : m_vecDataArray)
{
AZ::Vector2 result = vecData.v1.GetCos();
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathVector2, GetSinCos)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& vecData : m_vecDataArray)
{
AZ::Vector2 sin, cos;
vecData.v1.GetSinCos(sin, cos);
benchmark::DoNotOptimize(sin);
benchmark::DoNotOptimize(cos);
}
}
}
BENCHMARK_F(BM_MathVector2, GetAcos)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& vecData : m_vecDataArray)
{
AZ::Vector2 result = vecData.v1.GetAcos();
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathVector2, GetAtan)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& vecData : m_vecDataArray)
{
AZ::Vector2 result = vecData.v1.GetAtan();
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathVector2, GetAtan2)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& vecData : m_vecDataArray)
{
float result = vecData.v1.GetAtan2();
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathVector2, GetAngleMod)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& vecData : m_vecDataArray)
{
AZ::Vector2 result = vecData.v1.GetAngleMod();
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathVector2, Angle)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& vecData : m_vecDataArray)
{
float result = vecData.v1.Angle(vecData.v2);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathVector2, AngleDeg)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& vecData : m_vecDataArray)
{
float result = vecData.v1.AngleDeg(vecData.v2);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathVector2, GetAbs)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& vecData : m_vecDataArray)
{
AZ::Vector2 result = vecData.v1.GetAbs();
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathVector2, GetReciprocal)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& vecData : m_vecDataArray)
{
AZ::Vector2 result = vecData.v1.GetReciprocal();
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathVector2, GetReciprocalEstimate)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& vecData : m_vecDataArray)
{
AZ::Vector2 result = vecData.v1.GetReciprocalEstimate();
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathVector2, GetProjected)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& vecData : m_vecDataArray)
{
AZ::Vector2 result = vecData.v1.GetProjected(vecData.v2);
benchmark::DoNotOptimize(result);
}
}
}
}
#endif
@@ -0,0 +1,467 @@
/*
* 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/Vector2.h>
#include <AzCore/UnitTest/TestTypes.h>
using namespace AZ;
namespace UnitTest
{
TEST(MATH_Vector2, TestConstructors)
{
Vector2 vA(0.0f);
AZ_TEST_ASSERT((vA.GetX() == 0.0f) && (vA.GetY() == 0.0f));
Vector2 vB(5.0f);
AZ_TEST_ASSERT((vB.GetX() == 5.0f) && (vB.GetY() == 5.0f));
Vector2 vC(1.0f, 2.0f);
AZ_TEST_ASSERT((vC.GetX() == 1.0f) && (vC.GetY() == 2.0f));
}
TEST(MATH_Vector2, TestCreate)
{
float values[2] = { 10.0f, 20.0f };
AZ_TEST_ASSERT(Vector2::CreateOne() == Vector2(1.0f, 1.0f));
AZ_TEST_ASSERT(Vector2::CreateZero() == Vector2(0.0f, 0.0f));
AZ_TEST_ASSERT(Vector2::CreateFromFloat2(values) == Vector2(10.0f, 20.0f));
AZ_TEST_ASSERT(Vector2::CreateAxisX() == Vector2(1.0f, 0.0f));
AZ_TEST_ASSERT(Vector2::CreateAxisY() == Vector2(0.0f, 1.0f));
}
TEST(MATH_Vector2, TestCreateFromAngle)
{
AZ_TEST_ASSERT(Vector2::CreateFromAngle() == Vector2(0.0f, 1.0f));
AZ_TEST_ASSERT(Vector2::CreateFromAngle(0.78539816339f).IsClose(Vector2(0.7071067811f, 0.7071067811f)));
AZ_TEST_ASSERT(Vector2::CreateFromAngle(4.0f).IsClose(Vector2(-0.7568024953f, -0.6536436208f)));
AZ_TEST_ASSERT(Vector2::CreateFromAngle(-1.0f).IsClose(Vector2(-0.8414709848f, 0.5403023058f)));
}
TEST(MATH_Vector2, TestCompareEqual)
{
Vector2 vA(-100.0f, 10.0f);
Vector2 vB(35.0f, 10.0f);
Vector2 vC(35.0f, 20.0f);
// operation r.x = (cmp1.x == cmp2.x) ? vA.x : vB.x per component
Vector2 compareEqualAB = Vector2::CreateSelectCmpEqual(vA, vB, Vector2(1.0f), Vector2(0.0f));
AZ_TEST_ASSERT(compareEqualAB.IsClose(Vector2(0.0f, 1.0f)));
Vector2 compareEqualBC = Vector2::CreateSelectCmpEqual(vB, vC, Vector2(1.0f), Vector2(0.0f));
AZ_TEST_ASSERT(compareEqualBC.IsClose(Vector2(1.0f, 0.0f)));
}
TEST(MATH_Vector2, TestCompareGreaterEqual)
{
Vector2 vA(-100.0f, 10.0f);
Vector2 vB(35.0f, 10.0f);
Vector2 vD(15.0f, 30.0f);
// operation ( r.x = (cmp1.x >= cmp2.x) ? vA.x : vB.x ) per component
Vector2 compareGreaterEqualAB = Vector2::CreateSelectCmpGreaterEqual(vA, vB, Vector2(1.0f), Vector2(0.0f));
AZ_TEST_ASSERT(compareGreaterEqualAB.IsClose(Vector2(0.0f, 1.0f)));
Vector2 compareGreaterEqualBD = Vector2::CreateSelectCmpGreaterEqual(vB, vD, Vector2(1.0f), Vector2(0.0f));
AZ_TEST_ASSERT(compareGreaterEqualBD.IsClose(Vector2(1.0f, 0.0f)));
}
TEST(MATH_Vector2, TestCompareGreater)
{
Vector2 vA(-100.0f, 10.0f);
Vector2 vB(35.0f, 10.0f);
Vector2 vC(35.0f, 20.0f);
// operation ( r.x = (cmp1.x > cmp2.x) ? vA.x : vB.x ) per component
Vector2 compareGreaterAB = Vector2::CreateSelectCmpGreater(vA, vB, Vector2(1.0f), Vector2(0.0f));
AZ_TEST_ASSERT(compareGreaterAB.IsClose(Vector2(0.0f, 0.0f)));
Vector2 compareGreaterCA = Vector2::CreateSelectCmpGreater(vC, vA, Vector2(1.0f), Vector2(0.0f));
AZ_TEST_ASSERT(compareGreaterCA.IsClose(Vector2(1.0f, 1.0f)));
}
TEST(MATH_Vector2, TestStoreFloat)
{
float values[2] = { 0.0f, 0.0f };
Vector2 vA(1.0f, 2.0f);
vA.StoreToFloat2(values);
AZ_TEST_ASSERT(values[0] == 1.0f && values[1] == 2.0f);
}
TEST(MATH_Vector2, TestGetSet)
{
Vector2 vA(2.0f, 3.0f);
AZ_TEST_ASSERT(vA == Vector2(2.0f, 3.0f));
AZ_TEST_ASSERT(vA.GetX() == 2.0f && vA.GetY() == 3.0f);
vA.SetX(10.0f);
AZ_TEST_ASSERT(vA == Vector2(10.0f, 3.0f));
vA.SetY(11.0f);
AZ_TEST_ASSERT(vA == Vector2(10.0f, 11.0f));
vA.Set(15.0f);
AZ_TEST_ASSERT(vA == Vector2(15.0f));
vA.SetElement(0, 20.0f);
AZ_TEST_ASSERT(vA.GetX() == 20.0f && vA.GetY() == 15.0f);
AZ_TEST_ASSERT(vA.GetElement(0) == 20.0f && vA.GetElement(1) == 15.0f);
AZ_TEST_ASSERT(vA(0) == 20.0f && vA(1) == 15.0f);
vA.SetElement(1, 21.0f);
AZ_TEST_ASSERT(vA.GetX() == 20.0f && vA.GetY() == 21.0f);
AZ_TEST_ASSERT(vA.GetElement(0) == 20.0f && vA.GetElement(1) == 21.0f);
AZ_TEST_ASSERT(vA(0) == 20.0f && vA(1) == 21.0f);
}
TEST(MATH_Vector2, TestGetLength)
{
AZ_TEST_ASSERT_FLOAT_CLOSE(Vector2(3.0f, 4.0f).GetLengthSq(), 25.0f);
AZ_TEST_ASSERT_FLOAT_CLOSE(Vector2(4.0f, -3.0f).GetLength(), 5.0f);
AZ_TEST_ASSERT_FLOAT_CLOSE(Vector2(4.0f, -3.0f).GetLengthEstimate(), 5.0f);
}
TEST(MATH_Vector2, TestGetLengthReciprocal)
{
AZ_TEST_ASSERT_FLOAT_CLOSE(Vector2(4.0f, -3.0f).GetLengthReciprocal(), 0.2f);
AZ_TEST_ASSERT_FLOAT_CLOSE(Vector2(4.0f, -3.0f).GetLengthReciprocalEstimate(), 0.2f);
}
TEST(MATH_Vector2, TestGetNormalized)
{
AZ_TEST_ASSERT(Vector2(3.0f, 4.0f).GetNormalized().IsClose(Vector2(3.0f / 5.0f, 4.0f / 5.0f)));
AZ_TEST_ASSERT(Vector2(3.0f, 4.0f).GetNormalizedEstimate().IsClose(Vector2(3.0f / 5.0f, 4.0f / 5.0f)));
}
TEST(MATH_Vector2, TestGetNormalizedSafe)
{
AZ_TEST_ASSERT(Vector2(3.0f, 4.0f).GetNormalizedSafe().IsClose(Vector2(3.0f / 5.0f, 4.0f / 5.0f)));
AZ_TEST_ASSERT(Vector2(3.0f, 4.0f).GetNormalizedSafeEstimate().IsClose(Vector2(3.0f / 5.0f, 4.0f / 5.0f)));
AZ_TEST_ASSERT(Vector2(0.0f).GetNormalizedSafe() == Vector2(0.0f, 0.0f));
AZ_TEST_ASSERT(Vector2(0.0f).GetNormalizedSafeEstimate() == Vector2(0.0f, 0.0f));
}
TEST(MATH_Vector2, TestNormalize)
{
Vector2 v1(4.0f, 3.0f);
v1.Normalize();
AZ_TEST_ASSERT(v1.IsClose(Vector2(4.0f / 5.0f, 3.0f / 5.0f)));
v1.Set(4.0f, 3.0f);
v1.NormalizeEstimate();
AZ_TEST_ASSERT(v1.IsClose(Vector2(4.0f / 5.0f, 3.0f / 5.0f)));
}
TEST(MATH_Vector2, TestNormalizeWithLength)
{
Vector2 v1(4.0f, 3.0f);
float length = v1.NormalizeWithLength();
AZ_TEST_ASSERT_FLOAT_CLOSE(length, 5.0f);
AZ_TEST_ASSERT(v1.IsClose(Vector2(4.0f / 5.0f, 3.0f / 5.0f)));
v1.Set(4.0f, 3.0f);
length = v1.NormalizeWithLengthEstimate();
AZ_TEST_ASSERT_FLOAT_CLOSE(length, 5.0f);
AZ_TEST_ASSERT(v1.IsClose(Vector2(4.0f / 5.0f, 3.0f / 5.0f)));
}
TEST(MATH_Vector2, TestNormalizeSafe)
{
Vector2 v1(3.0f, 4.0f);
v1.NormalizeSafe();
AZ_TEST_ASSERT(v1.IsClose(Vector2(3.0f / 5.0f, 4.0f / 5.0f)));
v1.Set(0.0f);
v1.NormalizeSafe();
AZ_TEST_ASSERT(v1 == Vector2(0.0f, 0.0f));
v1.Set(3.0f, 4.0f);
v1.NormalizeSafeEstimate();
AZ_TEST_ASSERT(v1.IsClose(Vector2(3.0f / 5.0f, 4.0f / 5.0f)));
v1.Set(0.0f);
v1.NormalizeSafeEstimate();
AZ_TEST_ASSERT(v1 == Vector2(0.0f, 0.0f));
}
TEST(MATH_Vector2, TestNormalizeSafeWithLength)
{
Vector2 v1(3.0f, 4.0f);
float length = v1.NormalizeSafeWithLength();
AZ_TEST_ASSERT_FLOAT_CLOSE(length, 5.0f);
AZ_TEST_ASSERT(v1.IsClose(Vector2(3.0f / 5.0f, 4.0f / 5.0f)));
v1.Set(0.0f);
length = v1.NormalizeSafeWithLength();
AZ_TEST_ASSERT(length == 0.0f);
AZ_TEST_ASSERT(v1 == Vector2(0.0f, 0.0f));
v1.Set(3.0f, 4.0f);
length = v1.NormalizeSafeWithLengthEstimate();
AZ_TEST_ASSERT_FLOAT_CLOSE(length, 5.0f);
AZ_TEST_ASSERT(v1.IsClose(Vector2(3.0f / 5.0f, 4.0f / 5.0f)));
v1.Set(0.0f);
length = v1.NormalizeSafeWithLengthEstimate();
AZ_TEST_ASSERT(length == 0.0f);
AZ_TEST_ASSERT(v1 == Vector2(0.0f, 0.0f));
}
TEST(MATH_Vector2, TestIsNormalized)
{
AZ_TEST_ASSERT(Vector2(1.0f, 0.0f).IsNormalized());
AZ_TEST_ASSERT(Vector2(0.7071f, 0.7071f).IsNormalized());
AZ_TEST_ASSERT(!Vector2(1.0f, 1.0f).IsNormalized());
}
TEST(MATH_Vector2, TestSetLength)
{
Vector2 v1(3.0f, 4.0f);
v1.SetLength(10.0f);
AZ_TEST_ASSERT(v1.IsClose(Vector2(6.0f, 8.0f)));
v1.Set(3.0f, 4.0f);
v1.SetLengthEstimate(10.0f);
AZ_TEST_ASSERT(v1.IsClose(Vector2(6.0f, 8.0f), 1e-3f));
}
TEST(MATH_Vector2, TestDistance)
{
Vector2 vA(1.0f, 2.0f);
AZ_TEST_ASSERT_FLOAT_CLOSE(vA.GetDistanceSq(Vector2(-2.0f, 6.0f)), 25.0f);
AZ_TEST_ASSERT_FLOAT_CLOSE(vA.GetDistance(Vector2(5.0f, -1.0f)), 5.0f);
AZ_TEST_ASSERT_FLOAT_CLOSE(vA.GetDistanceEstimate(Vector2(5.0f, -1.0f)), 5.0f);
}
TEST(MATH_Vector2, TestLerpSlerpNLerp)
{
AZ_TEST_ASSERT(Vector2(4.0f, 5.0f).Lerp(Vector2(5.0f, 10.0f), 0.5f).IsClose(Vector2(4.5f, 7.5f)));
AZ_TEST_ASSERT(Vector2(1.0f, 0.0f).Slerp(Vector2(0.0f, 1.0f), 0.5f).IsClose(Vector2(0.7071f, 0.7071f)));
AZ_TEST_ASSERT(Vector2(1.0f, 0.0f).Nlerp(Vector2(0.0f, 1.0f), 0.5f).IsClose(Vector2(0.7071f, 0.7071f)));
}
TEST(MATH_Vector2, TestGetPerpendicular)
{
AZ_TEST_ASSERT(Vector2(1.0f, 2.0f).GetPerpendicular() == Vector2(-2.0f, 1.0f));
}
TEST(MATH_Vector2, TestIsClose)
{
AZ_TEST_ASSERT(Vector2(1.0f, 2.0f).IsClose(Vector2(1.0f, 2.0f)));
AZ_TEST_ASSERT(!Vector2(1.0f, 2.0f).IsClose(Vector2(1.0f, 3.0f)));
// Verify tolerance works
AZ_TEST_ASSERT(Vector2(1.0f, 2.0f).IsClose(Vector2(1.0f, 2.4f), 0.5f));
}
TEST(MATH_Vector2, TestIsZero)
{
AZ_TEST_ASSERT(Vector2(0.0f).IsZero());
AZ_TEST_ASSERT(!Vector2(1.0f).IsZero());
// Verify tolerance works
AZ_TEST_ASSERT(Vector2(0.05f).IsZero(0.1f));
}
TEST(MATH_Vector2, TestEqualityInequality)
{
Vector2 vA(1.0f, 2.0f);
AZ_TEST_ASSERT(vA == Vector2(1.0f, 2.0f));
AZ_TEST_ASSERT(!(vA == Vector2(5.0f, 6.0f)));
AZ_TEST_ASSERT(vA != Vector2(7.0f, 8.0f));
AZ_TEST_ASSERT(!(vA != Vector2(1.0f, 2.0f)));
}
TEST(MATH_Vector2, TestIsLessThan)
{
AZ_TEST_ASSERT(Vector2(1.0f, 2.0f).IsLessThan(Vector2(2.0f, 3.0f)));
AZ_TEST_ASSERT(!Vector2(1.0f, 2.0f).IsLessThan(Vector2(0.0f, 3.0f)));
AZ_TEST_ASSERT(!Vector2(1.0f, 2.0f).IsLessThan(Vector2(2.0f, 2.0f)));
}
TEST(MATH_Vector2, TestIsLessEqualThan)
{
AZ_TEST_ASSERT(Vector2(1.0f, 2.0f).IsLessEqualThan(Vector2(2.0f, 3.0f)));
AZ_TEST_ASSERT(!Vector2(1.0f, 2.0f).IsLessEqualThan(Vector2(0.0f, 3.0f)));
AZ_TEST_ASSERT(Vector2(1.0f, 2.0f).IsLessEqualThan(Vector2(2.0f, 2.0f)));
}
TEST(MATH_Vector2, TestIsGreaterThan)
{
AZ_TEST_ASSERT(Vector2(1.0f, 2.0f).IsGreaterThan(Vector2(0.0f, 1.0f)));
AZ_TEST_ASSERT(!Vector2(1.0f, 2.0f).IsGreaterThan(Vector2(0.0f, 3.0f)));
AZ_TEST_ASSERT(!Vector2(1.0f, 2.0f).IsGreaterThan(Vector2(0.0f, 2.0f)));
}
TEST(MATH_Vector2, TestIsGreaterEqualThan)
{
AZ_TEST_ASSERT(Vector2(1.0f, 2.0f).IsGreaterEqualThan(Vector2(0.0f, 1.0f)));
AZ_TEST_ASSERT(!Vector2(1.0f, 2.0f).IsGreaterEqualThan(Vector2(0.0f, 3.0f)));
AZ_TEST_ASSERT(Vector2(1.0f, 2.0f).IsGreaterEqualThan(Vector2(0.0f, 2.0f)));
}
TEST(MATH_Vector2, TestMinMax)
{
AZ_TEST_ASSERT(Vector2(2.0f, 3.0f).GetMin(Vector2(1.0f, 4.0f)) == Vector2(1.0f, 3.0f));
AZ_TEST_ASSERT(Vector2(2.0f, 3.0f).GetMax(Vector2(1.0f, 4.0f)) == Vector2(2.0f, 4.0f));
}
TEST(MATH_Vector2, TestClamp)
{
AZ_TEST_ASSERT(Vector2(1.0f, 2.0f).GetClamp(Vector2(5.0f, -10.0f), Vector2(10.0f, -5.0f)) == Vector2(5.0f, -5.0f));
AZ_TEST_ASSERT(Vector2(1.0f, 2.0f).GetClamp(Vector2(0.0f, 0.0f), Vector2(10.0f, 10.0f)) == Vector2(1.0f, 2.0f));
}
TEST(MATH_Vector2, TestSelect)
{
Vector2 vA(1.0f);
Vector2 vB(2.0f);
AZ_TEST_ASSERT(vA.GetSelect(Vector2(0.0f, 1.0f), vB) == Vector2(1.0f, 2.0f));
vA.Select(Vector2(1.0f, 0.0f), vB);
AZ_TEST_ASSERT(vA == Vector2(2.0f, 1.0f));
}
TEST(MATH_Vector2, TestAbs)
{
AZ_TEST_ASSERT(Vector2(-1.0f, 2.0f).GetAbs() == Vector2(1.0f, 2.0f));
AZ_TEST_ASSERT(Vector2(3.0f, -4.0f).GetAbs() == Vector2(3.0f, 4.0f));
}
TEST(MATH_Vector2, TestReciprocal)
{
AZ_TEST_ASSERT(Vector2(2.0f, 4.0f).GetReciprocal().IsClose(Vector2(0.5f, 0.25f)));
AZ_TEST_ASSERT(Vector2(2.0f, 4.0f).GetReciprocalEstimate().IsClose(Vector2(0.5f, 0.25f)));
}
TEST(MATH_Vector2, TestAdd)
{
Vector2 vA(1.0f, 2.0f);
vA += Vector2(3.0f, 4.0f);
AZ_TEST_ASSERT(vA == Vector2(4.0f, 6.0f));
AZ_TEST_ASSERT((Vector2(1.0f, 2.0f) + Vector2(2.0f, -1.0f)) == Vector2(3.0f, 1.0f));
}
TEST(MATH_Vector2, TestSub)
{
Vector2 vA(10.0f, 11.0f);
vA -= Vector2(2.0f, 4.0f);
AZ_TEST_ASSERT(vA == Vector2(8.0f, 7.0f));
AZ_TEST_ASSERT((Vector2(1.0f, 2.0f) - Vector2(2.0f, -1.0f)) == Vector2(-1.0f, 3.0f));
}
TEST(MATH_Vector2, TestMul)
{
Vector2 vA(2.0f, 4.0f);
vA *= Vector2(3.0f, 6.0f);
AZ_TEST_ASSERT(vA == Vector2(6.0f, 24.0f));
vA.Set(2.0f, 3.0f);
vA *= 5.0f;
AZ_TEST_ASSERT(vA == Vector2(10.0f, 15.0f));
AZ_TEST_ASSERT((Vector2(3.0f, 2.0f) * Vector2(2.0f, -4.0f)) == Vector2(6.0f, -8.0f));
AZ_TEST_ASSERT((Vector2(3.0f, 2.0f) * 2.0f) == Vector2(6.0f, 4.0f));
}
TEST(MATH_Vector2, TestDiv)
{
Vector2 vA(15.0f, 20.0f);
vA /= Vector2(3.0f, 2.0f);
AZ_TEST_ASSERT(vA == Vector2(5.0f, 10.0f));
vA.Set(20.0f, 30.0f);
vA /= 10.0f;
AZ_TEST_ASSERT(vA == Vector2(2.0f, 3.0f));
AZ_TEST_ASSERT((Vector2(30.0f, 20.0f) / Vector2(10.0f, -4.0f)) == Vector2(3.0f, -5.0f));
AZ_TEST_ASSERT((Vector2(30.0f, 20.0f) / 10.0f) == Vector2(3.0f, 2.0f));
}
TEST(MATH_Vector2, TestNegate)
{
AZ_TEST_ASSERT((-Vector2(1.0f, -2.0f)) == Vector2(-1.0f, 2.0f));
}
TEST(MATH_Vector2, TestDot)
{
AZ_TEST_ASSERT_FLOAT_CLOSE(Vector2(1.0f, 2.0f).Dot(Vector2(-1.0f, 5.0f)), 9.0f);
}
TEST(MATH_Vector2, TestMadd)
{
AZ_TEST_ASSERT(Vector2(1.0f, 2.0f).GetMadd(Vector2(2.0f, 6.0f), Vector2(3.0f, 4.0f)) == Vector2(5.0f, 16.0f));
Vector2 vA(1.0f, 2.0f);
vA.Madd(Vector2(2.0f, 6.0f), Vector2(3.0f, 4.0f));
AZ_TEST_ASSERT(vA == Vector2(5.0f, 16.0f));
}
TEST(MATH_Vector2, TestProject)
{
Vector2 vA(0.5f);
vA.Project(Vector2(0.0f, 2.0f));
AZ_TEST_ASSERT(vA == Vector2(0.0f, 0.5f));
vA.Set(0.5f);
vA.ProjectOnNormal(Vector2(0.0f, 1.0f));
AZ_TEST_ASSERT(vA == Vector2(0.0f, 0.5f));
vA.Set(2.0f, 4.0f);
AZ_TEST_ASSERT(vA.GetProjected(Vector2(1.0f, 1.0f)) == Vector2(3.0f, 3.0f));
AZ_TEST_ASSERT(vA.GetProjectedOnNormal(Vector2(1.0f, 0.0f)) == Vector2(2.0f, 0.0f));
}
TEST(MATH_Vector2, TestTrig)
{
AZ_TEST_ASSERT(Vector2(DegToRad(78.0f), DegToRad(-150.0f)).GetAngleMod().IsClose(Vector2(DegToRad(78.0f), DegToRad(-150.0f))));
AZ_TEST_ASSERT(Vector2(DegToRad(390.0f), DegToRad(-190.0f)).GetAngleMod().IsClose(Vector2(DegToRad(30.0f), DegToRad(170.0f))));
AZ_TEST_ASSERT(Vector2(DegToRad(60.0f), DegToRad(105.0f)).GetSin().IsClose(Vector2(0.866f, 0.966f), 0.005f));
AZ_TEST_ASSERT(Vector2(DegToRad(60.0f), DegToRad(105.0f)).GetCos().IsClose(Vector2(0.5f, -0.259f), 0.005f));
Vector2 sin, cos;
Vector2 v1(DegToRad(60.0f), DegToRad(105.0f));
v1.GetSinCos(sin, cos);
AZ_TEST_ASSERT(sin.IsClose(Vector2(0.866f, 0.966f), 0.005f));
AZ_TEST_ASSERT(cos.IsClose(Vector2(0.5f, -0.259f), 0.005f));
}
TEST(MATH_Vector2, TestIsFinite)
{
AZ_TEST_ASSERT(Vector2(1.0f, 1.0f).IsFinite());
const float infinity = std::numeric_limits<float>::infinity();
AZ_TEST_ASSERT(!Vector2(infinity, infinity).IsFinite());
}
TEST(MATH_Vector2, TestAngles)
{
using Vec2CalcFunc = float(Vector2::*)(const Vector2&) const;
auto angleTest = [](Vec2CalcFunc func, const Vector2& self, const Vector2& other, const float target)
{
const float epsilon = 0.01f;
float value = (self.*func)(other);
AZ_TEST_ASSERT(AZ::IsClose(value, target, epsilon));
};
const Vec2CalcFunc angleFuncs[2] = { &Vector2::Angle, &Vector2::AngleSafe };
for (Vec2CalcFunc angleFunc : angleFuncs)
{
angleTest(angleFunc, Vector2{ 1.0f, 0.0f }, Vector2{ 0.0f, 1.0f }, AZ::Constants::HalfPi);
angleTest(angleFunc, Vector2{ 42.0f, 0.0f }, Vector2{ 0.0f, 23.0f }, AZ::Constants::HalfPi);
angleTest(angleFunc, Vector2{ 1.0f, 0.0f }, Vector2{ -1.0f, 0.0f }, AZ::Constants::Pi);
angleTest(angleFunc, Vector2{ 1.0f, 0.0f }, Vector2{ 1.0f, 1.0f }, AZ::Constants::QuarterPi);
angleTest(angleFunc, Vector2{ 1.0f, 0.0f }, Vector2{ 1.0f, 0.0f }, 0.f);
angleTest(angleFunc, Vector2{ 1.0f, 1.0f }, Vector2{ -1.0f, -1.0f }, AZ::Constants::Pi);
}
const Vec2CalcFunc angleDegFuncs[2] = { &Vector2::AngleDeg, &Vector2::AngleSafeDeg };
for (Vec2CalcFunc angleDegFunc : angleDegFuncs)
{
angleTest(angleDegFunc, Vector2{ 1.0f, 0.0f }, Vector2{ 0.0f, 1.0f }, 90.f);
angleTest(angleDegFunc, Vector2{ 42.0f, 0.0f }, Vector2{ 0.0f, 23.0f }, 90.f);
angleTest(angleDegFunc, Vector2{ 1.0f, 0.0f }, Vector2{ -1.0f, 0.0f }, 180.f);
angleTest(angleDegFunc, Vector2{ 1.0f, 0.0f }, Vector2{ 1.0f, 1.0f }, 45.f);
angleTest(angleDegFunc, Vector2{ 1.0f, 0.0f }, Vector2{ 1.0f, 0.0f }, 0.f);
angleTest(angleDegFunc, Vector2{ 1.0f, 1.0f }, Vector2{ -1.0f, -1.0f }, 180.f);
}
const Vec2CalcFunc angleSafeFuncs[2] = { &Vector2::AngleSafe, &Vector2::AngleSafeDeg };
for (Vec2CalcFunc angleSafeFunc : angleSafeFuncs)
{
angleTest(angleSafeFunc, Vector2{ 0.0f, 0.0f }, Vector2{ 0.0f, 1.0f }, 0.f);
angleTest(angleSafeFunc, Vector2{ 0.0f, 0.0f }, Vector2{ 0.0f, 0.0f }, 0.f);
angleTest(angleSafeFunc, Vector2{ 1.0f, 0.0f }, Vector2{ 0.0f, 0.0f }, 0.f);
angleTest(angleSafeFunc, Vector2{ 0.0f, 0.0f }, Vector2{ 0.0f, 323432.0f }, 0.f);
angleTest(angleSafeFunc, Vector2{ 323432.0f, 0.0f }, Vector2{ 0.0f, 0.0f }, 0.f);
}
}
}
@@ -0,0 +1,690 @@
/*
* 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/Vector3.h>
#include <AzCore/UnitTest/TestTypes.h>
#if defined(HAVE_BENCHMARK)
#include <random>
#include <benchmark/benchmark.h>
namespace Benchmark
{
class BM_MathVector3
: public benchmark::Fixture
{
public:
void SetUp([[maybe_unused]] const ::benchmark::State& state) override
{
m_vecDataArray.resize(1000);
const unsigned int seed = 1;
std::mt19937_64 rng(seed);
std::uniform_real_distribution<float> unif;
std::generate(m_vecDataArray.begin(), m_vecDataArray.end(), [&unif, &rng]()
{
VecData vecData;
vecData.v1 = AZ::Vector3(unif(rng), unif(rng), unif(rng));
vecData.v2 = AZ::Vector3(unif(rng), unif(rng), unif(rng));
vecData.v3 = AZ::Vector3(unif(rng), unif(rng), unif(rng));
return vecData;
});
}
struct VecData
{
AZ::Vector3 v1;
AZ::Vector3 v2;
AZ::Vector3 v3;
};
std::vector<VecData> m_vecDataArray;
};
BENCHMARK_F(BM_MathVector3, GetSet)(benchmark::State& state)
{
for (auto _ : state)
{
AZ::Vector3 v1, v2, v3;
float x = 0.0f, y = 0.0f, z = 0.0f;
for (auto& vecData : m_vecDataArray)
{
x += vecData.v1.GetX();
y += vecData.v2.GetY();
z += vecData.v3.GetZ();
x += vecData.v2.GetX();
y += vecData.v3.GetY();
z += vecData.v1.GetZ();
x += vecData.v3.GetX();
y += vecData.v1.GetY();
z += vecData.v2.GetZ();
v1.SetX(x);
v2.SetX(x);
v3.SetX(x);
v1.SetY(y);
v2.SetY(y);
v3.SetY(y);
v1.SetZ(z);
v2.SetZ(z);
v3.SetZ(z);
}
benchmark::DoNotOptimize(v1);
benchmark::DoNotOptimize(v2);
benchmark::DoNotOptimize(v3);
}
}
BENCHMARK_F(BM_MathVector3, ElementAccess)(benchmark::State& state)
{
for (auto _ : state)
{
AZ::Vector3 v1, v2, v3;
float x = 0.0f, y = 0.0f, z = 0.0f;
for (auto& vecData : m_vecDataArray)
{
x += vecData.v1.GetElement(0);
y += vecData.v2.GetElement(1);
z += vecData.v3.GetElement(2);
x += vecData.v2.GetElement(0);
y += vecData.v3.GetElement(1);
z += vecData.v1.GetElement(2);
x += vecData.v3.GetElement(0);
y += vecData.v1.GetElement(1);
z += vecData.v2.GetElement(2);
v1.SetElement(0, x);
v2.SetElement(0, x);
v3.SetElement(0, x);
v1.SetElement(1, y);
v2.SetElement(1, y);
v3.SetElement(1, y);
v1.SetElement(2, z);
v2.SetElement(2, z);
v3.SetElement(2, z);
}
benchmark::DoNotOptimize(v1);
benchmark::DoNotOptimize(v2);
benchmark::DoNotOptimize(v3);
}
}
BENCHMARK_F(BM_MathVector3, CreateSelectCmpEqual)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& vecData : m_vecDataArray)
{
AZ::Vector3 result = AZ::Vector3::CreateSelectCmpEqual(vecData.v1, vecData.v2, vecData.v2, vecData.v3);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathVector3, CreateSelectCmpGreaterEqual)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& vecData : m_vecDataArray)
{
AZ::Vector3 result = AZ::Vector3::CreateSelectCmpGreaterEqual(vecData.v1, vecData.v2, vecData.v2, vecData.v3);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathVector3, CreateSelectCmpGreater)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& vecData : m_vecDataArray)
{
AZ::Vector3 result = AZ::Vector3::CreateSelectCmpGreater(vecData.v1, vecData.v2, vecData.v2, vecData.v3);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathVector3, GetNormalized)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& vecData : m_vecDataArray)
{
AZ::Vector3 result = vecData.v1.GetNormalized();
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathVector3, GetNormalizedEstimate)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& vecData : m_vecDataArray)
{
AZ::Vector3 result = vecData.v1.GetNormalizedEstimate();
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathVector3, NormalizeWithLength)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& vecData : m_vecDataArray)
{
float result = vecData.v1.NormalizeWithLength();
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathVector3, NormalizeWithLengthEstimate)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& vecData : m_vecDataArray)
{
float result = vecData.v1.NormalizeWithLengthEstimate();
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathVector3, GetNormalizedSafe)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& vecData : m_vecDataArray)
{
AZ::Vector3 result = vecData.v1.GetNormalizedSafe();
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathVector3, GetNormalizedSafeEstimate)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& vecData : m_vecDataArray)
{
AZ::Vector3 result = vecData.v1.GetNormalizedSafeEstimate();
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathVector3, GetDistance)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& vecData : m_vecDataArray)
{
float result = vecData.v2.GetDistance(vecData.v1);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathVector3, GetDistanceEstimate)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& vecData : m_vecDataArray)
{
float result = vecData.v2.GetDistanceEstimate(vecData.v1);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathVector3, Lerp)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& vecData : m_vecDataArray)
{
AZ::Vector3 result = vecData.v2.Lerp(vecData.v1, 0.0f);
benchmark::DoNotOptimize(result);
result = vecData.v2.Lerp(vecData.v1, 0.25f);
benchmark::DoNotOptimize(result);
result = vecData.v2.Lerp(vecData.v1, 0.5f);
benchmark::DoNotOptimize(result);
result = vecData.v2.Lerp(vecData.v1, 0.75f);
benchmark::DoNotOptimize(result);
result = vecData.v2.Lerp(vecData.v1, 1.0f);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathVector3, Slerp)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& vecData : m_vecDataArray)
{
AZ::Vector3 result = vecData.v2.Slerp(vecData.v1, 0.0f);
benchmark::DoNotOptimize(result);
result = vecData.v2.Slerp(vecData.v1, 0.25f);
benchmark::DoNotOptimize(result);
result = vecData.v2.Slerp(vecData.v1, 0.5f);
benchmark::DoNotOptimize(result);
result = vecData.v2.Slerp(vecData.v1, 0.75f);
benchmark::DoNotOptimize(result);
result = vecData.v2.Slerp(vecData.v1, 1.0f);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathVector3, Nlerp)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& vecData : m_vecDataArray)
{
AZ::Vector3 result = vecData.v2.Nlerp(vecData.v1, 0.0f);
benchmark::DoNotOptimize(result);
result = vecData.v2.Nlerp(vecData.v1, 0.25f);
benchmark::DoNotOptimize(result);
result = vecData.v2.Nlerp(vecData.v1, 0.5f);
benchmark::DoNotOptimize(result);
result = vecData.v2.Nlerp(vecData.v1, 0.75f);
benchmark::DoNotOptimize(result);
result = vecData.v2.Nlerp(vecData.v1, 1.0f);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathVector3, Dot)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& vecData : m_vecDataArray)
{
float result = vecData.v1.Dot(vecData.v2);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathVector3, Cross)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& vecData : m_vecDataArray)
{
AZ::Vector3 result = vecData.v1.Cross(vecData.v2);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathVector3, Equality)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& vecData : m_vecDataArray)
{
bool result = vecData.v1 == vecData.v2;
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathVector3, Inequality)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& vecData : m_vecDataArray)
{
bool result = vecData.v1 != vecData.v2;
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathVector3, IsLessThan)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& vecData : m_vecDataArray)
{
bool result = vecData.v1.IsLessThan(vecData.v2);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathVector3, IsLessEqualThan)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& vecData : m_vecDataArray)
{
bool result = vecData.v1.IsLessEqualThan(vecData.v2);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathVector3, IsGreaterThan)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& vecData : m_vecDataArray)
{
bool result = vecData.v1.IsGreaterThan(vecData.v2);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathVector3, IsGreaterEqualThan)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& vecData : m_vecDataArray)
{
bool result = vecData.v1.IsGreaterEqualThan(vecData.v2);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathVector3, GetMin)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& vecData : m_vecDataArray)
{
AZ::Vector3 result = vecData.v1.GetMin(vecData.v2);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathVector3, GetMax)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& vecData : m_vecDataArray)
{
AZ::Vector3 result = vecData.v1.GetMax(vecData.v2);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathVector3, GetClamp)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& vecData : m_vecDataArray)
{
AZ::Vector3 result = vecData.v1.GetClamp(vecData.v2, vecData.v3);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathVector3, Sub)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& vecData : m_vecDataArray)
{
AZ::Vector3 result = vecData.v1 - vecData.v2;
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathVector3, Sum)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& vecData : m_vecDataArray)
{
AZ::Vector3 result = vecData.v1 + vecData.v2;
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathVector3, Mul)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& vecData : m_vecDataArray)
{
AZ::Vector3 result = vecData.v1 * vecData.v2;
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathVector3, Div)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& vecData : m_vecDataArray)
{
AZ::Vector3 result = vecData.v1 / vecData.v2;
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathVector3, GetSin)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& vecData : m_vecDataArray)
{
AZ::Vector3 result = vecData.v1.GetSin();
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathVector3, GetCos)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& vecData : m_vecDataArray)
{
AZ::Vector3 result = vecData.v1.GetCos();
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathVector3, GetSinCos)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& vecData : m_vecDataArray)
{
AZ::Vector3 sin, cos;
vecData.v1.GetSinCos(sin, cos);
benchmark::DoNotOptimize(sin);
benchmark::DoNotOptimize(cos);
}
}
}
BENCHMARK_F(BM_MathVector3, GetAcos)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& vecData : m_vecDataArray)
{
AZ::Vector3 result = vecData.v1.GetAcos();
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathVector3, GetAtan)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& vecData : m_vecDataArray)
{
AZ::Vector3 result = vecData.v1.GetAtan();
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathVector3, GetAngleMod)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& vecData : m_vecDataArray)
{
AZ::Vector3 result = vecData.v1.GetAngleMod();
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathVector3, Angle)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& vecData : m_vecDataArray)
{
float result = vecData.v1.Angle(vecData.v2);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathVector3, AngleDeg)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& vecData : m_vecDataArray)
{
float result = vecData.v1.AngleDeg(vecData.v2);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathVector3, GetAbs)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& vecData : m_vecDataArray)
{
AZ::Vector3 result = vecData.v1.GetAbs();
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathVector3, GetReciprocal)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& vecData : m_vecDataArray)
{
AZ::Vector3 result = vecData.v1.GetReciprocal();
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathVector3, GetReciprocalEstimate)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& vecData : m_vecDataArray)
{
AZ::Vector3 result = vecData.v1.GetReciprocalEstimate();
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathVector3, IsPerpendicular)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& vecData : m_vecDataArray)
{
bool result = vecData.v1.IsPerpendicular(vecData.v2, 0.01f);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathVector3, GetOrthogonalVector)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& vecData : m_vecDataArray)
{
AZ::Vector3 result = vecData.v1.GetOrthogonalVector();
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathVector3, GetProjected)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& vecData : m_vecDataArray)
{
AZ::Vector3 result = vecData.v1.GetProjected(vecData.v2);
benchmark::DoNotOptimize(result);
}
}
}
}
#endif
@@ -0,0 +1,504 @@
/*
* 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/Vector3.h>
#include <AzCore/UnitTest/TestTypes.h>
using namespace AZ;
namespace UnitTest
{
TEST(MATH_Vector3, TestConstructors)
{
Vector3 v1(0.0f);
AZ_TEST_ASSERT((v1.GetX() == 0.0f) && (v1.GetY() == 0.0f) && (v1.GetZ() == 0.0f));
Vector3 v2(5.0f);
AZ_TEST_ASSERT((v2.GetX() == 5.0f) && (v2.GetY() == 5.0f) && (v2.GetZ() == 5.0f));
Vector3 v3(1.0f, 2.0f, 3.0f);
AZ_TEST_ASSERT((v3.GetX() == 1.0f) && (v3.GetY() == 2.0f) && (v3.GetZ() == 3.0f));
}
TEST(MATH_Vector3, TestCreateFunctions)
{
AZ_TEST_ASSERT(Vector3::CreateOne() == Vector3(1.0f, 1.0f, 1.0f));
AZ_TEST_ASSERT(Vector3::CreateZero() == Vector3(0.0f));
float values[3] = { 10.0f, 20.0f, 30.0f };
AZ_TEST_ASSERT(Vector3::CreateFromFloat3(values) == Vector3(10.0f, 20.0f, 30.0f));
AZ_TEST_ASSERT(Vector3::CreateAxisX() == Vector3(1.0f, 0.0f, 0.0f));
AZ_TEST_ASSERT(Vector3::CreateAxisY() == Vector3(0.0f, 1.0f, 0.0f));
AZ_TEST_ASSERT(Vector3::CreateAxisZ() == Vector3(0.0f, 0.0f, 1.0f));
}
TEST(MATH_Vector3, TestCompareEqual)
{
Vector3 vA(-100.0f, 10.0f, -1.0f);
Vector3 vB(35.0f, 10.0f, -5.0f);
Vector3 vC(35.0f, 20.0f, -1.0f);
// operation r.x = (cmp1.x == cmp2.x) ? vA.x : vB.x per component
Vector3 compareEqualAB = Vector3::CreateSelectCmpEqual(vA, vB, Vector3(1.0f), Vector3(0.0f));
AZ_TEST_ASSERT(compareEqualAB.IsClose(Vector3(0.0f, 1.0f, 0.0f)));
Vector3 compareEqualBC = Vector3::CreateSelectCmpEqual(vB, vC, Vector3(1.0f), Vector3(0.0f));
AZ_TEST_ASSERT(compareEqualBC.IsClose(Vector3(1.0f, 0.0f, 0.0f)));
}
TEST(MATH_Vector3, TestCompareGreaterEqual)
{
Vector3 vA(-100.0f, 10.0f, -1.0f);
Vector3 vB(35.0f, 10.0f, -5.0f);
Vector3 vD(15.0f, 30.0f, 45.0f);
// operation ( r.x = (cmp1.x >= cmp2.x) ? vA.x : vB.x ) per component
Vector3 compareGreaterEqualAB = Vector3::CreateSelectCmpGreaterEqual(vA, vB, Vector3(1.0f), Vector3(0.0f));
AZ_TEST_ASSERT(compareGreaterEqualAB.IsClose(Vector3(0.0f, 1.0f, 1.0f)));
Vector3 compareGreaterEqualBD = Vector3::CreateSelectCmpGreaterEqual(vB, vD, Vector3(1.0f), Vector3(0.0f));
AZ_TEST_ASSERT(compareGreaterEqualBD.IsClose(Vector3(1.0f, 0.0f, 0.0f)));
}
TEST(MATH_Vector3, TestCompareGreater)
{
Vector3 vA(-100.0f, 10.0f, -1.0f);
Vector3 vB(35.0f, 10.0f, -5.0f);
Vector3 vC(35.0f, 20.0f, -1.0f);
// operation ( r.x = (cmp1.x > cmp2.x) ? vA.x : vB.x ) per component
Vector3 compareGreaterAB = Vector3::CreateSelectCmpGreater(vA, vB, Vector3(1.0f), Vector3(0.0f));
AZ_TEST_ASSERT(compareGreaterAB.IsClose(Vector3(0.0f, 0.0f, 1.0f)));
Vector3 compareGreaterCA = Vector3::CreateSelectCmpGreater(vC, vA, Vector3(1.0f), Vector3(0.0f));
AZ_TEST_ASSERT(compareGreaterCA.IsClose(Vector3(1.0f, 1.0f, 0.0f)));
}
TEST(MATH_Vector3, TestStoreFloat)
{
Vector3 v1(1.0f, 2.0f, 3.0f);
float values[3];
v1.StoreToFloat3(values);
AZ_TEST_ASSERT(values[0] == 1.0f && values[1] == 2.0f && values[2] == 3.0f);
float values4[4];
v1.StoreToFloat4(values4);
AZ_TEST_ASSERT(values4[0] == 1.0f && values4[1] == 2.0f && values4[2] == 3.0f);
}
TEST(MATH_Vector3, TestGetSet)
{
Vector3 v1(2.0f, 3.0f, 4.0f);
float values[3] = { 1.0f, 2.0f, 3.0f };
AZ_TEST_ASSERT(v1 == Vector3(2.0f, 3.0f, 4.0f));
v1.SetX(10.0f);
AZ_TEST_ASSERT(v1 == Vector3(10.0f, 3.0f, 4.0f));
v1.SetY(11.0f);
AZ_TEST_ASSERT(v1 == Vector3(10.0f, 11.0f, 4.0f));
v1.SetZ(12.0f);
AZ_TEST_ASSERT(v1 == Vector3(10.0f, 11.0f, 12.0f));
v1.Set(15.0f);
AZ_TEST_ASSERT(v1 == Vector3(15.0f));
v1.Set(values);
AZ_TEST_ASSERT((v1.GetX() == 1.0f) && (v1.GetY() == 2.0f) && (v1.GetZ() == 3.0f));
}
TEST(MATH_Vector3, TestIndexElement)
{
Vector3 v1(1.0f, 2.0f, 3.0f);
AZ_TEST_ASSERT(v1(0) == 1.0f);
AZ_TEST_ASSERT(v1(1) == 2.0f);
AZ_TEST_ASSERT(v1(2) == 3.0f);
AZ_TEST_ASSERT(v1.GetElement(0) == 1.0f);
AZ_TEST_ASSERT(v1.GetElement(1) == 2.0f);
AZ_TEST_ASSERT(v1.GetElement(2) == 3.0f);
v1.SetElement(0, 5.0f);
v1.SetElement(1, 6.0f);
v1.SetElement(2, 7.0f);
AZ_TEST_ASSERT(v1.GetElement(0) == 5.0f);
AZ_TEST_ASSERT(v1.GetElement(1) == 6.0f);
AZ_TEST_ASSERT(v1.GetElement(2) == 7.0f);
}
TEST(MATH_Vector3, TestGetLength)
{
AZ_TEST_ASSERT_FLOAT_CLOSE(Vector3(3.0f, 4.0f, 0.0f).GetLengthSq(), 25.0f);
AZ_TEST_ASSERT_FLOAT_CLOSE(Vector3(0.0f, 4.0f, -3.0f).GetLength(), 5.0f);
AZ_TEST_ASSERT_FLOAT_CLOSE(Vector3(0.0f, 4.0f, -3.0f).GetLengthEstimate(), 5.0f);
}
TEST(MATH_Vector3, TestGetLengthReciprocal)
{
AZ_TEST_ASSERT_FLOAT_CLOSE(Vector3(0.0f, 4.0f, -3.0f).GetLengthReciprocal(), 0.2f);
AZ_TEST_ASSERT_FLOAT_CLOSE(Vector3(0.0f, 4.0f, -3.0f).GetLengthReciprocalEstimate(), 0.2f);
}
TEST(MATH_Vector3, TestGetNormalized)
{
AZ_TEST_ASSERT(Vector3(3.0f, 0.0f, 4.0f).GetNormalized().IsClose(Vector3(3.0f / 5.0f, 0.0f, 4.0f / 5.0f)));
AZ_TEST_ASSERT(Vector3(3.0f, 0.0f, 4.0f).GetNormalizedEstimate().IsClose(Vector3(3.0f / 5.0f, 0.0f, 4.0f / 5.0f)));
}
TEST(MATH_Vector3, TestGetNormalizedSafe)
{
AZ_TEST_ASSERT(Vector3(3.0f, 0.0f, 4.0f).GetNormalizedSafe().IsClose(Vector3(3.0f / 5.0f, 0.0f, 4.0f / 5.0f)));
AZ_TEST_ASSERT(Vector3(3.0f, 0.0f, 4.0f).GetNormalizedSafeEstimate().IsClose(Vector3(3.0f / 5.0f, 0.0f, 4.0f / 5.0f)));
AZ_TEST_ASSERT(Vector3(0.0f).GetNormalizedSafe() == Vector3(0.0f, 0.0f, 0.0f));
AZ_TEST_ASSERT(Vector3(0.0f).GetNormalizedSafeEstimate() == Vector3(0.0f, 0.0f, 0.0f));
}
TEST(MATH_Vector3, TestNormalize)
{
Vector3 v1(4.0f, 3.0f, 0.0f);
v1.Normalize();
AZ_TEST_ASSERT(v1.IsClose(Vector3(4.0f / 5.0f, 3.0f / 5.0f, 0.0f)));
v1.Set(4.0f, 3.0f, 0.0f);
v1.NormalizeEstimate();
AZ_TEST_ASSERT(v1.IsClose(Vector3(4.0f / 5.0f, 3.0f / 5.0f, 0.0f)));
}
TEST(MATH_Vector3, TestNormalizeWithLength)
{
Vector3 v1(4.0f, 3.0f, 0.0f);
float length = v1.NormalizeWithLength();
AZ_TEST_ASSERT_FLOAT_CLOSE(length, 5.0f);
AZ_TEST_ASSERT(v1.IsClose(Vector3(4.0f / 5.0f, 3.0f / 5.0f, 0.0f)));
v1.Set(4.0f, 3.0f, 0.0f);
length = v1.NormalizeWithLengthEstimate();
AZ_TEST_ASSERT_FLOAT_CLOSE(length, 5.0f);
AZ_TEST_ASSERT(v1.IsClose(Vector3(4.0f / 5.0f, 3.0f / 5.0f, 0.0f)));
}
TEST(MATH_Vector3, TestNormalizeSafe)
{
Vector3 v1(0.0f, 3.0f, 4.0f);
v1.NormalizeSafe();
AZ_TEST_ASSERT(v1.IsClose(Vector3(0.0f, 3.0f / 5.0f, 4.0f / 5.0f)));
v1.Set(0.0f);
v1.NormalizeSafe();
AZ_TEST_ASSERT(v1 == Vector3(0.0f, 0.0f, 0.0f));
v1.Set(0.0f, 3.0f, 4.0f);
v1.NormalizeSafeEstimate();
AZ_TEST_ASSERT(v1.IsClose(Vector3(0.0f, 3.0f / 5.0f, 4.0f / 5.0f)));
v1.Set(0.0f);
v1.NormalizeSafeEstimate();
AZ_TEST_ASSERT(v1 == Vector3(0.0f, 0.0f, 0.0f));
}
TEST(MATH_Vector3, TestNormalizeSafeWithLength)
{
Vector3 v1(0.0f, 3.0f, 4.0f);
float length = v1.NormalizeSafeWithLength();
AZ_TEST_ASSERT_FLOAT_CLOSE(length, 5.0f);
AZ_TEST_ASSERT(v1.IsClose(Vector3(0.0f, 3.0f / 5.0f, 4.0f / 5.0f)));
v1.Set(0.0f);
length = v1.NormalizeSafeWithLength();
AZ_TEST_ASSERT(length == 0.0f);
AZ_TEST_ASSERT(v1 == Vector3(0.0f, 0.0f, 0.0f));
v1.Set(0.0f, 3.0f, 4.0f);
length = v1.NormalizeSafeWithLengthEstimate();
AZ_TEST_ASSERT_FLOAT_CLOSE(length, 5.0f);
AZ_TEST_ASSERT(v1.IsClose(Vector3(0.0f, 3.0f / 5.0f, 4.0f / 5.0f)));
v1.Set(0.0f);
length = v1.NormalizeSafeWithLengthEstimate();
AZ_TEST_ASSERT(length == 0.0f);
AZ_TEST_ASSERT(v1 == Vector3(0.0f, 0.0f, 0.0f));
}
TEST(MATH_Vector3, TestIsNormalized)
{
AZ_TEST_ASSERT(Vector3(1.0f, 0.0f, 0.0f).IsNormalized());
AZ_TEST_ASSERT(Vector3(0.7071f, 0.7071f, 0.0f).IsNormalized());
AZ_TEST_ASSERT(!Vector3(1.0f, 1.0f, 0.0f).IsNormalized());
}
TEST(MATH_Vector3, TestSetLength)
{
Vector3 v1(3.0f, 4.0f, 0.0f);
v1.SetLength(10.0f);
AZ_TEST_ASSERT(v1.IsClose(Vector3(6.0f, 8.0f, 0.0f)));
v1.Set(3.0f, 4.0f, 0.0f);
v1.SetLengthEstimate(10.0f);
AZ_TEST_ASSERT(v1.IsClose(Vector3(6.0f, 8.0f, 0.0f), 1e-3f));
}
TEST(MATH_Vector3, TestDistance)
{
Vector3 v1(1.0f, 2.0f, 3.0f);
AZ_TEST_ASSERT_FLOAT_CLOSE(v1.GetDistanceSq(Vector3(-2.0f, 6.0f, 3.0f)), 25.0f);
AZ_TEST_ASSERT_FLOAT_CLOSE(v1.GetDistance(Vector3(-2.0f, 2.0f, -1.0f)), 5.0f);
AZ_TEST_ASSERT_FLOAT_CLOSE(v1.GetDistanceEstimate(Vector3(-2.0f, 2.0f, -1.0f)), 5.0f);
}
TEST(MATH_Vector3, TestLerpSlerpNLerp)
{
AZ_TEST_ASSERT(Vector3(4.0f, 5.0f, 6.0f).Lerp(Vector3(5.0f, 10.0f, 2.0f), 0.5f).IsClose(Vector3(4.5f, 7.5f, 4.0f)));
AZ_TEST_ASSERT(Vector3(1.0f, 0.0f, 0.0f).Slerp(Vector3(0.0f, 1.0f, 0.0f), 0.5f).IsClose(Vector3(0.7071f, 0.7071f, 0.0f)));
AZ_TEST_ASSERT(Vector3(1.0f, 0.0f, 0.0f).Nlerp(Vector3(0.0f, 1.0f, 0.0f), 0.5f).IsClose(Vector3(0.7071f, 0.7071f, 0.0f)));
}
TEST(MATH_Vector3, TestDotProduct)
{
AZ_TEST_ASSERT_FLOAT_CLOSE(Vector3(1.0f, 2.0f, 3.0f).Dot(Vector3(-1.0f, 5.0f, 3.0f)), 18.0f);
}
TEST(MATH_Vector3, TestCrossProduct)
{
AZ_TEST_ASSERT(Vector3(1.0f, 2.0f, 3.0f).Cross(Vector3(3.0f, 1.0f, -1.0f)) == Vector3(-5.0f, 10.0f, -5.0f));
AZ_TEST_ASSERT(Vector3(1.0f, 2.0f, 3.0f).CrossXAxis() == Vector3(0.0f, 3.0f, -2.0f));
AZ_TEST_ASSERT(Vector3(1.0f, 2.0f, 3.0f).CrossYAxis() == Vector3(-3.0f, 0.0f, 1.0f));
AZ_TEST_ASSERT(Vector3(1.0f, 2.0f, 3.0f).CrossZAxis() == Vector3(2.0f, -1.0f, 0.0f));
AZ_TEST_ASSERT(Vector3(1.0f, 2.0f, 3.0f).XAxisCross() == Vector3(0.0f, -3.0f, 2.0f));
AZ_TEST_ASSERT(Vector3(1.0f, 2.0f, 3.0f).YAxisCross() == Vector3(3.0f, 0.0f, -1.0f));
AZ_TEST_ASSERT(Vector3(1.0f, 2.0f, 3.0f).ZAxisCross() == Vector3(-2.0f, 1.0f, 0.0f));
}
TEST(MATH_Vector3, TestIsClose)
{
AZ_TEST_ASSERT(Vector3(1.0f, 2.0f, 3.0f).IsClose(Vector3(1.0f, 2.0f, 3.0f)));
AZ_TEST_ASSERT(!Vector3(1.0f, 2.0f, 3.0f).IsClose(Vector3(1.0f, 2.0f, 4.0f)));
AZ_TEST_ASSERT(Vector3(1.0f, 2.0f, 3.0f).IsClose(Vector3(1.0f, 2.0f, 3.4f), 0.5f));
}
TEST(MATH_Vector3, TestEquality)
{
Vector3 v3(1.0f, 2.0f, 3.0f);
AZ_TEST_ASSERT(v3 == Vector3(1.0f, 2.0f, 3.0f));
AZ_TEST_ASSERT(!(v3 == Vector3(1.0f, 2.0f, 4.0f)));
AZ_TEST_ASSERT(v3 != Vector3(1.0f, 2.0f, 5.0f));
AZ_TEST_ASSERT(!(v3 != Vector3(1.0f, 2.0f, 3.0f)));
}
TEST(MATH_Vector3, TestIsLessThan)
{
AZ_TEST_ASSERT(Vector3(1.0f, 2.0f, 3.0f).IsLessThan(Vector3(2.0f, 3.0f, 4.0f)));
AZ_TEST_ASSERT(!Vector3(1.0f, 2.0f, 3.0f).IsLessThan(Vector3(0.0f, 3.0f, 4.0f)));
AZ_TEST_ASSERT(!Vector3(1.0f, 2.0f, 3.0f).IsLessThan(Vector3(2.0f, 2.0f, 4.0f)));
}
TEST(MATH_Vector3, TestIsLessEqualThan)
{
AZ_TEST_ASSERT(Vector3(1.0f, 2.0f, 3.0f).IsLessEqualThan(Vector3(2.0f, 3.0f, 4.0f)));
AZ_TEST_ASSERT(!Vector3(1.0f, 2.0f, 3.0f).IsLessEqualThan(Vector3(0.0f, 3.0f, 4.0f)));
AZ_TEST_ASSERT(Vector3(1.0f, 2.0f, 3.0f).IsLessEqualThan(Vector3(2.0f, 2.0f, 4.0f)));
}
TEST(MATH_Vector3, TestIsGreaterThan)
{
AZ_TEST_ASSERT(Vector3(1.0f, 2.0f, 3.0f).IsGreaterThan(Vector3(0.0f, 1.0f, 2.0f)));
AZ_TEST_ASSERT(!Vector3(1.0f, 2.0f, 3.0f).IsGreaterThan(Vector3(0.0f, 3.0f, 2.0f)));
AZ_TEST_ASSERT(!Vector3(1.0f, 2.0f, 3.0f).IsGreaterThan(Vector3(0.0f, 2.0f, 2.0f)));
}
TEST(MATH_Vector3, TestIsGreaterEqualThan)
{
AZ_TEST_ASSERT(Vector3(1.0f, 2.0f, 3.0f).IsGreaterEqualThan(Vector3(0.0f, 1.0f, 2.0f)));
AZ_TEST_ASSERT(!Vector3(1.0f, 2.0f, 3.0f).IsGreaterEqualThan(Vector3(0.0f, 3.0f, 2.0f)));
AZ_TEST_ASSERT(Vector3(1.0f, 2.0f, 3.0f).IsGreaterEqualThan(Vector3(0.0f, 2.0f, 2.0f)));
}
TEST(MATH_Vector3, TestMinMax)
{
AZ_TEST_ASSERT(Vector3(2.0f, 5.0f, 6.0f).GetMin(Vector3(1.0f, 6.0f, 5.0f)) == Vector3(1.0f, 5.0f, 5.0f));
AZ_TEST_ASSERT(Vector3(2.0f, 5.0f, 6.0f).GetMax(Vector3(1.0f, 6.0f, 5.0f)) == Vector3(2.0f, 6.0f, 6.0f));
}
TEST(MATH_Vector3, TestClamp)
{
AZ_TEST_ASSERT(Vector3(1.0f, 2.0f, 3.0f).GetClamp(Vector3(0.0f, -1.0f, 4.0f), Vector3(2.0f, 1.0f, 10.0f)) == Vector3(1.0f, 1.0f, 4.0f));
}
TEST(MATH_Vector3, TestTrig)
{
AZ_TEST_ASSERT(Vector3(DegToRad(78.0f), DegToRad(-150.0f), DegToRad(190.0f)).GetAngleMod().IsClose(Vector3(DegToRad(78.0f), DegToRad(-150.0f), DegToRad(-170.0f))));
AZ_TEST_ASSERT(Vector3(DegToRad(390.0f), DegToRad(-190.0f), DegToRad(-400.0f)).GetAngleMod().IsClose(Vector3(DegToRad(30.0f), DegToRad(170.0f), DegToRad(-40.0f))));
AZ_TEST_ASSERT(Vector3(DegToRad(60.0f), DegToRad(105.0f), DegToRad(-174.0f)).GetSin().IsClose(Vector3(0.866f, 0.966f, -0.105f), 0.005f));
AZ_TEST_ASSERT(Vector3(DegToRad(60.0f), DegToRad(105.0f), DegToRad(-174.0f)).GetCos().IsClose(Vector3(0.5f, -0.259f, -0.995f), 0.005f));
Vector3 sin, cos;
Vector3 v1(DegToRad(60.0f), DegToRad(105.0f), DegToRad(-174.0f));
v1.GetSinCos(sin, cos);
AZ_TEST_ASSERT(sin.IsClose(Vector3(0.866f, 0.966f, -0.105f), 0.005f));
AZ_TEST_ASSERT(cos.IsClose(Vector3(0.5f, -0.259f, -0.995f), 0.005f));
}
TEST(MATH_Vector3, TestAbs)
{
AZ_TEST_ASSERT(Vector3(-1.0f, 2.0f, -5.0f).GetAbs() == Vector3(1.0f, 2.0f, 5.0f));
}
TEST(MATH_Vector3, TestReciprocal)
{
AZ_TEST_ASSERT(Vector3(2.0f, 4.0f, 5.0f).GetReciprocal().IsClose(Vector3(0.5f, 0.25f, 0.2f)));
AZ_TEST_ASSERT(Vector3(2.0f, 4.0f, 5.0f).GetReciprocalEstimate().IsClose(Vector3(0.5f, 0.25f, 0.2f), 1e-3f));
}
TEST(MATH_Vector3, TestNegate)
{
AZ_TEST_ASSERT((-Vector3(1.0f, 2.0f, -3.0f)) == Vector3(-1.0f, -2.0f, 3.0f));
}
TEST(MATH_Vector3, TestAdd)
{
AZ_TEST_ASSERT((Vector3(1.0f, 2.0f, 3.0f) + Vector3(-1.0f, 4.0f, 5.0f)) == Vector3(0.0f, 6.0f, 8.0f));
Vector3 v1(1.0f, 2.0f, 3.0f);
v1 += Vector3(5.0f, 3.0f, -1.0f);
AZ_TEST_ASSERT(v1 == Vector3(6.0f, 5.0f, 2.0f));
}
TEST(MATH_Vector3, TestSub)
{
AZ_TEST_ASSERT((Vector3(1.0f, 2.0f, 3.0f) - Vector3(-1.0f, 4.0f, 5.0f)) == Vector3(2.0f, -2.0f, -2.0f));
Vector3 v1(1.0f, 2.0f, 3.0f);
v1 += Vector3(5.0f, 3.0f, -1.0f);
v1 -= Vector3(2.0f, -1.0f, 3.0f);
AZ_TEST_ASSERT(v1 == Vector3(4.0f, 6.0f, -1.0f));
}
TEST(MATH_Vector3, TestMul)
{
AZ_TEST_ASSERT((Vector3(1.0f, 2.0f, 3.0f) * Vector3(-1.0f, 4.0f, 5.0f)) == Vector3(-1.0f, 8.0f, 15.0f));
AZ_TEST_ASSERT((Vector3(1.0f, 2.0f, 3.0f) * 2.0f) == Vector3(2.0f, 4.0f, 6.0f));
AZ_TEST_ASSERT((2.0f * Vector3(1.0f, 2.0f, 3.0f)) == Vector3(2.0f, 4.0f, 6.0f));
Vector3 v1(1.0f, 2.0f, 3.0f);
v1 += Vector3(5.0f, 3.0f, -1.0f);
v1 -= Vector3(2.0f, -1.0f, 3.0f);
v1 *= 3.0f;
AZ_TEST_ASSERT(v1 == Vector3(12.0f, 18.0f, -3.0f));
}
TEST(MATH_Vector3, TestDiv)
{
AZ_TEST_ASSERT((Vector3(1.0f, 2.0f, 3.0f) / Vector3(-1.0f, 4.0f, 5.0f)).IsClose(Vector3(-1.0f, 0.5f, 3.0f / 5.0f)));
AZ_TEST_ASSERT((Vector3(1.0f, 2.0f, 3.0f) / 2.0f).IsClose(Vector3(0.5f, 1.0f, 1.5f)));
Vector3 v1(1.0f, 2.0f, 3.0f);
v1 += Vector3(5.0f, 3.0f, -1.0f);
v1 -= Vector3(2.0f, -1.0f, 3.0f);
v1 *= 3.0f;
v1 /= 2.0f;
AZ_TEST_ASSERT(v1.IsClose(Vector3(6.0f, 9.0f, -1.5f)));
}
TEST(MATH_Vector3, TestBuildTangentBasis)
{
Vector3 v1 = Vector3(1.0f, 1.0f, 2.0f).GetNormalized();
Vector3 v2, v3;
v1.BuildTangentBasis(v2, v3);
AZ_TEST_ASSERT(v2.IsNormalized());
AZ_TEST_ASSERT(v3.IsNormalized());
AZ_TEST_ASSERT(fabsf(v2.Dot(v1)) < 0.001f);
AZ_TEST_ASSERT(fabsf(v3.Dot(v1)) < 0.001f);
AZ_TEST_ASSERT(fabsf(v2.Dot(v3)) < 0.001f);
}
TEST(MATH_Vector3, TestMadd)
{
AZ_TEST_ASSERT(Vector3(1.0f, 2.0f, 3.0f).GetMadd(Vector3(2.0f, 1.0f, 4.0f), Vector3(1.0f, 2.0f, 4.0f)) == Vector3(3.0f, 4.0f, 16.0f));
Vector3 v1(1.0f, 2.0f, 3.0f);
v1.Madd(Vector3(2.0f, 1.0f, 4.0f), Vector3(1.0f, 2.0f, 4.0f));
AZ_TEST_ASSERT(v1 == Vector3(3.0f, 4.0f, 16.0f));
}
TEST(MATH_Vector3, TestIsPerpendicular)
{
AZ_TEST_ASSERT(Vector3(1.0f, 2.0f, 0.0f).IsPerpendicular(Vector3(0.0f, 0.0f, 1.0f)));
AZ_TEST_ASSERT(!Vector3(1.0f, 2.0f, 0.0f).IsPerpendicular(Vector3(0.0f, 1.0f, 1.0f)));
}
TEST(MATH_Vector3, TestGetOrthogonalVector)
{
Vector3 v1(1.0f, 2.0f, 3.0f);
Vector3 v2 = v1.GetOrthogonalVector();
AZ_TEST_ASSERT(v1.IsPerpendicular(v2));
v1 = Vector3::CreateAxisX();
v2 = v1.GetOrthogonalVector();
AZ_TEST_ASSERT(v1.IsPerpendicular(v2));
}
TEST(MATH_Vector3, TestProject)
{
Vector3 v1(0.5f, 0.5f, 0.5f);
v1.Project(Vector3(0.0f, 2.0f, 1.0f));
AZ_TEST_ASSERT(v1 == Vector3(0.0f, 0.6f, 0.3f));
v1.Set(0.5f, 0.5f, 0.5f);
v1.ProjectOnNormal(Vector3(0.0f, 1.0f, 0.0f));
AZ_TEST_ASSERT(v1 == Vector3(0.0f, 0.5f, 0.0f));
v1.Set(1.0f, 2.0f, 3.0f);
AZ_TEST_ASSERT(v1.GetProjected(Vector3(1.0f, 1.0f, 1.0f)) == Vector3(2.0f, 2.0f, 2.0f));
AZ_TEST_ASSERT(v1.GetProjectedOnNormal(Vector3(1.0f, 0.0f, 0.0f)) == Vector3(1.0f, 0.0f, 0.0f));
}
TEST(MATH_Vector3, TestIsFinite)
{
//IsFinite
AZ_TEST_ASSERT(Vector3(1.0f, 1.0f, 1.0f).IsFinite());
const float infinity = std::numeric_limits<float>::infinity();
AZ_TEST_ASSERT(!Vector3(infinity, infinity, infinity).IsFinite());
}
TEST(MATH_Vector3, TestAngles)
{
using Vec3CalcFunc = float(Vector3::*)(const Vector3&) const;
auto angleTest = [](Vec3CalcFunc func, const Vector3& self, const Vector3& other, float target)
{
const float epsilon = 0.01f;
float value = (self.*func)(other);
AZ_TEST_ASSERT(AZ::IsClose(value, target, epsilon));
};
const Vec3CalcFunc angleFuncs[2] = { &Vector3::Angle, &Vector3::AngleSafe };
for (Vec3CalcFunc angleFunc : angleFuncs)
{
angleTest(angleFunc, Vector3{ 1.0f, 0.0f, 0.0f }, Vector3{ 0.0f, 1.0f, 0.0f }, AZ::Constants::HalfPi);
angleTest(angleFunc, Vector3{ 42.0f, 0.0f, 0.0f }, Vector3{ 0.0f, 23.0f, 0.0f }, AZ::Constants::HalfPi);
angleTest(angleFunc, Vector3{ 1.0f, 0.0f, 0.0f }, Vector3{ -1.0f, 0.0f, 0.0f }, AZ::Constants::Pi);
angleTest(angleFunc, Vector3{ 1.0f, 0.0f, 0.0f }, Vector3{ 1.0f, 1.0f, 0.0f }, AZ::Constants::QuarterPi);
angleTest(angleFunc, Vector3{ 1.0f, 0.0f, 0.0f }, Vector3{ 1.0f, 0.0f, 0.0f }, 0.f);
angleTest(angleFunc, Vector3{ 1.0f, 1.0f, 0.0f }, Vector3{ -1.0f, -1.0f, 0.0f }, AZ::Constants::Pi);
}
const Vec3CalcFunc angleDegFuncs[2] = { &Vector3::AngleDeg, &Vector3::AngleSafeDeg };
for (Vec3CalcFunc angleDegFunc : angleDegFuncs)
{
angleTest(angleDegFunc, Vector3{ 1.0f, 0.0f, 0.0f }, Vector3{ 0.0f, 1.0f, 0.0f }, 90.f);
angleTest(angleDegFunc, Vector3{ 42.0f, 0.0f, 0.0f }, Vector3{ 0.0f, 23.0f, 0.0f }, 90.f);
angleTest(angleDegFunc, Vector3{ 1.0f, 0.0f, 0.0f }, Vector3{ -1.0f, 0.0f, 0.0f }, 180.f);
angleTest(angleDegFunc, Vector3{ 1.0f, 0.0f, 0.0f }, Vector3{ 1.0f, 1.0f, 0.0f }, 45.f);
angleTest(angleDegFunc, Vector3{ 1.0f, 0.0f, 0.0f }, Vector3{ 1.0f, 0.0f, 0.0f }, 0.f);
angleTest(angleDegFunc, Vector3{ 1.0f, 1.0f, 0.0f }, Vector3{ -1.0f, -1.0f, 0.0f }, 180.f);
}
const Vec3CalcFunc angleSafeFuncs[2] = { &Vector3::AngleSafe, &Vector3::AngleSafeDeg };
for (Vec3CalcFunc angleSafeFunc : angleSafeFuncs)
{
angleTest(angleSafeFunc, Vector3{ 0.0f, 0.0f, 0.0f }, Vector3{ 0.0f, 1.0f, 0.0f }, 0.f);
angleTest(angleSafeFunc, Vector3{ 0.0f, 0.0f, 0.0f }, Vector3{ 0.0f, 0.0f, 0.0f }, 0.f);
angleTest(angleSafeFunc, Vector3{ 1.0f, 0.0f, 0.0f }, Vector3{ 0.0f, 0.0f, 0.0f }, 0.f);
angleTest(angleSafeFunc, Vector3{ 0.0f, 0.0f, 0.0f }, Vector3{ 0.0f, 323432.0f, 0.0f }, 0.f);
angleTest(angleSafeFunc, Vector3{ 323432.0f, 0.0f, 0.0f }, Vector3{ 0.0f, 0.0f, 0.0f }, 0.f);
}
}
TEST(MATH_Vector3, CompareTest)
{
Vector3 vA(-100.0f, 10.0f, 10.0f);
Vector3 vB(35.0f, -11.0f, 10.0f);
// compare equal
Vector3 rEq = Vector3::CreateSelectCmpEqual(vA, vB, Vector3(1.0f), Vector3(0.0f));
AZ_TEST_ASSERT(rEq.IsClose(Vector3(0.0f, 0.0f, 1.0f)));
// compare greater equal
Vector3 rGr = Vector3::CreateSelectCmpGreaterEqual(vA, vB, Vector3(1.0f), Vector3(0.0f));
AZ_TEST_ASSERT(rGr.IsClose(Vector3(0.0f, 1.0f, 1.0f)));
// compare greater
Vector3 rGrEq = Vector3::CreateSelectCmpGreater(vA, vB, Vector3(1.0f), Vector3(0.0f));
AZ_TEST_ASSERT(rGrEq.IsClose(Vector3(0.0f, 1.0f, 0.0f)));
}
}
@@ -0,0 +1,702 @@
/*
* 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/Vector4.h>
#include <AzCore/UnitTest/TestTypes.h>
#if defined(HAVE_BENCHMARK)
#include <random>
#include <benchmark/benchmark.h>
namespace Benchmark
{
class BM_MathVector4
: public benchmark::Fixture
{
public:
void SetUp([[maybe_unused]] const ::benchmark::State& state) override
{
m_vecDataArray.resize(1000);
const unsigned int seed = 1;
std::mt19937_64 rng(seed);
std::uniform_real_distribution<float> unif;
std::generate(m_vecDataArray.begin(), m_vecDataArray.end(), [&unif, &rng]()
{
VecData vecData;
vecData.v1 = AZ::Vector4(unif(rng), unif(rng), unif(rng), unif(rng));
vecData.v2 = AZ::Vector4(unif(rng), unif(rng), unif(rng), unif(rng));
vecData.v3 = AZ::Vector4(unif(rng), unif(rng), unif(rng), unif(rng));
vecData.v4 = AZ::Vector4(unif(rng), unif(rng), unif(rng), unif(rng));
return vecData;
});
}
struct VecData
{
AZ::Vector4 v1;
AZ::Vector4 v2;
AZ::Vector4 v3;
AZ::Vector4 v4;
};
std::vector<VecData> m_vecDataArray;
};
BENCHMARK_F(BM_MathVector4, GetSet)(benchmark::State& state)
{
for (auto _ : state)
{
AZ::Vector4 v1, v2, v3, v4;
float x = 0.0f, y = 0.0f, z = 0.0f, w = 0.0f;
for (auto& vecData : m_vecDataArray)
{
x += vecData.v1.GetX();
y += vecData.v2.GetY();
z += vecData.v3.GetZ();
w += vecData.v4.GetW();
x += vecData.v2.GetX();
y += vecData.v3.GetY();
z += vecData.v4.GetZ();
w += vecData.v1.GetW();
x += vecData.v3.GetX();
y += vecData.v4.GetY();
z += vecData.v1.GetZ();
w += vecData.v2.GetW();
x += vecData.v4.GetX();
y += vecData.v1.GetY();
z += vecData.v2.GetZ();
w += vecData.v3.GetW();
v1.SetX(x);
v2.SetX(x);
v3.SetX(x);
v4.SetX(x);
v1.SetY(y);
v2.SetY(y);
v3.SetY(y);
v4.SetY(y);
v1.SetZ(z);
v2.SetZ(z);
v3.SetZ(z);
v4.SetZ(z);
v1.SetW(w);
v2.SetW(w);
v3.SetW(w);
v4.SetW(w);
}
benchmark::DoNotOptimize(v1);
benchmark::DoNotOptimize(v2);
benchmark::DoNotOptimize(v3);
benchmark::DoNotOptimize(v4);
}
}
BENCHMARK_F(BM_MathVector4, ElementAccess)(benchmark::State& state)
{
for (auto _ : state)
{
AZ::Vector4 v1, v2, v3, v4;
float x = 0.0f, y = 0.0f, z = 0.0f, w = 0.0f;
for (auto& vecData : m_vecDataArray)
{
x += vecData.v1.GetElement(0);
y += vecData.v2.GetElement(1);
z += vecData.v3.GetElement(2);
w += vecData.v4.GetElement(3);
x += vecData.v2.GetElement(0);
y += vecData.v3.GetElement(1);
z += vecData.v4.GetElement(2);
w += vecData.v1.GetElement(3);
x += vecData.v3.GetElement(0);
y += vecData.v4.GetElement(1);
z += vecData.v1.GetElement(2);
w += vecData.v2.GetElement(3);
x += vecData.v4.GetElement(0);
y += vecData.v1.GetElement(1);
z += vecData.v2.GetElement(2);
w += vecData.v3.GetElement(3);
v1.SetElement(0, x);
v2.SetElement(0, x);
v3.SetElement(0, x);
v4.SetElement(0, x);
v1.SetElement(1, y);
v2.SetElement(1, y);
v3.SetElement(1, y);
v4.SetElement(1, y);
v1.SetElement(2, z);
v2.SetElement(2, z);
v3.SetElement(2, z);
v4.SetElement(2, z);
v1.SetElement(3, w);
v2.SetElement(3, w);
v3.SetElement(3, w);
v4.SetElement(3, w);
}
benchmark::DoNotOptimize(v1);
benchmark::DoNotOptimize(v2);
benchmark::DoNotOptimize(v3);
benchmark::DoNotOptimize(v4);
}
}
BENCHMARK_F(BM_MathVector4, CreateSelectCmpEqual)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& vecData : m_vecDataArray)
{
AZ::Vector4 result = AZ::Vector4::CreateSelectCmpEqual(vecData.v1, vecData.v2, vecData.v2, vecData.v3);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathVector4, CreateSelectCmpGreaterEqual)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& vecData : m_vecDataArray)
{
AZ::Vector4 result = AZ::Vector4::CreateSelectCmpGreaterEqual(vecData.v1, vecData.v2, vecData.v2, vecData.v3);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathVector4, CreateSelectCmpGreater)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& vecData : m_vecDataArray)
{
AZ::Vector4 result = AZ::Vector4::CreateSelectCmpGreater(vecData.v1, vecData.v2, vecData.v2, vecData.v3);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathVector4, GetNormalized)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& vecData : m_vecDataArray)
{
AZ::Vector4 result = vecData.v1.GetNormalized();
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathVector4, GetNormalizedEstimate)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& vecData : m_vecDataArray)
{
AZ::Vector4 result = vecData.v1.GetNormalizedEstimate();
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathVector4, NormalizeWithLength)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& vecData : m_vecDataArray)
{
float result = vecData.v1.NormalizeWithLength();
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathVector4, NormalizeWithLengthEstimate)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& vecData : m_vecDataArray)
{
float result = vecData.v1.NormalizeWithLengthEstimate();
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathVector4, GetNormalizedSafe)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& vecData : m_vecDataArray)
{
AZ::Vector4 result = vecData.v1.GetNormalizedSafe();
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathVector4, GetNormalizedSafeEstimate)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& vecData : m_vecDataArray)
{
AZ::Vector4 result = vecData.v1.GetNormalizedSafeEstimate();
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathVector4, GetDistance)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& vecData : m_vecDataArray)
{
float result = vecData.v2.GetDistance(vecData.v1);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathVector4, GetDistanceEstimate)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& vecData : m_vecDataArray)
{
float result = vecData.v2.GetDistanceEstimate(vecData.v1);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathVector4, Lerp)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& vecData : m_vecDataArray)
{
AZ::Vector4 result = vecData.v2.Lerp(vecData.v1, 0.0f);
benchmark::DoNotOptimize(result);
result = vecData.v2.Lerp(vecData.v1, 0.25f);
benchmark::DoNotOptimize(result);
result = vecData.v2.Lerp(vecData.v1, 0.5f);
benchmark::DoNotOptimize(result);
result = vecData.v2.Lerp(vecData.v1, 0.75f);
benchmark::DoNotOptimize(result);
result = vecData.v2.Lerp(vecData.v1, 1.0f);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathVector4, Slerp)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& vecData : m_vecDataArray)
{
AZ::Vector4 result = vecData.v2.Slerp(vecData.v1, 0.0f);
benchmark::DoNotOptimize(result);
result = vecData.v2.Slerp(vecData.v1, 0.25f);
benchmark::DoNotOptimize(result);
result = vecData.v2.Slerp(vecData.v1, 0.5f);
benchmark::DoNotOptimize(result);
result = vecData.v2.Slerp(vecData.v1, 0.75f);
benchmark::DoNotOptimize(result);
result = vecData.v2.Slerp(vecData.v1, 1.0f);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathVector4, Nlerp)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& vecData : m_vecDataArray)
{
AZ::Vector4 result = vecData.v2.Nlerp(vecData.v1, 0.0f);
benchmark::DoNotOptimize(result);
result = vecData.v2.Nlerp(vecData.v1, 0.25f);
benchmark::DoNotOptimize(result);
result = vecData.v2.Nlerp(vecData.v1, 0.5f);
benchmark::DoNotOptimize(result);
result = vecData.v2.Nlerp(vecData.v1, 0.75f);
benchmark::DoNotOptimize(result);
result = vecData.v2.Nlerp(vecData.v1, 1.0f);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathVector4, Dot)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& vecData : m_vecDataArray)
{
float result = vecData.v1.Dot(vecData.v2);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathVector4, Dot3)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& vecData : m_vecDataArray)
{
float result = vecData.v1.Dot3(vecData.v2.GetAsVector3());
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathVector4, GetHomogenized)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& vecData : m_vecDataArray)
{
AZ::Vector3 result = vecData.v1.GetHomogenized();
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathVector4, Equality)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& vecData : m_vecDataArray)
{
bool result = vecData.v1 == vecData.v2;
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathVector4, Inequality)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& vecData : m_vecDataArray)
{
bool result = vecData.v1 != vecData.v2;
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathVector4, IsLessThan)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& vecData : m_vecDataArray)
{
bool result = vecData.v1.IsLessThan(vecData.v2);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathVector4, IsLessEqualThan)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& vecData : m_vecDataArray)
{
bool result = vecData.v1.IsLessEqualThan(vecData.v2);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathVector4, IsGreaterThan)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& vecData : m_vecDataArray)
{
bool result = vecData.v1.IsGreaterThan(vecData.v2);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathVector4, IsGreaterEqualThan)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& vecData : m_vecDataArray)
{
bool result = vecData.v1.IsGreaterEqualThan(vecData.v2);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathVector4, GetMin)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& vecData : m_vecDataArray)
{
AZ::Vector4 result = vecData.v1.GetMin(vecData.v2);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathVector4, GetMax)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& vecData : m_vecDataArray)
{
AZ::Vector4 result = vecData.v1.GetMax(vecData.v2);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathVector4, GetClamp)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& vecData : m_vecDataArray)
{
AZ::Vector4 result = vecData.v1.GetClamp(vecData.v2, vecData.v3);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathVector4, Sub)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& vecData : m_vecDataArray)
{
AZ::Vector4 result = vecData.v1 - vecData.v2;
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathVector4, Sum)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& vecData : m_vecDataArray)
{
AZ::Vector4 result = vecData.v1 + vecData.v2;
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathVector4, Mul)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& vecData : m_vecDataArray)
{
AZ::Vector4 result = vecData.v1 * vecData.v2;
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathVector4, Div)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& vecData : m_vecDataArray)
{
AZ::Vector4 result = vecData.v1 / vecData.v2;
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathVector4, GetSin)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& vecData : m_vecDataArray)
{
AZ::Vector4 result = vecData.v1.GetSin();
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathVector4, GetCos)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& vecData : m_vecDataArray)
{
AZ::Vector4 result = vecData.v1.GetCos();
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathVector4, GetSinCos)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& vecData : m_vecDataArray)
{
AZ::Vector4 sin, cos;
vecData.v1.GetSinCos(sin, cos);
benchmark::DoNotOptimize(sin);
benchmark::DoNotOptimize(cos);
}
}
}
BENCHMARK_F(BM_MathVector4, GetAcos)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& vecData : m_vecDataArray)
{
AZ::Vector4 result = vecData.v1.GetAcos();
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathVector4, GetAtan)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& vecData : m_vecDataArray)
{
AZ::Vector4 result = vecData.v1.GetAtan();
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathVector4, GetAngleMod)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& vecData : m_vecDataArray)
{
AZ::Vector4 result = vecData.v1.GetAngleMod();
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathVector4, Angle)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& vecData : m_vecDataArray)
{
float result = vecData.v1.Angle(vecData.v2);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathVector4, AngleDeg)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& vecData : m_vecDataArray)
{
float result = vecData.v1.AngleDeg(vecData.v2);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathVector4, GetAbs)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& vecData : m_vecDataArray)
{
AZ::Vector4 result = vecData.v1.GetAbs();
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathVector4, GetReciprocal)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& vecData : m_vecDataArray)
{
AZ::Vector4 result = vecData.v1.GetReciprocal();
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(BM_MathVector4, GetReciprocalEstimate)(benchmark::State& state)
{
for (auto _ : state)
{
for (auto& vecData : m_vecDataArray)
{
AZ::Vector4 result = vecData.v1.GetReciprocalEstimate();
benchmark::DoNotOptimize(result);
}
}
}
}
#endif
@@ -0,0 +1,446 @@
/*
* 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/Vector4.h>
#include <AzCore/UnitTest/TestTypes.h>
using namespace AZ;
namespace UnitTest
{
float values[4] = { 10.0f, 20.0f, 30.0f, 40.0f };
TEST(MATH_Vector4, TestConstructors)
{
Vector4 v1(0.0f);
AZ_TEST_ASSERT((v1.GetX() == 0.0f) && (v1.GetY() == 0.0f) && (v1.GetZ() == 0.0f) && (v1.GetW() == 0.0f));
Vector4 v2(5.0f);
AZ_TEST_ASSERT((v2.GetX() == 5.0f) && (v2.GetY() == 5.0f) && (v2.GetZ() == 5.0f) && (v2.GetW() == 5.0f));
Vector4 v3(1.0f, 2.0f, 3.0f, 4.0f);
AZ_TEST_ASSERT((v3.GetX() == 1.0f) && (v3.GetY() == 2.0f) && (v3.GetZ() == 3.0f) && (v3.GetW() == 4.0f));
}
TEST(MATH_Vector4, TestCreateFrom)
{
Vector4 v4 = Vector4::CreateFromFloat4(values);
AZ_TEST_ASSERT((v4.GetX() == 10.0f) && (v4.GetY() == 20.0f) && (v4.GetZ() == 30.0f) && (v4.GetW() == 40.0f));
Vector4 v5 = Vector4::CreateFromVector3(Vector3(2.0f, 3.0f, 4.0f));
AZ_TEST_ASSERT((v5.GetX() == 2.0f) && (v5.GetY() == 3.0f) && (v5.GetZ() == 4.0f) && (v5.GetW() == 1.0f));
Vector4 v6 = Vector4::CreateFromVector3AndFloat(Vector3(2.0f, 3.0f, 4.0f), 5.0f);
AZ_TEST_ASSERT((v6.GetX() == 2.0f) && (v6.GetY() == 3.0f) && (v6.GetZ() == 4.0f) && (v6.GetW() == 5.0f));
}
TEST(MATH_Vector4, TestCreate)
{
AZ_TEST_ASSERT(Vector4::CreateOne() == Vector4(1.0f, 1.0f, 1.0f, 1.0f));
AZ_TEST_ASSERT(Vector4::CreateZero() == Vector4(0.0f));
}
TEST(MATH_Vector4, TestCreateAxis)
{
AZ_TEST_ASSERT(Vector4::CreateAxisX() == Vector4(1.0f, 0.0f, 0.0f, 0.0f));
AZ_TEST_ASSERT(Vector4::CreateAxisY() == Vector4(0.0f, 1.0f, 0.0f, 0.0f));
AZ_TEST_ASSERT(Vector4::CreateAxisZ() == Vector4(0.0f, 0.0f, 1.0f, 0.0f));
AZ_TEST_ASSERT(Vector4::CreateAxisW() == Vector4(0.0f, 0.0f, 0.0f, 1.0f));
}
TEST(MATH_Vector4, TestCompareEqual)
{
Vector4 vA(-100.0f, 10.0f, -1.0f, 0.0f);
Vector4 vB(35.0f, 10.0f, -5.0f, 0.0f);
Vector4 vC(35.0f, 20.0f, -1.0f, 0.0f);
// operation r.x = (cmp1.x == cmp2.x) ? vA.x : vB.x per component
Vector4 compareEqualAB = Vector4::CreateSelectCmpEqual(vA, vB, Vector4(1.0f), Vector4(0.0f));
AZ_TEST_ASSERT(compareEqualAB.IsClose(Vector4(0.0f, 1.0f, 0.0f, 1.0f)));
Vector4 compareEqualBC = Vector4::CreateSelectCmpEqual(vB, vC, Vector4(1.0f), Vector4(0.0f));
AZ_TEST_ASSERT(compareEqualBC.IsClose(Vector4(1.0f, 0.0f, 0.0f, 1.0f)));
}
TEST(MATH_Vector4, TestCompareGreaterEqual)
{
Vector4 vA(-100.0f, 10.0f, -1.0f, 0.0f);
Vector4 vB(35.0f, 10.0f, -5.0f, 0.0f);
Vector4 vD(15.0f, 30.0f, 45.0f, 0.0f);
// operation ( r.x = (cmp1.x >= cmp2.x) ? vA.x : vB.x ) per component
Vector4 compareGreaterEqualAB = Vector4::CreateSelectCmpGreaterEqual(vA, vB, Vector4(1.0f), Vector4(0.0f));
AZ_TEST_ASSERT(compareGreaterEqualAB.IsClose(Vector4(0.0f, 1.0f, 1.0f, 1.0f)));
Vector4 compareGreaterEqualBD = Vector4::CreateSelectCmpGreaterEqual(vB, vD, Vector4(1.0f), Vector4(0.0f));
AZ_TEST_ASSERT(compareGreaterEqualBD.IsClose(Vector4(1.0f, 0.0f, 0.0f, 1.0f)));
}
TEST(MATH_Vector4, TestCompareGreater)
{
Vector4 vA(-100.0f, 10.0f, -1.0f, 0.0f);
Vector4 vB(35.0f, 10.0f, -5.0f, 0.0f);
Vector4 vC(35.0f, 20.0f, -1.0f, 0.0f);
// operation ( r.x = (cmp1.x > cmp2.x) ? vA.x : vB.x ) per component
Vector4 compareGreaterAB = Vector4::CreateSelectCmpGreater(vA, vB, Vector4(1.0f), Vector4(0.0f));
AZ_TEST_ASSERT(compareGreaterAB.IsClose(Vector4(0.0f, 0.0f, 1.0f, 0.0f)));
Vector4 compareGreaterCA = Vector4::CreateSelectCmpGreater(vC, vA, Vector4(1.0f), Vector4(0.0f));
AZ_TEST_ASSERT(compareGreaterCA.IsClose(Vector4(1.0f, 1.0f, 0.0f, 0.0f)));
}
TEST(MATH_Vector4, TestGetSet)
{
Vector4 v1(2.0f, 3.0f, 4.0f, 5.0f);
AZ_TEST_ASSERT(v1 == Vector4(2.0f, 3.0f, 4.0f, 5.0f));
v1.SetX(10.0f);
AZ_TEST_ASSERT(v1 == Vector4(10.0f, 3.0f, 4.0f, 5.0f));
v1.SetY(11.0f);
AZ_TEST_ASSERT(v1 == Vector4(10.0f, 11.0f, 4.0f, 5.0f));
v1.SetZ(12.0f);
AZ_TEST_ASSERT(v1 == Vector4(10.0f, 11.0f, 12.0f, 5.0f));
v1.SetW(13.0f);
AZ_TEST_ASSERT(v1 == Vector4(10.0f, 11.0f, 12.0f, 13.0f));
v1.Set(15.0f);
AZ_TEST_ASSERT(v1 == Vector4(15.0f));
v1.Set(values);
AZ_TEST_ASSERT((v1.GetX() == 10.0f) && (v1.GetY() == 20.0f) && (v1.GetZ() == 30.0f) && (v1.GetW() == 40.0f));
v1.Set(Vector3(2.0f, 3.0f, 4.0f));
AZ_TEST_ASSERT((v1.GetX() == 2.0f) && (v1.GetY() == 3.0f) && (v1.GetZ() == 4.0f) && (v1.GetW() == 1.0f));
v1.Set(Vector3(2.0f, 3.0f, 4.0f), 5.0f);
AZ_TEST_ASSERT((v1.GetX() == 2.0f) && (v1.GetY() == 3.0f) && (v1.GetZ() == 4.0f) && (v1.GetW() == 5.0f));
}
TEST(MATH_Vector4, TestIndexOperators)
{
Vector4 v1(1.0f, 2.0f, 3.0f, 4.0f);
AZ_TEST_ASSERT(v1.GetElement(0) == 1.0f);
AZ_TEST_ASSERT(v1.GetElement(1) == 2.0f);
AZ_TEST_ASSERT(v1.GetElement(2) == 3.0f);
AZ_TEST_ASSERT(v1.GetElement(3) == 4.0f);
AZ_TEST_ASSERT(v1(0) == 1.0f);
AZ_TEST_ASSERT(v1(1) == 2.0f);
AZ_TEST_ASSERT(v1(2) == 3.0f);
AZ_TEST_ASSERT(v1(3) == 4.0f);
}
TEST(MATH_Vector4, TestGetElementSetElement)
{
Vector4 v1;
v1.SetElement(0, 5.0f);
v1.SetElement(1, 6.0f);
v1.SetElement(2, 7.0f);
v1.SetElement(3, 8.0f);
AZ_TEST_ASSERT(v1.GetElement(0) == 5.0f);
AZ_TEST_ASSERT(v1.GetElement(1) == 6.0f);
AZ_TEST_ASSERT(v1.GetElement(2) == 7.0f);
AZ_TEST_ASSERT(v1.GetElement(3) == 8.0f);
}
TEST(MATH_Vector4, TestEquality)
{
Vector4 v3(1.0f, 2.0f, 3.0f, 4.0f);
AZ_TEST_ASSERT(v3 == Vector4(1.0f, 2.0f, 3.0f, 4.0));
AZ_TEST_ASSERT(!(v3 == Vector4(1.0f, 2.0f, 3.0f, 5.0f)));
AZ_TEST_ASSERT(v3 != Vector4(1.0f, 2.0f, 3.0f, 5.0f));
AZ_TEST_ASSERT(!(v3 != Vector4(1.0f, 2.0f, 3.0f, 4.0f)));
}
TEST(MATH_Vector4, TestGetLength)
{
AZ_TEST_ASSERT_FLOAT_CLOSE(Vector4(0.0f, 3.0f, 4.0f, 0.0f).GetLengthSq(), 25.0f);
AZ_TEST_ASSERT_FLOAT_CLOSE(Vector4(0.0f, 0.0f, 4.0f, -3.0f).GetLength(), 5.0f);
AZ_TEST_ASSERT_FLOAT_CLOSE(Vector4(0.0f, 0.0f, 4.0f, -3.0f).GetLengthEstimate(), 5.0f);
}
TEST(MATH_Vector4, TestGetLengthReciprocal)
{
AZ_TEST_ASSERT_FLOAT_CLOSE(Vector4(0.0f, 0.0f, 4.0f, -3.0f).GetLengthReciprocal(), 0.2f);
AZ_TEST_ASSERT_FLOAT_CLOSE(Vector4(0.0f, 0.0f, 4.0f, -3.0f).GetLengthReciprocalEstimate(), 0.2f);
}
TEST(MATH_Vector4, TestGetNormalized)
{
AZ_TEST_ASSERT(Vector4(3.0f, 0.0f, 4.0f, 0.0f).GetNormalized().IsClose(Vector4(3.0f / 5.0f, 0.0f, 4.0f / 5.0f, 0.0f)));
AZ_TEST_ASSERT(Vector4(3.0f, 0.0f, 4.0f, 0.0f).GetNormalizedEstimate().IsClose(Vector4(3.0f / 5.0f, 0.0f, 4.0f / 5.0f, 0.0f)));
}
TEST(MATH_Vector4, TestGetNormalizedSafe)
{
AZ_TEST_ASSERT(Vector4(3.0f, 0.0f, 4.0f, 0.0f).GetNormalizedSafe().IsClose(Vector4(3.0f / 5.0f, 0.0f, 4.0f / 5.0f, 0.0f)));
AZ_TEST_ASSERT(Vector4(3.0f, 0.0f, 4.0f, 0.0f).GetNormalizedSafeEstimate().IsClose(Vector4(3.0f / 5.0f, 0.0f, 4.0f / 5.0f, 0.0f)));
AZ_TEST_ASSERT(Vector4(0.0f).GetNormalizedSafe() == Vector4(0.0f, 0.0f, 0.0f, 0.0f));
AZ_TEST_ASSERT(Vector4(0.0f).GetNormalizedSafeEstimate() == Vector4(0.0f, 0.0f, 0.0f, 0.0f));
}
TEST(MATH_Vector4, TestNormalize)
{
Vector4 v1(4.0f, 3.0f, 0.0f, 0.0f);
v1.Normalize();
AZ_TEST_ASSERT(v1.IsClose(Vector4(4.0f / 5.0f, 3.0f / 5.0f, 0.0f, 0.0f)));
v1.Set(4.0f, 3.0f, 0.0f, 0.0f);
v1.NormalizeEstimate();
AZ_TEST_ASSERT(v1.IsClose(Vector4(4.0f / 5.0f, 3.0f / 5.0f, 0.0f, 0.0f)));
}
TEST(MATH_Vector4, TestNormalizeWithLength)
{
Vector4 v1(4.0f, 3.0f, 0.0f, 0.0f);
float length = v1.NormalizeWithLength();
AZ_TEST_ASSERT_FLOAT_CLOSE(length, 5.0f);
AZ_TEST_ASSERT(v1.IsClose(Vector4(4.0f / 5.0f, 3.0f / 5.0f, 0.0f, 0.0f)));
v1.Set(4.0f, 3.0f, 0.0f, 0.0f);
length = v1.NormalizeWithLengthEstimate();
AZ_TEST_ASSERT_FLOAT_CLOSE(length, 5.0f);
AZ_TEST_ASSERT(v1.IsClose(Vector4(4.0f / 5.0f, 3.0f / 5.0f, 0.0f, 0.0f)));
}
TEST(MATH_Vector4, TestNormalizeSafe)
{
Vector4 v1(0.0f, 3.0f, 4.0f, 0.0f);
v1.NormalizeSafe();
AZ_TEST_ASSERT(v1.IsClose(Vector4(0.0f, 3.0f / 5.0f, 4.0f / 5.0f, 0.0f)));
v1.Set(0.0f);
v1.NormalizeSafe();
AZ_TEST_ASSERT(v1 == Vector4(0.0f, 0.0f, 0.0f, 0.0f));
v1.Set(0.0f, 3.0f, 4.0f, 0.0f);
v1.NormalizeSafeEstimate();
AZ_TEST_ASSERT(v1.IsClose(Vector4(0.0f, 3.0f / 5.0f, 4.0f / 5.0f, 0.0f)));
v1.Set(0.0f);
v1.NormalizeSafeEstimate();
AZ_TEST_ASSERT(v1 == Vector4(0.0f, 0.0f, 0.0f, 0.0f));
}
TEST(MATH_Vector4, TestNormalizeSafeWithLength)
{
Vector4 v1(0.0f, 3.0f, 4.0f, 0.0f);
float length = v1.NormalizeSafeWithLength();
AZ_TEST_ASSERT_FLOAT_CLOSE(length, 5.0f);
AZ_TEST_ASSERT(v1.IsClose(Vector4(0.0f, 3.0f / 5.0f, 4.0f / 5.0f, 0.0f)));
v1.Set(0.0f);
length = v1.NormalizeSafeWithLength();
AZ_TEST_ASSERT(length == 0.0f);
AZ_TEST_ASSERT(v1 == Vector4(0.0f, 0.0f, 0.0f, 0.0f));
v1.Set(0.0f, 3.0f, 4.0f, 0.0f);
length = v1.NormalizeSafeWithLengthEstimate();
AZ_TEST_ASSERT_FLOAT_CLOSE(length, 5.0f);
AZ_TEST_ASSERT(v1.IsClose(Vector4(0.0f, 3.0f / 5.0f, 4.0f / 5.0f, 0.0f)));
v1.Set(0.0f);
length = v1.NormalizeSafeWithLengthEstimate();
AZ_TEST_ASSERT(length == 0.0f);
AZ_TEST_ASSERT(v1 == Vector4(0.0f, 0.0f, 0.0f, 0.0f));
}
TEST(MATH_Vector4, TestIsNormalized)
{
AZ_TEST_ASSERT(Vector4(1.0f, 0.0f, 0.0f, 0.0f).IsNormalized());
AZ_TEST_ASSERT(Vector4(0.7071f, 0.7071f, 0.0f, 0.0f).IsNormalized());
AZ_TEST_ASSERT(!Vector4(1.0f, 1.0f, 0.0f, 0.0f).IsNormalized());
}
TEST(MATH_Vector4, TestSetLength)
{
Vector4 v1(3.0f, 4.0f, 0.0f, 0.0f);
v1.SetLength(10.0f);
AZ_TEST_ASSERT(v1.IsClose(Vector4(6.0f, 8.0f, 0.0f, 0.0f)));
v1.Set(3.0f, 4.0f, 0.0f, 0.0f);
v1.SetLengthEstimate(10.0f);
AZ_TEST_ASSERT(v1.IsClose(Vector4(6.0f, 8.0f, 0.0f, 0.0f), 1e-3f));
}
TEST(MATH_Vector4, TestDistance)
{
Vector4 v1(1.0f, 2.0f, 3.0f, 4.0f);
AZ_TEST_ASSERT_FLOAT_CLOSE(v1.GetDistanceSq(Vector4(-2.0f, 6.0f, 3.0f, 4.0f)), 25.0f);
AZ_TEST_ASSERT_FLOAT_CLOSE(v1.GetDistance(Vector4(-2.0f, 2.0f, -1.0f, 4.0f)), 5.0f);
AZ_TEST_ASSERT_FLOAT_CLOSE(v1.GetDistanceEstimate(Vector4(-2.0f, 2.0f, -1.0f, 4.0f)), 5.0f);
}
TEST(MATH_Vector4, TestIsLessThan)
{
AZ_TEST_ASSERT(Vector4(1.0f, 2.0f, 3.0f, 4.0f).IsLessThan(Vector4(2.0f, 3.0f, 4.0f, 5.0f)));
AZ_TEST_ASSERT(!Vector4(1.0f, 2.0f, 3.0f, 4.0f).IsLessThan(Vector4(0.0f, 3.0f, 4.0f, 5.0f)));
AZ_TEST_ASSERT(!Vector4(1.0f, 2.0f, 3.0f, 4.0f).IsLessThan(Vector4(1.0f, 2.0f, 4.0f, 5.0f)));
}
TEST(MATH_Vector4, TestIsLessEqualThan)
{
AZ_TEST_ASSERT(Vector4(1.0f, 2.0f, 3.0f, 4.0f).IsLessEqualThan(Vector4(2.0f, 3.0f, 4.0f, 5.0f)));
AZ_TEST_ASSERT(!Vector4(1.0f, 2.0f, 3.0f, 4.0f).IsLessEqualThan(Vector4(0.0f, 3.0f, 4.0f, 5.0f)));
AZ_TEST_ASSERT(Vector4(1.0f, 2.0f, 3.0f, 4.0f).IsLessEqualThan(Vector4(2.0f, 2.0f, 4.0f, 5.0f)));
}
TEST(MATH_Vector4, TestIsGreaterThan)
{
AZ_TEST_ASSERT(Vector4(1.0f, 2.0f, 3.0f, 4.0f).IsGreaterThan(Vector4(0.0f, 1.0f, 2.0f, 3.0f)));
AZ_TEST_ASSERT(!Vector4(1.0f, 2.0f, 3.0f, 4.0f).IsGreaterThan(Vector4(0.0f, 3.0f, 2.0f, 3.0f)));
AZ_TEST_ASSERT(!Vector4(1.0f, 2.0f, 3.0f, 4.0f).IsGreaterThan(Vector4(0.0f, 2.0f, 2.0f, 3.0f)));
}
TEST(MATH_Vector4, TestIsGreaterEqualThan)
{
AZ_TEST_ASSERT(Vector4(1.0f, 2.0f, 3.0f, 4.0f).IsGreaterEqualThan(Vector4(0.0f, 1.0f, 2.0f, 3.0f)));
AZ_TEST_ASSERT(!Vector4(1.0f, 2.0f, 3.0f, 4.0f).IsGreaterEqualThan(Vector4(0.0f, 3.0f, 2.0f, 3.0f)));
AZ_TEST_ASSERT(Vector4(1.0f, 2.0f, 3.0f, 4.0f).IsGreaterEqualThan(Vector4(0.0f, 2.0f, 2.0f, 3.0f)));
}
TEST(MATH_Vector4, TestLerpSlerpNLerp)
{
AZ_TEST_ASSERT(Vector4(4.0f, 5.0f, 6.0f, 7.0f).Lerp(Vector4(5.0f, 10.0f, 2.0f, 1.0f), 0.5f).IsClose(Vector4(4.5f, 7.5f, 4.0f, 4.0f)));
AZ_TEST_ASSERT(Vector4(1.0f, 0.0f, 0.0f, 0.0f).Slerp(Vector4(0.0f, 1.0f, 0.0f, 0.0f), 0.5f).IsClose(Vector4(0.7071f, 0.7071f, 0.0f, 0.0f)));
AZ_TEST_ASSERT(Vector4(1.0f, 0.0f, 0.0f, 0.0f).Nlerp(Vector4(0.0f, 1.0f, 0.0f, 0.0f), 0.5f).IsClose(Vector4(0.7071f, 0.7071f, 0.0f, 0.0f)));
}
TEST(MATH_Vector4, TestDot)
{
AZ_TEST_ASSERT_FLOAT_CLOSE(Vector4(1.0f, 2.0f, 3.0f, 4.0f).Dot(Vector4(-1.0f, 5.0f, 3.0f, 2.0f)), 26.0f);
AZ_TEST_ASSERT_FLOAT_CLOSE(Vector4(1.0f, 2.0f, 3.0f, 4.0f).Dot3(Vector3(-1.0f, 5.0f, 3.0f)), 18.0f);
}
TEST(MATH_Vector4, TestIsClose)
{
AZ_TEST_ASSERT(Vector4(1.0f, 2.0f, 3.0f, 4.0f).IsClose(Vector4(1.0f, 2.0f, 3.0f, 4.0f)));
AZ_TEST_ASSERT(!Vector4(1.0f, 2.0f, 3.0f, 4.0f).IsClose(Vector4(1.0f, 2.0f, 3.0f, 5.0f)));
AZ_TEST_ASSERT(Vector4(1.0f, 2.0f, 3.0f, 4.0f).IsClose(Vector4(1.0f, 2.0f, 3.0f, 4.4f), 0.5f));
}
TEST(MATH_Vector4, TestHomogenize)
{
Vector4 v1(1.0f, 2.0f, 3.0f, 4.0f);
AZ_TEST_ASSERT(v1.GetHomogenized().IsClose(Vector3(0.25f, 0.5f, 0.75f)));
v1.Homogenize();
AZ_TEST_ASSERT(v1.IsClose(Vector4(0.25f, 0.5f, 0.75f, 1.0f)));
}
TEST(MATH_Vector4, TestMinMax)
{
AZ_TEST_ASSERT(Vector4(2.0f, 5.0f, 6.0f, 7.0f).GetMin(Vector4(1.0f, 6.0f, 5.0f, 4.0f)) == Vector4(1.0f, 5.0f, 5.0f, 4.0f));
AZ_TEST_ASSERT(Vector4(2.0f, 5.0f, 6.0f, 7.0f).GetMax(Vector4(1.0f, 6.0f, 5.0f, 4.0f)) == Vector4(2.0f, 6.0f, 6.0f, 7.0f));
}
TEST(MATH_Vector4, TestClamp)
{
AZ_TEST_ASSERT(Vector4(1.0f, 2.0f, 3.0f, 4.0f).GetClamp(Vector4(0.0f, -1.0f, 4.0f, 4.0f), Vector4(2.0f, 1.0f, 10.0f, 4.0f)) == Vector4(1.0f, 1.0f, 4.0f, 4.0f));
}
TEST(MATH_Vector4, TestTrig)
{
AZ_TEST_ASSERT(Vector4(DegToRad( 78.0f), DegToRad(-150.0f), DegToRad( 190.0f), DegToRad( 78.0f)).GetAngleMod().IsClose(Vector4(DegToRad(78.0f), DegToRad(-150.0f), DegToRad(-170.0f), DegToRad(78.0f))));
AZ_TEST_ASSERT(Vector4(DegToRad(390.0f), DegToRad(-190.0f), DegToRad(-400.0f), DegToRad(390.0f)).GetAngleMod().IsClose(Vector4(DegToRad(30.0f), DegToRad(170.0f), DegToRad(-40.0f), DegToRad(30.0f))));
AZ_TEST_ASSERT(Vector4(DegToRad( 60.0f), DegToRad( 105.0f), DegToRad(-174.0f), DegToRad( 60.0f)).GetSin().IsClose(Vector4(0.866f, 0.966f, -0.105f, 0.866f), 0.005f));
AZ_TEST_ASSERT(Vector4(DegToRad( 60.0f), DegToRad( 105.0f), DegToRad(-174.0f), DegToRad( 60.0f)).GetCos().IsClose(Vector4(0.5f, -0.259f, -0.995f, 0.5f), 0.005f));
Vector4 sin, cos;
Vector4 v1(DegToRad(60.0f), DegToRad(105.0f), DegToRad(-174.0f), DegToRad(60.0f));
v1.GetSinCos(sin, cos);
AZ_TEST_ASSERT(sin.IsClose(Vector4(0.866f, 0.966f, -0.105f, 0.866f), 0.005f));
AZ_TEST_ASSERT(cos.IsClose(Vector4(0.5f, -0.259f, -0.995f, 0.5f), 0.005f));
}
TEST(MATH_Vector4, TestAbs)
{
AZ_TEST_ASSERT(Vector4(-1.0f, 2.0f, -5.0f, 1.0f).GetAbs() == Vector4(1.0f, 2.0f, 5.0f, 1.0f));
AZ_TEST_ASSERT(Vector4(1.0f, -2.0f, 5.0f, -1.0f).GetAbs() == Vector4(1.0f, 2.0f, 5.0f, 1.0f));
}
TEST(MATH_Vector4, TestReciprocal)
{
AZ_TEST_ASSERT(Vector4(2.0f, 4.0f, 5.0f, 10.0f).GetReciprocal().IsClose(Vector4(0.5f, 0.25f, 0.2f, 0.1f)));
AZ_TEST_ASSERT(Vector4(2.0f, 4.0f, 5.0f, 10.0f).GetReciprocalEstimate().IsClose(Vector4(0.5f, 0.25f, 0.2f, 0.1f), 1e-3f));
}
TEST(MATH_Vector4, TestNegate)
{
AZ_TEST_ASSERT((-Vector4(1.0f, 2.0f, -3.0f, -1.0f)) == Vector4(-1.0f, -2.0f, 3.0f, 1.0f));
}
TEST(MATH_Vector4, TestAdd)
{
AZ_TEST_ASSERT((Vector4(1.0f, 2.0f, 3.0f, 4.0f) + Vector4(-1.0f, 4.0f, 5.0f, 2.0f)) == Vector4(0.0f, 6.0f, 8.0f, 6.0f));
Vector4 v1(1.0f, 2.0f, 3.0f, 4.0f);
v1 += Vector4(5.0f, 3.0f, -1.0f, 2.0f);
AZ_TEST_ASSERT(v1 == Vector4(6.0f, 5.0f, 2.0f, 6.0f));
}
TEST(MATH_Vector4, TestSub)
{
AZ_TEST_ASSERT((Vector4(1.0f, 2.0f, 3.0f, 4.0f) - Vector4(-1.0f, 4.0f, 5.0f, 2.0f)) == Vector4(2.0f, -2.0f, -2.0f, 2.0f));
Vector4 v1(1.0f, 2.0f, 3.0f, 4.0f);
v1 += Vector4(5.0f, 3.0f, -1.0f, 2.0f);
v1 -= Vector4(2.0f, -1.0f, 3.0f, 1.0f);
AZ_TEST_ASSERT(v1 == Vector4(4.0f, 6.0f, -1.0f, 5.0f));
}
TEST(MATH_Vector4, TestMultiply)
{
AZ_TEST_ASSERT((Vector4(1.0f, 2.0f, 3.0f, 4.0f) * Vector4(-1.0f, 4.0f, 5.0f, 2.0f)) == Vector4(-1.0f, 8.0f, 15.0f, 8.0f));
AZ_TEST_ASSERT((Vector4(1.0f, 2.0f, 3.0f, 4.0f) * 2.0f) == Vector4(2.0f, 4.0f, 6.0f, 8.0f));
AZ_TEST_ASSERT((2.0f * Vector4(1.0f, 2.0f, 3.0f, 4.0f)) == Vector4(2.0f, 4.0f, 6.0f, 8.0f));
Vector4 v1(1.0f, 2.0f, 3.0f, 4.0f);
v1 += Vector4(5.0f, 3.0f, -1.0f, 2.0f);
v1 -= Vector4(2.0f, -1.0f, 3.0f, 1.0f);
v1 *= 3.0f;
AZ_TEST_ASSERT(v1 == Vector4(12.0f, 18.0f, -3.0f, 15.0f));
}
TEST(MATH_Vector4, TestDivide)
{
AZ_TEST_ASSERT((Vector4(1.0f, 2.0f, 3.0f, 4.0f) / Vector4(-1.0f, 4.0f, 5.0f, 2.0f)).IsClose(Vector4(-1.0f, 0.5f, 3.0f / 5.0f, 2.0f)));
AZ_TEST_ASSERT((Vector4(1.0f, 2.0f, 3.0f, 4.0f) / 2.0f).IsClose(Vector4(0.5f, 1.0f, 1.5f, 2.0f)));
Vector4 v1(1.0f, 2.0f, 3.0f, 4.0f);
v1 += Vector4(5.0f, 3.0f, -1.0f, 2.0f);
v1 -= Vector4(2.0f, -1.0f, 3.0f, 1.0f);
v1 *= 3.0f;
v1 /= 2.0f;
AZ_TEST_ASSERT(v1.IsClose(Vector4(6.0f, 9.0f, -1.5f, 7.5f)));
}
TEST(MATH_Vector4, TestAngles)
{
using Vec4CalcFunc = float(Vector4::*)(const Vector4&) const;
auto angleTest = [](Vec4CalcFunc func, const Vector4& self, const Vector4& other, float target)
{
const float epsilon = 0.01f;
float value = (self.*func)(other);
AZ_TEST_ASSERT(AZ::IsClose(value, target, epsilon));
};
const Vec4CalcFunc angleFuncs[2] = { &Vector4::Angle, &Vector4::AngleSafe };
for (Vec4CalcFunc angleFunc : angleFuncs)
{
angleTest(angleFunc, Vector4{ 1.0f, 0.0f, 0.0f, 0.0f }, Vector4{ 0.0f, 1.0f, 0.0f, 0.0f }, AZ::Constants::HalfPi);
angleTest(angleFunc, Vector4{ 42.0f, 0.0f, 0.0f, 0.0f }, Vector4{ 0.0f, 23.0f, 0.0f, 0.0f }, AZ::Constants::HalfPi);
angleTest(angleFunc, Vector4{ 1.0f, 0.0f, 0.0f, 0.0f }, Vector4{ -1.0f, 0.0f, 0.0f, 0.0f }, AZ::Constants::Pi);
angleTest(angleFunc, Vector4{ 1.0f, 0.0f, 0.0f, 0.0f }, Vector4{ 1.0f, 1.0f, 0.0f, 0.0f }, AZ::Constants::QuarterPi);
angleTest(angleFunc, Vector4{ 1.0f, 0.0f, 0.0f, 0.0f }, Vector4{ 1.0f, 0.0f, 0.0f, 0.0f }, 0.f);
angleTest(angleFunc, Vector4{ 1.0f, 1.0f, 0.0f, 0.0f }, Vector4{ -1.0f, -1.0f, 0.0f, 0.0f }, AZ::Constants::Pi);
}
const Vec4CalcFunc angleDegFuncs[2] = { &Vector4::AngleDeg, &Vector4::AngleSafeDeg };
for (Vec4CalcFunc angleDegFunc : angleDegFuncs)
{
angleTest(angleDegFunc, Vector4{ 1.0f, 0.0f, 0.0f, 0.0f }, Vector4{ 0.0f, 1.0f, 0.0f, 0.0f }, 90.f);
angleTest(angleDegFunc, Vector4{ 42.0f, 0.0f, 0.0f, 0.0f }, Vector4{ 0.0f, 23.0f, 0.0f, 0.0f }, 90.f);
angleTest(angleDegFunc, Vector4{ 1.0f, 0.0f, 0.0f, 0.0f }, Vector4{ -1.0f, 0.0f, 0.0f, 0.0f }, 180.f);
angleTest(angleDegFunc, Vector4{ 1.0f, 0.0f, 0.0f, 0.0f }, Vector4{ 1.0f, 1.0f, 0.0f, 0.0f }, 45.f);
angleTest(angleDegFunc, Vector4{ 1.0f, 0.0f, 0.0f, 0.0f }, Vector4{ 1.0f, 0.0f, 0.0f, 0.0f }, 0.f);
angleTest(angleDegFunc, Vector4{ 1.0f, 1.0f, 0.0f, 0.0f }, Vector4{ -1.0f, -1.0f, 0.0f, 0.0f }, 180.f);
}
const Vec4CalcFunc angleSafeFuncs[2] = { &Vector4::AngleSafe, &Vector4::AngleSafeDeg };
for (Vec4CalcFunc angleSafeFunc : angleSafeFuncs)
{
angleTest(angleSafeFunc, Vector4{ 0.0f, 0.0f, 0.0f, 0.0f }, Vector4{ 0.0f, 1.0f, 0.0f, 0.0f }, 0.f);
angleTest(angleSafeFunc, Vector4{ 0.0f, 0.0f, 0.0f, 0.0f }, Vector4{ 0.0f, 0.0f, 0.0f, 0.0f }, 0.f);
angleTest(angleSafeFunc, Vector4{ 1.0f, 0.0f, 0.0f, 0.0f }, Vector4{ 0.0f, 0.0f, 0.0f, 0.0f }, 0.f);
angleTest(angleSafeFunc, Vector4{ 0.0f, 0.0f, 0.0f, 0.0f }, Vector4{ 0.0f, 323432.0f, 0.0f, 0.0f }, 0.f);
angleTest(angleSafeFunc, Vector4{ 323432.0f, 0.0f, 0.0f, 0.0f }, Vector4{ 0.0f, 0.0f, 0.0f, 0.0f }, 0.f);
}
}
}