/* * 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 "ActorFixture.h" #include #include #include #include #include #include namespace EMotionFX { using ColliderCommandTests = ActorFixture; TEST_F(ColliderCommandTests, AddRemoveColliders) { AZStd::string result; CommandSystem::CommandManager commandManager; MCore::CommandGroup commandGroup; const AZ::u32 actorId = m_actor->GetID(); const AZStd::vector jointNames = GetTestJointNames(); const size_t jointCount = jointNames.size(); // 1. Add colliders const AZStd::string serializedBeforeAdd = SerializePhysicsSetup(m_actor.get()); for (const AZStd::string& jointName : jointNames) { CommandColliderHelpers::AddCollider(actorId, jointName, PhysicsSetup::HitDetection, azrtti_typeid(), &commandGroup); CommandColliderHelpers::AddCollider(actorId, jointName, PhysicsSetup::HitDetection, azrtti_typeid(), &commandGroup); CommandColliderHelpers::AddCollider(actorId, jointName, PhysicsSetup::HitDetection, azrtti_typeid(), &commandGroup); } EXPECT_TRUE(commandManager.ExecuteCommandGroup(commandGroup, result)); const AZStd::string serializedAfterAdd = SerializePhysicsSetup(m_actor.get()); EXPECT_EQ(jointCount * 3, PhysicsSetupUtils::CountColliders(m_actor.get(), PhysicsSetup::HitDetection)); EXPECT_EQ(jointCount, PhysicsSetupUtils::CountColliders(m_actor.get(), PhysicsSetup::HitDetection, /*ignoreShapeType*/false, Physics::ShapeType::Box)); EXPECT_TRUE(commandManager.Undo(result)); EXPECT_EQ(0, PhysicsSetupUtils::CountColliders(m_actor.get(), PhysicsSetup::HitDetection)); EXPECT_EQ(serializedBeforeAdd, SerializePhysicsSetup(m_actor.get())); EXPECT_TRUE(commandManager.Redo(result)); EXPECT_EQ(jointCount * 3, PhysicsSetupUtils::CountColliders(m_actor.get(), PhysicsSetup::HitDetection)); EXPECT_EQ(jointCount, PhysicsSetupUtils::CountColliders(m_actor.get(), PhysicsSetup::HitDetection, /*ignoreShapeType*/false, Physics::ShapeType::Box)); EXPECT_EQ(serializedAfterAdd, SerializePhysicsSetup(m_actor.get())); // 2. Remove colliders commandGroup.RemoveAllCommands(); const AZStd::string serializedBeforeRemove = SerializePhysicsSetup(m_actor.get()); size_t colliderIndexToRemove = 1; for (const AZStd::string& jointName : jointNames) { CommandColliderHelpers::RemoveCollider(actorId, jointName, PhysicsSetup::HitDetection, colliderIndexToRemove, &commandGroup); } EXPECT_TRUE(commandManager.ExecuteCommandGroup(commandGroup, result)); const AZStd::string serializedAfterRemove = SerializePhysicsSetup(m_actor.get()); EXPECT_EQ(jointCount * 2, PhysicsSetupUtils::CountColliders(m_actor.get(), PhysicsSetup::HitDetection)); EXPECT_EQ(0, PhysicsSetupUtils::CountColliders(m_actor.get(), PhysicsSetup::HitDetection, /*ignoreShapeType*/false, Physics::ShapeType::Capsule)); EXPECT_TRUE(commandManager.Undo(result)); EXPECT_EQ(jointCount * 3, PhysicsSetupUtils::CountColliders(m_actor.get(), PhysicsSetup::HitDetection)); EXPECT_EQ(serializedBeforeRemove, SerializePhysicsSetup(m_actor.get())); EXPECT_TRUE(commandManager.Redo(result)); EXPECT_EQ(jointCount * 2, PhysicsSetupUtils::CountColliders(m_actor.get(), PhysicsSetup::HitDetection)); EXPECT_EQ(0, PhysicsSetupUtils::CountColliders(m_actor.get(), PhysicsSetup::HitDetection, /*ignoreShapeType*/false, Physics::ShapeType::Capsule)); EXPECT_EQ(serializedAfterRemove, SerializePhysicsSetup(m_actor.get())); } TEST_F(ColliderCommandTests, AddRemove1000Colliders) { AZStd::string result; CommandSystem::CommandManager commandManager; MCore::CommandGroup commandGroup; const AZ::u32 actorId = m_actor->GetID(); const AZStd::string jointName = "Bip01__pelvis"; // 1. Add colliders const AZStd::string serializedBeforeAdd = SerializePhysicsSetup(m_actor.get()); const size_t colliderCount = 1000; for (AZ::u32 i = 0; i < colliderCount; ++i) { CommandColliderHelpers::AddCollider(actorId, jointName, PhysicsSetup::HitDetection, azrtti_typeid(), &commandGroup); } EXPECT_TRUE(commandManager.ExecuteCommandGroup(commandGroup, result)); const AZStd::string serializedAfterAdd = SerializePhysicsSetup(m_actor.get()); EXPECT_EQ(colliderCount, PhysicsSetupUtils::CountColliders(m_actor.get(), PhysicsSetup::HitDetection)); EXPECT_EQ(colliderCount, PhysicsSetupUtils::CountColliders(m_actor.get(), PhysicsSetup::HitDetection, /*ignoreShapeType*/false, Physics::ShapeType::Box)); EXPECT_TRUE(commandManager.Undo(result)); EXPECT_EQ(0, PhysicsSetupUtils::CountColliders(m_actor.get(), PhysicsSetup::HitDetection)); EXPECT_EQ(serializedBeforeAdd, SerializePhysicsSetup(m_actor.get())); EXPECT_TRUE(commandManager.Redo(result)); EXPECT_EQ(colliderCount, PhysicsSetupUtils::CountColliders(m_actor.get(), PhysicsSetup::HitDetection)); EXPECT_EQ(colliderCount, PhysicsSetupUtils::CountColliders(m_actor.get(), PhysicsSetup::HitDetection, /*ignoreShapeType*/false, Physics::ShapeType::Box)); EXPECT_EQ(serializedAfterAdd, SerializePhysicsSetup(m_actor.get())); // 2. Clear colliders commandGroup.RemoveAllCommands(); const AZStd::string serializedBeforeRemove = SerializePhysicsSetup(m_actor.get()); CommandColliderHelpers::ClearColliders(actorId, jointName, PhysicsSetup::HitDetection, &commandGroup); EXPECT_TRUE(commandManager.ExecuteCommandGroup(commandGroup, result)); const AZStd::string serializedAfterRemove = SerializePhysicsSetup(m_actor.get()); EXPECT_EQ(0, PhysicsSetupUtils::CountColliders(m_actor.get(), PhysicsSetup::HitDetection)); EXPECT_EQ(0, PhysicsSetupUtils::CountColliders(m_actor.get(), PhysicsSetup::HitDetection, /*ignoreShapeType*/false, Physics::ShapeType::Box)); EXPECT_TRUE(commandManager.Undo(result)); EXPECT_EQ(colliderCount, PhysicsSetupUtils::CountColliders(m_actor.get(), PhysicsSetup::HitDetection)); EXPECT_EQ(serializedBeforeRemove, SerializePhysicsSetup(m_actor.get())); EXPECT_TRUE(commandManager.Redo(result)); EXPECT_EQ(0, PhysicsSetupUtils::CountColliders(m_actor.get(), PhysicsSetup::HitDetection)); EXPECT_EQ(0, PhysicsSetupUtils::CountColliders(m_actor.get(), PhysicsSetup::HitDetection, /*ignoreShapeType*/false, Physics::ShapeType::Box)); EXPECT_EQ(serializedAfterRemove, SerializePhysicsSetup(m_actor.get())); } TEST_F(ColliderCommandTests, AutoSizingColliders) { CommandSystem::CommandManager commandManager; const AZ::u32 actorId = m_actor->GetID(); const AZStd::vector jointNames = GetTestJointNames(); ASSERT_TRUE(jointNames.size() > 0) << "The joint names test data needs at least one joint for this test."; const AZStd::string& jointName = jointNames[0]; CommandColliderHelpers::AddCollider(actorId, jointName, PhysicsSetup::HitDetection, azrtti_typeid()); const AZStd::shared_ptr& physicsSetup = m_actor->GetPhysicsSetup(); Physics::CharacterColliderConfiguration* colliderConfig = physicsSetup->GetColliderConfigByType(PhysicsSetup::HitDetection); EXPECT_NE(colliderConfig, nullptr) << "Collider config should be valid after we added a collider to it."; Physics::CharacterColliderNodeConfiguration* jointConfig = colliderConfig->FindNodeConfigByName(jointName); EXPECT_NE(jointConfig, nullptr) << "Joint config should be valid after we added a collider to it."; EXPECT_EQ(jointConfig->m_shapes.size(), 1) << "Joint config should contain one collider."; Physics::BoxShapeConfiguration* box = azdynamic_cast(jointConfig->m_shapes[0].second.get()); EXPECT_NE(box, nullptr) << "The containing collider should be a box collider."; EXPECT_TRUE(box->m_dimensions.GetLength() > AZ::g_fltEps) << "A collider with size zero won't be visible in the viewport. Make sure the auto sizing uses defaults in case of missing data."; } /////////////////////////////////////////////////////////////////////////// struct EditColliderCommandTestParameter { AZ::TypeId m_shapeType; bool m_isTrigger; AZ::Vector3 m_position; AZ::Quaternion m_rotation; std::string m_tag; float m_radius; float m_height; AZ::Vector3 m_dimensions; }; class EditColliderCommandFixture : public ActorFixture , public ::testing::WithParamInterface { }; TEST_P(EditColliderCommandFixture, EditColliderCommandTest) { AZStd::string result; CommandSystem::CommandManager commandManager; const EditColliderCommandTestParameter param = GetParam(); const AZStd::string m_jointName = "l_ankle"; const PhysicsSetup::ColliderConfigType m_configType = PhysicsSetup::ColliderConfigType::HitDetection; // Add collider to the given joint first. const AZStd::shared_ptr& physicsSetup = m_actor->GetPhysicsSetup(); EXPECT_TRUE(CommandColliderHelpers::AddCollider(m_actor->GetID(), m_jointName, m_configType, param.m_shapeType)); Physics::CharacterColliderConfiguration* characterColliderConfig = physicsSetup->GetColliderConfigByType(m_configType); ASSERT_TRUE(characterColliderConfig != nullptr); Physics::CharacterColliderNodeConfiguration* nodeConfig = CommandColliderHelpers::GetCreateNodeConfig(m_actor.get(), m_jointName, *characterColliderConfig, result); ASSERT_TRUE(nodeConfig != nullptr); EXPECT_EQ(nodeConfig->m_shapes.size(), 1); Physics::ShapeConfigurationPair& shapeConfigPair = nodeConfig->m_shapes[0]; Physics::ColliderConfiguration* colliderConfig = shapeConfigPair.first.get(); Physics::ShapeConfiguration* shapeConfig = shapeConfigPair.second.get(); Physics::BoxShapeConfiguration* boxShapeConfig = azdynamic_cast(shapeConfig); Physics::CapsuleShapeConfiguration* capsuleShapeConfig = azdynamic_cast(shapeConfig); // Create the adjust collider command and using the data from the test parameter. MCore::Command* orgCommand = CommandSystem::GetCommandManager()->FindCommand(CommandAdjustCollider::s_commandName); CommandAdjustCollider* command = aznew CommandAdjustCollider(m_actor->GetID(), m_jointName, m_configType, /*colliderIndex=*/0, orgCommand); command->SetOldIsTrigger(colliderConfig->m_isTrigger); command->SetIsTrigger(param.m_isTrigger); command->SetOldPosition(colliderConfig->m_position); command->SetPosition(param.m_position); command->SetOldRotation(colliderConfig->m_rotation); command->SetRotation(param.m_rotation); command->SetOldTag(colliderConfig->m_tag); command->SetTag(param.m_tag.c_str()); if (capsuleShapeConfig) { command->SetOldRadius(capsuleShapeConfig->m_radius); command->SetRadius(param.m_radius); command->SetOldHeight(capsuleShapeConfig->m_height); command->SetHeight(param.m_height); } if (boxShapeConfig) { command->SetOldDimensions(boxShapeConfig->m_dimensions); command->SetDimensions(param.m_dimensions); } // Check execute. const AZStd::string serializedBeforeExecute = SerializePhysicsSetup(m_actor.get()); EXPECT_TRUE(CommandSystem::GetCommandManager()->ExecuteCommand(command, result)); const AZStd::string serializedAfterExecute = SerializePhysicsSetup(m_actor.get()); EXPECT_EQ(colliderConfig->m_isTrigger, param.m_isTrigger); EXPECT_EQ(colliderConfig->m_position, param.m_position); EXPECT_EQ(colliderConfig->m_rotation, param.m_rotation); EXPECT_EQ(colliderConfig->m_tag, param.m_tag.c_str()); if (capsuleShapeConfig) { EXPECT_EQ(capsuleShapeConfig->m_radius, param.m_radius); EXPECT_EQ(capsuleShapeConfig->m_height, param.m_height); } if (boxShapeConfig) { EXPECT_EQ(boxShapeConfig->m_dimensions, param.m_dimensions); } // Check undo. EXPECT_TRUE(CommandSystem::GetCommandManager()->Undo(result)); const AZStd::string serializedAfterUndo = SerializePhysicsSetup(m_actor.get()); EXPECT_EQ(serializedAfterUndo, serializedBeforeExecute); // Check redo. EXPECT_TRUE(CommandSystem::GetCommandManager()->Redo(result)); const AZStd::string serializedAfterRedo = SerializePhysicsSetup(m_actor.get()); EXPECT_EQ(serializedAfterRedo, serializedAfterExecute); } std::vector editColliderCommandTestParameters { { azrtti_typeid(), false, AZ::Vector3::CreateZero(), AZ::Quaternion::CreateRotationX(0.0f), "Tag1", 0.0f, 0.0f, AZ::Vector3(1.0f, 2.0f, 3.0f), }, { azrtti_typeid(), true, AZ::Vector3(std::numeric_limits::max(), std::numeric_limits::max(), std::numeric_limits::max()), AZ::Quaternion::CreateRotationX(180.0f), "Tag2", 0.0f, 0.0f, AZ::Vector3(std::numeric_limits::max(), std::numeric_limits::max(), std::numeric_limits::max()), }, { azrtti_typeid(), true, AZ::Vector3(-std::numeric_limits::max(), -std::numeric_limits::max(), -std::numeric_limits::max()), AZ::Quaternion::CreateRotationX(180.0f), "Tag2", 0.0f, 0.0f, AZ::Vector3(-std::numeric_limits::max(), -std::numeric_limits::max(), -std::numeric_limits::max()), }, { azrtti_typeid(), false, AZ::Vector3::CreateAxisX(99.0f), AZ::Quaternion::CreateRotationX(45.0f), "Tag3", 1.0f, 3.0f, AZ::Vector3::CreateZero(), }, { azrtti_typeid(), true, AZ::Vector3::CreateAxisY(1.0f), AZ::Quaternion::CreateRotationX(-90.0f), "", FLT_MAX, FLT_MAX, AZ::Vector3::CreateZero(), } }; INSTANTIATE_TEST_CASE_P(EditColliderCommandTests, EditColliderCommandFixture, ::testing::ValuesIn(editColliderCommandTestParameters) ); } // namespace EMotionFX