DeformedHeightFieldShapeTest.cpp 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  1. // Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
  2. // SPDX-FileCopyrightText: 2023 Jorrit Rouwe
  3. // SPDX-License-Identifier: MIT
  4. #include <TestFramework.h>
  5. #include <Tests/Shapes/DeformedHeightFieldShapeTest.h>
  6. #include <Math/Perlin.h>
  7. #include <Jolt/Physics/Body/BodyCreationSettings.h>
  8. #include <Jolt/Physics/Collision/Shape/SphereShape.h>
  9. #include <Jolt/Physics/Collision/ShapeCast.h>
  10. #include <Jolt/Physics/Collision/CollisionCollectorImpl.h>
  11. #include <Layers.h>
  12. JPH_IMPLEMENT_RTTI_VIRTUAL(DeformedHeightFieldShapeTest)
  13. {
  14. JPH_ADD_BASE_CLASS(DeformedHeightFieldShapeTest, Test)
  15. }
  16. void DeformedHeightFieldShapeTest::Initialize()
  17. {
  18. constexpr float cCellSize = 1.0f;
  19. constexpr float cMaxHeight = 2.5f;
  20. constexpr float cSphereRadius = 2.0f;
  21. // Create height samples
  22. mHeightSamples.resize(cSampleCount * cSampleCount);
  23. for (int y = 0; y < cSampleCount; ++y)
  24. for (int x = 0; x < cSampleCount; ++x)
  25. mHeightSamples[y * cSampleCount + x] = cMaxHeight * PerlinNoise3(float(x) * 8.0f / cSampleCount, 0, float(y) * 8.0f / cSampleCount, 256, 256, 256);
  26. // Determine scale and offset of the terrain
  27. Vec3 offset(-0.5f * cCellSize * cSampleCount, 0, -0.5f * cCellSize * cSampleCount);
  28. Vec3 scale(cCellSize, 1.0f, cCellSize);
  29. // Create height field
  30. HeightFieldShapeSettings settings(mHeightSamples.data(), offset, scale, cSampleCount);
  31. settings.mBlockSize = cBlockSize;
  32. settings.mBitsPerSample = 8;
  33. settings.mMinHeightValue = -15.0f;
  34. mHeightField = static_cast<HeightFieldShape *>(settings.Create().Get().GetPtr());
  35. mHeightFieldID = mBodyInterface->CreateAndAddBody(BodyCreationSettings(mHeightField, RVec3::sZero(), Quat::sIdentity(), EMotionType::Static, Layers::NON_MOVING), EActivation::DontActivate);
  36. // Spheres on top of the terrain
  37. RefConst<Shape> sphere_shape = new SphereShape(cSphereRadius);
  38. for (float t = 0.2f; t < 12.4f; t += 0.1f)
  39. {
  40. // Get the center of the path
  41. Vec3 center = offset + GetPathCenter(t);
  42. // Cast a ray onto the terrain
  43. RShapeCast shape_cast(sphere_shape, Vec3::sReplicate(1.0f), RMat44::sTranslation(RVec3(0, 10, 0) + center), Vec3(0, -20, 0));
  44. ClosestHitCollisionCollector<CastShapeCollector> collector;
  45. mPhysicsSystem->GetNarrowPhaseQuery().CastShape(shape_cast, { }, RVec3::sZero(), collector);
  46. if (collector.mHit.mBodyID2 == mHeightFieldID)
  47. {
  48. // Create sphere on terrain
  49. BodyCreationSettings bcs(sphere_shape, shape_cast.GetPointOnRay(collector.mHit.mFraction), Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING);
  50. mBodyInterface->CreateAndAddBody(bcs, EActivation::DontActivate);
  51. }
  52. }
  53. }
  54. Vec3 DeformedHeightFieldShapeTest::GetPathCenter(float inTime) const
  55. {
  56. constexpr float cOffset = 5.0f;
  57. constexpr float cRadiusX = 60.0f;
  58. constexpr float cRadiusY = 25.0f;
  59. constexpr float cFallOff = 0.1f;
  60. constexpr float cAngularSpeed = 2.0f;
  61. constexpr float cDisplacementSpeed = 10.0f;
  62. float fall_off = exp(-cFallOff * inTime);
  63. float angle = cAngularSpeed * inTime;
  64. return Vec3(cRadiusX * Cos(angle) * fall_off + 64.0f, 0, cOffset + cDisplacementSpeed * inTime + cRadiusY * Sin(angle) * fall_off);
  65. }
  66. void DeformedHeightFieldShapeTest::PrePhysicsUpdate(const PreUpdateParams &inParams)
  67. {
  68. constexpr float cPitRadius = 6.0f;
  69. constexpr float cPitHeight = 1.0f;
  70. constexpr float cSpeedScale = 2.0f;
  71. // Calculate center of pit
  72. Vec3 center = GetPathCenter(cSpeedScale * mTime);
  73. mTime += inParams.mDeltaTime;
  74. // Calculate affected area
  75. int start_x = max((int)floor(center.GetX() - cPitRadius) & ~cBlockMask, 0);
  76. int start_y = max((int)floor(center.GetZ() - cPitRadius) & ~cBlockMask, 0);
  77. int count_x = min(((int)ceil(center.GetX() + cPitRadius) + cBlockMask) & ~cBlockMask, cSampleCount) - start_x;
  78. int count_y = min(((int)ceil(center.GetZ() + cPitRadius) + cBlockMask) & ~cBlockMask, cSampleCount) - start_y;
  79. if (count_x > 0 && count_y > 0)
  80. {
  81. // Remember COM before we change the height field
  82. Vec3 old_com = mHeightField->GetCenterOfMass();
  83. // A function to calculate the delta height at a certain distance from the center of the pit
  84. constexpr float cHalfPi = 0.5f * JPH_PI;
  85. auto pit_shape = [=](float inDistanceX, float inDistanceY) { return Cos(min(sqrt(Square(inDistanceX) + Square(inDistanceY)) * cHalfPi / cPitRadius, cHalfPi)); };
  86. AABox affected_area;
  87. for (int y = 0; y < count_y; ++y)
  88. for (int x = 0; x < count_x; ++x)
  89. {
  90. // Update the height field
  91. float delta = pit_shape(float(start_x) + x - center.GetX(), float(start_y) + y - center.GetZ()) * cPitHeight;
  92. mHeightSamples[(start_y + y) * cSampleCount + start_x + x] -= delta;
  93. // Keep track of affected area to wake up bodies
  94. affected_area.Encapsulate(mHeightField->GetPosition(start_x + x, start_y + y));
  95. }
  96. mHeightField->SetHeights(start_x, start_y, count_x, count_y, mHeightSamples.data() + start_y * cSampleCount + start_x, cSampleCount, *mTempAllocator);
  97. // Notify the shape that it has changed its bounding box
  98. mBodyInterface->NotifyShapeChanged(mHeightFieldID, old_com, false, EActivation::DontActivate);
  99. // Activate bodies in the affected area (a change in the height field doesn't wake up bodies)
  100. affected_area.ExpandBy(Vec3::sReplicate(0.1f));
  101. DefaultBroadPhaseLayerFilter broadphase_layer_filter = mPhysicsSystem->GetDefaultBroadPhaseLayerFilter(Layers::MOVING);
  102. DefaultObjectLayerFilter object_layer_filter = mPhysicsSystem->GetDefaultLayerFilter(Layers::MOVING);
  103. mBodyInterface->ActivateBodiesInAABox(affected_area, broadphase_layer_filter, object_layer_filter);
  104. }
  105. }