CharacterTest.cpp 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. // SPDX-FileCopyrightText: 2021 Jorrit Rouwe
  2. // SPDX-License-Identifier: MIT
  3. #include <TestFramework.h>
  4. #include <Tests/Character/CharacterTest.h>
  5. #include <Physics/PhysicsScene.h>
  6. #include <Physics/Collision/Shape/CapsuleShape.h>
  7. #include <Physics/Collision/Shape/RotatedTranslatedShape.h>
  8. #include <Core/StringTools.h>
  9. #include <Application/DebugUI.h>
  10. #include <Layers.h>
  11. #include <Utils/Log.h>
  12. #include <Renderer/DebugRendererImp.h>
  13. JPH_IMPLEMENT_RTTI_VIRTUAL(CharacterTest)
  14. {
  15. JPH_ADD_BASE_CLASS(CharacterTest, Test)
  16. }
  17. const char *CharacterTest::sScenes[] =
  18. {
  19. "PerlinMesh",
  20. "PerlinHeightField",
  21. "Terrain1",
  22. "Terrain2",
  23. };
  24. const char *CharacterTest::sSceneName = "Terrain2";
  25. static const float cCharacterHeightStanding = 1.35f;
  26. static const float cCharacterRadiusStanding = 0.3f;
  27. static const float cCharacterHeightCrouching = 0.8f;
  28. static const float cCharacterRadiusCrouching = 0.3f;
  29. static const float cJumpSpeed = 4.0f;
  30. static const float cCollisionTolerance = 0.05f;
  31. CharacterTest::~CharacterTest()
  32. {
  33. mCharacter->RemoveFromPhysicsSystem();
  34. }
  35. void CharacterTest::Initialize()
  36. {
  37. if (strcmp(sSceneName, "PerlinMesh") == 0)
  38. {
  39. // Default terrain
  40. CreateMeshTerrain();
  41. }
  42. else if (strcmp(sSceneName, "PerlinHeightField") == 0)
  43. {
  44. // Default terrain
  45. CreateHeightFieldTerrain();
  46. }
  47. else
  48. {
  49. // Load scene
  50. Ref<PhysicsScene> scene;
  51. if (!ObjectStreamIn::sReadObject((string("Assets/") + sSceneName + ".bof").c_str(), scene))
  52. FatalError("Failed to load scene");
  53. scene->FixInvalidScales();
  54. for (BodyCreationSettings &settings : scene->GetBodies())
  55. settings.mFriction = 0.5f;
  56. scene->CreateBodies(mPhysicsSystem);
  57. }
  58. // Create capsule shapes for all stances
  59. mStandingShape = RotatedTranslatedShapeSettings(Vec3(0, 0.5f * cCharacterHeightStanding + cCharacterRadiusStanding, 0), Quat::sIdentity(), new CapsuleShape(0.5f * cCharacterHeightStanding, cCharacterRadiusStanding)).Create().Get();
  60. mCrouchingShape = RotatedTranslatedShapeSettings(Vec3(0, 0.5f * cCharacterHeightCrouching + cCharacterRadiusCrouching, 0), Quat::sIdentity(), new CapsuleShape(0.5f * cCharacterHeightCrouching, cCharacterRadiusCrouching)).Create().Get();
  61. // Create 'player' character
  62. Ref<CharacterSettings> settings = new CharacterSettings();
  63. settings->mLayer = Layers::MOVING;
  64. settings->mShape = mStandingShape;
  65. settings->mFriction = 0.5f;
  66. mCharacter = new Character(settings, Vec3::sZero(), Quat::sIdentity(), nullptr, mPhysicsSystem);
  67. mCharacter->AddToPhysicsSystem(EActivation::Activate);
  68. }
  69. void CharacterTest::PrePhysicsUpdate(const PreUpdateParams &inParams)
  70. {
  71. // Get the state of the character
  72. Character::EGroundState ground_state = mCharacter->GetGroundState();
  73. // Determine controller input
  74. Vec3 control_input = Vec3::sZero();
  75. if (inParams.mKeyboard->IsKeyPressed(DIK_LEFT)) control_input.SetX(-1);
  76. if (inParams.mKeyboard->IsKeyPressed(DIK_RIGHT)) control_input.SetX(1);
  77. if (inParams.mKeyboard->IsKeyPressed(DIK_UP)) control_input.SetZ(-1);
  78. if (inParams.mKeyboard->IsKeyPressed(DIK_DOWN)) control_input.SetZ(1);
  79. if (control_input != Vec3::sZero())
  80. control_input = control_input.Normalized();
  81. // Cancel movement in opposite direction of normal when sliding
  82. if (ground_state == Character::EGroundState::Sliding)
  83. {
  84. Vec3 normal = mCharacter->GetGroundNormal();
  85. normal.SetY(0);
  86. if (normal.Dot(control_input) <= 0.0f)
  87. control_input = Vec3::sZero();
  88. }
  89. // Update velocity
  90. const float cMaxSpeed = 6;
  91. Vec3 current_velocity = mCharacter->GetLinearVelocity();
  92. Vec3 desired_velocity = cMaxSpeed * control_input;
  93. desired_velocity.SetY(current_velocity.GetY());
  94. Vec3 new_velocity = 0.75f * current_velocity + 0.25f * desired_velocity;
  95. // Check actions
  96. for (int key = inParams.mKeyboard->GetFirstKey(); key != 0; key = inParams.mKeyboard->GetNextKey())
  97. {
  98. if (key == DIK_RETURN)
  99. {
  100. // Stance switch
  101. mCharacter->SetShape(mCharacter->GetShape() == mStandingShape? mCrouchingShape : mStandingShape, 1.5f * mPhysicsSystem->GetPhysicsSettings().mPenetrationSlop);
  102. break;
  103. }
  104. else if (key == DIK_J)
  105. {
  106. // Jump
  107. if (ground_state == Character::EGroundState::OnGround)
  108. new_velocity += Vec3(0, cJumpSpeed, 0);
  109. }
  110. }
  111. // Update the velocity
  112. mCharacter->SetLinearVelocity(new_velocity);
  113. // Get properties
  114. Vec3 position;
  115. Quat rotation;
  116. mCharacter->GetPositionAndRotation(position, rotation);
  117. // Draw current location
  118. // Drawing prior to update since the physics system state is also that prior to the simulation step (so that all detected collisions etc. make sense)
  119. mDebugRenderer->DrawCoordinateSystem(Mat44::sRotationTranslation(rotation, position));
  120. if (ground_state != Character::EGroundState::InAir)
  121. {
  122. Vec3 ground_position = mCharacter->GetGroundPosition();
  123. Vec3 ground_normal = mCharacter->GetGroundNormal();
  124. const PhysicsMaterial *ground_material = mCharacter->GetGroundMaterial();
  125. // Draw ground position
  126. mDebugRenderer->DrawWireSphere(ground_position, 0.1f, Color::sRed);
  127. mDebugRenderer->DrawArrow(ground_position, ground_position + 2.0f * ground_normal, Color::sGreen, 0.1f);
  128. // Draw ground material
  129. mDebugRenderer->DrawText3D(ground_position, ground_material->GetDebugName());
  130. }
  131. }
  132. void CharacterTest::PostPhysicsUpdate(float inDeltaTime)
  133. {
  134. // Fetch the new ground properties
  135. mCharacter->PostSimulation(cCollisionTolerance);
  136. }
  137. void CharacterTest::GetInitialCamera(CameraState &ioState) const
  138. {
  139. // Position camera behind character
  140. Vec3 cam_tgt = Vec3(0, cCharacterHeightStanding, 0);
  141. ioState.mPos = Vec3(0, 2.5f, 5);
  142. ioState.mForward = (cam_tgt - ioState.mPos).Normalized();
  143. }
  144. Mat44 CharacterTest::GetCameraPivot(float inCameraHeading, float inCameraPitch) const
  145. {
  146. // Get properties
  147. Vec3 position;
  148. Quat rotation;
  149. mCharacter->GetPositionAndRotation(position, rotation);
  150. return Mat44::sRotationTranslation(rotation, position);
  151. }
  152. void CharacterTest::CreateSettingsMenu(DebugUI *inUI, UIElement *inSubMenu)
  153. {
  154. inUI->CreateTextButton(inSubMenu, "Select Scene", [this, inUI]() {
  155. UIElement *scene_name = inUI->CreateMenu();
  156. for (uint i = 0; i < size(sScenes); ++i)
  157. inUI->CreateTextButton(scene_name, sScenes[i], [this, i]() { sSceneName = sScenes[i]; RestartTest(); });
  158. inUI->ShowMenu(scene_name);
  159. });
  160. }