TransformedShapeTests.cpp 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104
  1. // SPDX-FileCopyrightText: 2021 Jorrit Rouwe
  2. // SPDX-License-Identifier: MIT
  3. #include "UnitTestFramework.h"
  4. #include "PhysicsTestContext.h"
  5. #include <Jolt/Physics/Collision/Shape/ScaledShape.h>
  6. #include <Jolt/Physics/Collision/Shape/BoxShape.h>
  7. #include <Jolt/Physics/Collision/Shape/RotatedTranslatedShape.h>
  8. #include <Jolt/Physics/Collision/TransformedShape.h>
  9. #include <Jolt/Physics/Collision/CollisionCollectorImpl.h>
  10. #include <Jolt/Physics/Collision/RayCast.h>
  11. #include <Jolt/Physics/Collision/CastResult.h>
  12. #include <Jolt/Physics/Collision/PhysicsMaterialSimple.h>
  13. TEST_SUITE("TransformedShapeTests")
  14. {
  15. TEST_CASE("TestTransformedShape")
  16. {
  17. const Vec3 half_extents(0.5f, 1.0f, 1.5f);
  18. const Vec3 scale(-2, 3, 4);
  19. const Vec3 rtshape_translation(1, 3, 5);
  20. const Quat rtshape_rotation = Quat::sRotation(Vec3(1, 2, 3).Normalized(), 0.25f * JPH_PI);
  21. const Vec3 translation(13, 9, 7);
  22. const Quat rotation = Quat::sRotation(Vec3::sAxisY(), 0.5f * JPH_PI); // A rotation of 90 degrees in order to not shear the shape
  23. PhysicsMaterialSimple *material = new PhysicsMaterialSimple("Test Material", Color::sRed);
  24. // Create a scaled, rotated and translated box
  25. BoxShapeSettings box_settings(half_extents, 0.0f, material);
  26. box_settings.SetEmbedded();
  27. ScaledShapeSettings scale_settings(&box_settings, scale);
  28. scale_settings.SetEmbedded();
  29. RotatedTranslatedShapeSettings rtshape_settings(rtshape_translation, rtshape_rotation, &scale_settings);
  30. rtshape_settings.SetEmbedded();
  31. // Create a body with this shape
  32. PhysicsTestContext c;
  33. Body &body = c.CreateBody(&rtshape_settings, translation, rotation, EMotionType::Static, EMotionQuality::Discrete, 0, EActivation::DontActivate);
  34. // Collect the leaf shape transform
  35. AllHitCollisionCollector<TransformedShapeCollector> collector;
  36. c.GetSystem()->GetNarrowPhaseQuery().CollectTransformedShapes(AABox::sBiggest(), collector);
  37. // Check that there is exactly 1 shape
  38. CHECK(collector.mHits.size() == 1);
  39. TransformedShape &ts = collector.mHits.front();
  40. // Check that we got the leaf shape: box
  41. CHECK(ts.mShape == box_settings.Create().Get());
  42. // Check that its transform matches the transform that we provided
  43. Mat44 calc_transform = Mat44::sRotationTranslation(rotation, translation) * Mat44::sRotationTranslation(rtshape_rotation, rtshape_translation) * Mat44::sScale(scale);
  44. CHECK_APPROX_EQUAL(calc_transform, ts.GetWorldTransform());
  45. // Check that all corner points are in the bounding box
  46. AABox aabox = ts.GetWorldSpaceBounds();
  47. Vec3 corners[] = {
  48. Vec3(-0.99f, -0.99f, -0.99f) * half_extents,
  49. Vec3( 0.99f, -0.99f, -0.99f) * half_extents,
  50. Vec3(-0.99f, 0.99f, -0.99f) * half_extents,
  51. Vec3( 0.99f, 0.99f, -0.99f) * half_extents,
  52. Vec3(-0.99f, -0.99f, 0.99f) * half_extents,
  53. Vec3( 0.99f, -0.99f, 0.99f) * half_extents,
  54. Vec3(-0.99f, 0.99f, 0.99f) * half_extents,
  55. Vec3( 0.99f, 0.99f, 0.99f) * half_extents
  56. };
  57. for (Vec3 corner : corners)
  58. {
  59. CHECK(aabox.Contains(calc_transform * corner));
  60. CHECK(!aabox.Contains(calc_transform * (2 * corner))); // Check that points twice as far away are not in the box
  61. }
  62. // Now pick a point on the box near the edge in local space, determine a raycast that hits it
  63. const Vec3 point_on_box(half_extents.GetX() - 0.01f, half_extents.GetY() - 0.01f, half_extents.GetZ());
  64. const Vec3 normal_on_box(0, 0, 1);
  65. const Vec3 ray_direction_local(1, 1, -1);
  66. // Transform to world space and do the raycast
  67. Vec3 ray_start_local = point_on_box - ray_direction_local;
  68. Vec3 ray_end_local = point_on_box + ray_direction_local;
  69. Vec3 ray_start_world = calc_transform * ray_start_local;
  70. Vec3 ray_end_world = calc_transform * ray_end_local;
  71. Vec3 ray_direction_world = ray_end_world - ray_start_world;
  72. RayCast ray_in_world { ray_start_world, ray_direction_world };
  73. RayCastResult hit;
  74. ts.CastRay(ray_in_world, hit);
  75. // Check the hit result
  76. CHECK_APPROX_EQUAL(hit.mFraction, 0.5f);
  77. CHECK(hit.mBodyID == body.GetID());
  78. CHECK(ts.GetMaterial(hit.mSubShapeID2) == material);
  79. Vec3 world_space_normal = ts.GetWorldSpaceSurfaceNormal(hit.mSubShapeID2, ray_in_world.GetPointOnRay(hit.mFraction));
  80. Vec3 expected_normal = (calc_transform.GetDirectionPreservingMatrix() * normal_on_box).Normalized();
  81. CHECK_APPROX_EQUAL(world_space_normal, expected_normal);
  82. // Reset the transform to identity and check that it worked
  83. ts.SetWorldTransform(Mat44::sIdentity());
  84. CHECK_APPROX_EQUAL(ts.GetWorldTransform(), Mat44::sIdentity());
  85. // Set the calculated world transform again to see if getting/setting a transform is symmetric
  86. ts.SetWorldTransform(calc_transform);
  87. CHECK_APPROX_EQUAL(calc_transform, ts.GetWorldTransform());
  88. }
  89. }