#include #include #include using namespace XCEngine::Components; using namespace XCEngine::Math; namespace { class TransformComponentTest : public ::testing::Test { protected: void SetUp() override { transform = std::make_unique(); } std::unique_ptr transform; }; TEST(TransformComponent_Test, DefaultConstructor_IdentityValues) { TransformComponent tc; EXPECT_EQ(tc.GetLocalPosition(), Vector3::Zero()); EXPECT_TRUE(tc.GetLocalRotation().Dot(Quaternion::Identity()) > 0.99f); EXPECT_EQ(tc.GetLocalScale(), Vector3::One()); } TEST(TransformComponent_Test, LocalPosition_GetSet) { TransformComponent tc; Vector3 pos(1.0f, 2.0f, 3.0f); tc.SetLocalPosition(pos); EXPECT_EQ(tc.GetLocalPosition(), pos); } TEST(TransformComponent_Test, LocalRotation_GetSet) { TransformComponent tc; Quaternion rot = Quaternion::FromAxisAngle(Vector3::Up(), PI * 0.5f); tc.SetLocalRotation(rot); EXPECT_TRUE(tc.GetLocalRotation().Dot(rot) > 0.99f); } TEST(TransformComponent_Test, LocalScale_GetSet) { TransformComponent tc; Vector3 scale(2.0f, 2.0f, 2.0f); tc.SetLocalScale(scale); EXPECT_EQ(tc.GetLocalScale(), scale); } TEST(TransformComponent_Test, LocalEulerAngles_GetSet) { TransformComponent tc; Vector3 eulers(45.0f, 30.0f, 60.0f); tc.SetLocalEulerAngles(eulers); Vector3 result = tc.GetLocalEulerAngles(); EXPECT_NEAR(result.x, eulers.x, 1.0f); EXPECT_NEAR(result.y, eulers.y, 1.0f); EXPECT_NEAR(result.z, eulers.z, 1.0f); } TEST(TransformComponent_Test, WorldPosition_NoParent_EqualsLocal) { TransformComponent tc; Vector3 pos(1.0f, 2.0f, 3.0f); tc.SetLocalPosition(pos); EXPECT_EQ(tc.GetPosition(), pos); } TEST(TransformComponent_Test, WorldPosition_WithParent) { TransformComponent parent; TransformComponent child; parent.SetLocalPosition(Vector3(1.0f, 0.0f, 0.0f)); child.SetParent(&parent); child.SetLocalPosition(Vector3(2.0f, 0.0f, 0.0f)); Vector3 worldPos = child.GetPosition(); EXPECT_NEAR(worldPos.x, 3.0f, 0.001f); } TEST(TransformComponent_Test, WorldRotation_WithParent) { TransformComponent parent; TransformComponent child; parent.SetLocalRotation(Quaternion::FromAxisAngle(Vector3::Up(), PI * 0.5f)); child.SetParent(&parent); child.SetLocalRotation(Quaternion::FromAxisAngle(Vector3::Up(), PI * 0.5f)); Quaternion worldRot = child.GetRotation(); EXPECT_TRUE(worldRot.Magnitude() > 0.0f); } TEST(TransformComponent_Test, WorldScale_WithParent) { TransformComponent parent; TransformComponent child; parent.SetLocalScale(Vector3(2.0f, 2.0f, 2.0f)); child.SetParent(&parent); child.SetLocalScale(Vector3(2.0f, 2.0f, 2.0f)); Vector3 worldScale = child.GetScale(); EXPECT_NEAR(worldScale.x, 4.0f, 0.001f); EXPECT_NEAR(worldScale.y, 4.0f, 0.001f); EXPECT_NEAR(worldScale.z, 4.0f, 0.001f); } TEST(TransformComponent_Test, DirectionVectors_Forward) { TransformComponent tc; Vector3 forward = tc.GetForward(); EXPECT_NEAR(forward.z, 1.0f, 0.001f); } TEST(TransformComponent_Test, DirectionVectors_Right) { TransformComponent tc; Vector3 right = tc.GetRight(); EXPECT_NEAR(right.x, 1.0f, 0.001f); } TEST(TransformComponent_Test, DirectionVectors_Up) { TransformComponent tc; Vector3 up = tc.GetUp(); EXPECT_NEAR(up.y, 1.0f, 0.001f); } TEST(TransformComponent_Test, LocalToWorldMatrix_Identity) { TransformComponent tc; const Matrix4x4& matrix = tc.GetLocalToWorldMatrix(); EXPECT_EQ(matrix[0][0], 1.0f); EXPECT_EQ(matrix[1][1], 1.0f); EXPECT_EQ(matrix[2][2], 1.0f); EXPECT_EQ(matrix[3][3], 1.0f); } TEST(TransformComponent_Test, LocalToWorldMatrix_Translation) { TransformComponent tc; Vector3 pos(5.0f, 10.0f, 15.0f); tc.SetLocalPosition(pos); const Matrix4x4& matrix = tc.GetLocalToWorldMatrix(); EXPECT_NEAR(matrix[0][3], pos.x, 0.001f); EXPECT_NEAR(matrix[1][3], pos.y, 0.001f); EXPECT_NEAR(matrix[2][3], pos.z, 0.001f); } TEST(TransformComponent_Test, LocalToWorldMatrix_Rotation) { TransformComponent tc; tc.SetLocalRotation(Quaternion::FromAxisAngle(Vector3::Up(), PI * 0.5f)); const Matrix4x4& matrix = tc.GetLocalToWorldMatrix(); EXPECT_NEAR(matrix[0][0], 0.0f, 0.001f); EXPECT_NEAR(matrix[0][2], 1.0f, 0.001f); } TEST(TransformComponent_Test, LookAt_Target) { TransformComponent tc; Vector3 target(10.0f, 0.0f, 0.0f); tc.SetPosition(Vector3::Zero()); tc.LookAt(target); Vector3 forward = tc.GetForward(); EXPECT_NEAR(forward.x, 1.0f, 0.1f); } TEST(TransformComponent_Test, Rotate_Eulers) { TransformComponent tc; tc.Rotate(Vector3(90.0f, 0.0f, 0.0f)); Vector3 eulers = tc.GetLocalEulerAngles(); EXPECT_TRUE(eulers.x > 80.0f); } TEST(TransformComponent_Test, Translate_Self) { TransformComponent tc; Vector3 initialPos = tc.GetLocalPosition(); tc.Translate(Vector3(1.0f, 2.0f, 3.0f), Space::Self); Vector3 newPos = tc.GetLocalPosition(); EXPECT_NEAR(newPos.x, initialPos.x + 1.0f, 0.001f); EXPECT_NEAR(newPos.y, initialPos.y + 2.0f, 0.001f); EXPECT_NEAR(newPos.z, initialPos.z + 3.0f, 0.001f); } TEST(TransformComponent_Test, TransformPoint_LocalToWorld) { TransformComponent tc; tc.SetLocalPosition(Vector3(1.0f, 2.0f, 3.0f)); Vector3 localPoint(1.0f, 0.0f, 0.0f); Vector3 worldPoint = tc.TransformPoint(localPoint); EXPECT_NEAR(worldPoint.x, 2.0f, 0.001f); EXPECT_NEAR(worldPoint.y, 2.0f, 0.001f); EXPECT_NEAR(worldPoint.z, 3.0f, 0.001f); } TEST(TransformComponent_Test, InverseTransformPoint_WorldToLocal) { TransformComponent tc; tc.SetLocalPosition(Vector3(1.0f, 2.0f, 3.0f)); Vector3 worldPoint(2.0f, 2.0f, 3.0f); Vector3 localPoint = tc.InverseTransformPoint(worldPoint); EXPECT_NEAR(localPoint.x, 1.0f, 0.001f); EXPECT_NEAR(localPoint.y, 0.0f, 0.001f); EXPECT_NEAR(localPoint.z, 0.0f, 0.001f); } TEST(TransformComponent_Test, TransformDirection) { TransformComponent tc; tc.SetLocalRotation(Quaternion::FromAxisAngle(Vector3::Up(), PI * 0.5f)); Vector3 localDir(1.0f, 0.0f, 0.0f); Vector3 worldDir = tc.TransformDirection(localDir); EXPECT_NEAR(worldDir.z, -1.0f, 0.1f); } TEST(TransformComponent_Test, SetDirty_PropagatesToChildren) { TransformComponent parent; TransformComponent child; child.SetParent(&parent); parent.SetLocalPosition(Vector3(1.0f, 2.0f, 3.0f)); child.SetLocalPosition(Vector3(1.0f, 0.0f, 0.0f)); Vector3 childWorldPosBefore = child.GetPosition(); parent.SetLocalPosition(Vector3(10.0f, 0.0f, 0.0f)); Vector3 childWorldPosAfter = child.GetPosition(); EXPECT_NE(childWorldPosBefore.x, childWorldPosAfter.x); } TEST(TransformComponent_Test, GetChildCount_Empty) { TransformComponent tc; EXPECT_EQ(tc.GetChildCount(), 0u); } TEST(TransformComponent_Test, GetChild_InvalidIndex) { TransformComponent tc; EXPECT_EQ(tc.GetChild(0), nullptr); } TEST(TransformComponent_Test, DetachChildren) { TransformComponent parent; TransformComponent child1; TransformComponent child2; child1.SetParent(&parent); child2.SetParent(&parent); EXPECT_EQ(parent.GetChildCount(), 2u); parent.DetachChildren(); EXPECT_EQ(parent.GetChildCount(), 0u); } TEST(TransformComponent_Test, SetAsFirstSibling) { TransformComponent parent; TransformComponent child1; TransformComponent child2; child1.SetParent(&parent); child2.SetParent(&parent); child2.SetAsFirstSibling(); EXPECT_EQ(child2.GetSiblingIndex(), 0); } TEST(TransformComponent_Test, SetAsLastSibling) { TransformComponent parent; TransformComponent child1; TransformComponent child2; child1.SetParent(&parent); child2.SetParent(&parent); child1.SetAsLastSibling(); EXPECT_EQ(child1.GetSiblingIndex(), 1); } } // namespace