MutableCompoundShapeTest.cpp 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. // Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
  2. // SPDX-FileCopyrightText: 2021 Jorrit Rouwe
  3. // SPDX-License-Identifier: MIT
  4. #include <TestFramework.h>
  5. #include <Tests/Shapes/MutableCompoundShapeTest.h>
  6. #include <Jolt/Physics/Collision/Shape/StaticCompoundShape.h>
  7. #include <Jolt/Physics/Collision/Shape/MutableCompoundShape.h>
  8. #include <Jolt/Physics/Collision/Shape/BoxShape.h>
  9. #include <Jolt/Physics/Collision/Shape/TaperedCapsuleShape.h>
  10. #include <Jolt/Physics/Collision/Shape/CylinderShape.h>
  11. #include <Jolt/Physics/Body/BodyCreationSettings.h>
  12. #include <Jolt/Core/StreamWrapper.h>
  13. #include <Layers.h>
  14. JPH_IMPLEMENT_RTTI_VIRTUAL(MutableCompoundShapeTest)
  15. {
  16. JPH_ADD_BASE_CLASS(MutableCompoundShapeTest, Test)
  17. }
  18. void MutableCompoundShapeTest::Initialize()
  19. {
  20. // Floor (extra thick because we can randomly add sub shapes that then may stick out underneath the floor and cause objects to be pushed through)
  21. mBodyInterface->CreateAndAddBody(BodyCreationSettings(new BoxShape(Vec3(100.0f, 10.0f, 100.0f), 0.0f), RVec3(0.0f, -10.0f, 0.0f), Quat::sIdentity(), EMotionType::Static, Layers::NON_MOVING), EActivation::DontActivate);
  22. // Compound with sub compound and rotation
  23. StaticCompoundShapeSettings sub_compound_settings;
  24. sub_compound_settings.AddShape(Vec3(0, 1.5f, 0), Quat::sRotation(Vec3::sAxisZ(), 0.5f * JPH_PI), new BoxShape(Vec3(1.5f, 0.25f, 0.2f)));
  25. sub_compound_settings.AddShape(Vec3(1.5f, 0, 0), Quat::sRotation(Vec3::sAxisZ(), 0.5f * JPH_PI), new CylinderShape(1.5f, 0.2f));
  26. sub_compound_settings.AddShape(Vec3(0, 0, 1.5f), Quat::sRotation(Vec3::sAxisX(), 0.5f * JPH_PI), new TaperedCapsuleShapeSettings(1.5f, 0.25f, 0.2f));
  27. mSubCompound = sub_compound_settings.Create().Get();
  28. for (int i = 0; i < 10; ++i)
  29. {
  30. // Create a mutable compound per body and fill it up with 2 shapes initially
  31. Ref<MutableCompoundShapeSettings> compound_shape = new MutableCompoundShapeSettings;
  32. compound_shape->AddShape(Vec3::sZero(), Quat::sRotation(Vec3::sAxisX(), -0.25f * JPH_PI) * Quat::sRotation(Vec3::sAxisZ(), 0.25f * JPH_PI), mSubCompound);
  33. compound_shape->AddShape(Vec3::sZero(), Quat::sRotation(Vec3::sAxisX(), 0.25f * JPH_PI) * Quat::sRotation(Vec3::sAxisZ(), -0.75f * JPH_PI), mSubCompound);
  34. // Create a body
  35. BodyID body_id = mBodyInterface->CreateAndAddBody(BodyCreationSettings(compound_shape, RVec3(0, 10.0f + 5.0f * i, 0), Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING), EActivation::Activate);
  36. mBodyIDs.push_back(body_id);
  37. }
  38. }
  39. void MutableCompoundShapeTest::PrePhysicsUpdate(const PreUpdateParams &inParams)
  40. {
  41. BodyInterface &no_lock = mPhysicsSystem->GetBodyInterfaceNoLock();
  42. uniform_real_distribution<float> roll_distribution(0, 1);
  43. for (BodyID id : mBodyIDs)
  44. {
  45. BodyLockWrite lock(mPhysicsSystem->GetBodyLockInterface(), id);
  46. if (lock.Succeeded())
  47. {
  48. Body &body = lock.GetBody();
  49. // Get the shape
  50. MutableCompoundShape *shape = static_cast<MutableCompoundShape *>(const_cast<Shape *>(body.GetShape()));
  51. // Remember center of mass from before changes
  52. Vec3 old_com = shape->GetCenterOfMass();
  53. // Consistently seeded random engine so that bodies move in a predictable way
  54. default_random_engine consistent_random;
  55. // Simulate an engine data structure with strided positions/rotations
  56. struct PositionRotation
  57. {
  58. Vec3 mPosition;
  59. Quat mRotation;
  60. };
  61. Array<PositionRotation> pos_rot;
  62. // Animate sub shapes
  63. uint count = shape->GetNumSubShapes();
  64. for (uint i = 0; i < count; ++i)
  65. {
  66. const CompoundShape::SubShape &sub_shape = shape->GetSubShape(i);
  67. pos_rot.push_back({ Vec3::sZero(), (Quat::sRotation(Vec3::sRandom(consistent_random), DegreesToRadians(10.0f) * inParams.mDeltaTime) * sub_shape.GetRotation()).Normalized() });
  68. }
  69. // Set the new rotations/orientations on the sub shapes
  70. shape->ModifyShapes(0, count, &pos_rot.front().mPosition, &pos_rot.front().mRotation, sizeof(PositionRotation), sizeof(PositionRotation));
  71. // Initialize frame dependent random number generator
  72. default_random_engine frame_random(mFrameNumber++);
  73. // Roll the dice
  74. float roll = roll_distribution(frame_random);
  75. if (roll < 0.001f && count > 1)
  76. {
  77. // Remove a random shape
  78. uniform_int_distribution<uint> index_distribution(0, count - 1);
  79. shape->RemoveShape(index_distribution(frame_random));
  80. }
  81. else if (roll < 0.002f && count < 10)
  82. {
  83. // Add a shape in a random rotation
  84. shape->AddShape(Vec3::sZero(), Quat::sRandom(frame_random), mSubCompound);
  85. }
  86. // Ensure that the center of mass is updated
  87. shape->AdjustCenterOfMass();
  88. // Since we're already locking the body, we don't need to lock it again
  89. // We always update the mass properties of the shape because we're reorienting them every frame
  90. no_lock.NotifyShapeChanged(id, old_com, true, EActivation::Activate);
  91. }
  92. }
  93. }
  94. void MutableCompoundShapeTest::SaveState(StateRecorder &inStream) const
  95. {
  96. inStream.Write(mFrameNumber);
  97. for (BodyID id : mBodyIDs)
  98. {
  99. BodyLockRead lock(mPhysicsSystem->GetBodyLockInterface(), id);
  100. if (lock.Succeeded())
  101. {
  102. const Body &body = lock.GetBody();
  103. // Write the shape as a binary string
  104. stringstream data;
  105. StreamOutWrapper stream_out(data);
  106. body.GetShape()->SaveBinaryState(stream_out);
  107. inStream.Write(data.str());
  108. }
  109. }
  110. }
  111. void MutableCompoundShapeTest::RestoreState(StateRecorder &inStream)
  112. {
  113. inStream.Read(mFrameNumber);
  114. for (BodyID id : mBodyIDs)
  115. {
  116. BodyLockWrite lock(mPhysicsSystem->GetBodyLockInterface(), id);
  117. if (lock.Succeeded())
  118. {
  119. Body &body = lock.GetBody();
  120. // Read the shape as a binary string
  121. string str;
  122. if (inStream.IsValidating())
  123. {
  124. stringstream data;
  125. StreamOutWrapper stream_out(data);
  126. body.GetShape()->SaveBinaryState(stream_out);
  127. str = data.str();
  128. }
  129. inStream.Read(str);
  130. // Deserialize the shape
  131. stringstream data(str);
  132. StreamInWrapper stream_in(data);
  133. Shape::ShapeResult result = Shape::sRestoreFromBinaryState(stream_in);
  134. MutableCompoundShape *shape = StaticCast<MutableCompoundShape>(result.Get());
  135. // Restore the pointers to the sub compound
  136. ShapeList sub_shapes(shape->GetNumSubShapes(), mSubCompound);
  137. shape->RestoreSubShapeState(sub_shapes.data(), (uint)sub_shapes.size());
  138. // Update the shape (we're under lock protection, so use the no lock interface)
  139. mPhysicsSystem->GetBodyInterfaceNoLock().SetShape(id, shape, false, EActivation::DontActivate);
  140. }
  141. }
  142. }