CollidePointTests.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433
  1. // Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
  2. // SPDX-FileCopyrightText: 2021 Jorrit Rouwe
  3. // SPDX-License-Identifier: MIT
  4. #include "UnitTestFramework.h"
  5. #include "PhysicsTestContext.h"
  6. #include "Layers.h"
  7. #include <Jolt/Physics/Collision/Shape/BoxShape.h>
  8. #include <Jolt/Physics/Collision/Shape/SphereShape.h>
  9. #include <Jolt/Physics/Collision/Shape/CapsuleShape.h>
  10. #include <Jolt/Physics/Collision/Shape/CylinderShape.h>
  11. #include <Jolt/Physics/Collision/Shape/TaperedCapsuleShape.h>
  12. #include <Jolt/Physics/Collision/Shape/ConvexHullShape.h>
  13. #include <Jolt/Physics/Collision/Shape/RotatedTranslatedShape.h>
  14. #include <Jolt/Physics/Collision/Shape/ScaledShape.h>
  15. #include <Jolt/Physics/Collision/Shape/OffsetCenterOfMassShape.h>
  16. #include <Jolt/Physics/Collision/Shape/StaticCompoundShape.h>
  17. #include <Jolt/Physics/Collision/Shape/MutableCompoundShape.h>
  18. #include <Jolt/Physics/Collision/Shape/MeshShape.h>
  19. #include <Jolt/Physics/Collision/CollisionCollectorImpl.h>
  20. #include <Jolt/Physics/Collision/CollidePointResult.h>
  21. TEST_SUITE("CollidePointTests")
  22. {
  23. // Probe directions in the direction of the faces
  24. static Vec3 cube_probes[] = {
  25. Vec3(-1.0f, 0, 0),
  26. Vec3(1.0f, 0, 0),
  27. Vec3(0, -1.0f, 0),
  28. Vec3(0, 1.0f, 0),
  29. Vec3(0, 0, -1.0f),
  30. Vec3(0, 0, 1.0f)
  31. };
  32. // Probe directions in the direction of the faces
  33. static Vec3 cube_and_zero_probes[] = {
  34. Vec3(0, 0, 0),
  35. Vec3(-1.0f, 0, 0),
  36. Vec3(1.0f, 0, 0),
  37. Vec3(0, -1.0f, 0),
  38. Vec3(0, 1.0f, 0),
  39. Vec3(0, 0, -1.0f),
  40. Vec3(0, 0, 1.0f)
  41. };
  42. // Probes in the xy-plane
  43. static Vec3 xy_probes[] = {
  44. Vec3(-1.0f, 0, 0),
  45. Vec3(1.0f, 0, 0),
  46. Vec3(0, 0, -1.0f),
  47. Vec3(0, 0, 1.0f)
  48. };
  49. // Probes in the xy-plane and zero
  50. static Vec3 xy_and_zero_probes[] = {
  51. Vec3(0, 0, 0),
  52. Vec3(-1.0f, 0, 0),
  53. Vec3(1.0f, 0, 0),
  54. Vec3(0, 0, -1.0f),
  55. Vec3(0, 0, 1.0f)
  56. };
  57. // Vertices of a cube
  58. static Vec3 cube_vertices[] = {
  59. Vec3(-1.0f, -1.0f, -1.0f),
  60. Vec3( 1.0f, -1.0f, -1.0f),
  61. Vec3(-1.0f, -1.0f, 1.0f),
  62. Vec3( 1.0f, -1.0f, 1.0f),
  63. Vec3(-1.0f, 1.0f, -1.0f),
  64. Vec3( 1.0f, 1.0f, -1.0f),
  65. Vec3(-1.0f, 1.0f, 1.0f),
  66. Vec3( 1.0f, 1.0f, 1.0f)
  67. };
  68. static void sTestHit(const Shape *inShape, Vec3Arg inPosition)
  69. {
  70. AllHitCollisionCollector<CollidePointCollector> collector;
  71. inShape->CollidePoint(inPosition - inShape->GetCenterOfMass(), SubShapeIDCreator(), collector);
  72. CHECK(collector.mHits.size() == 1);
  73. }
  74. static void sTestHit(const NarrowPhaseQuery &inNarrowPhase, RVec3Arg inPosition, const BodyID &inBodyID)
  75. {
  76. AllHitCollisionCollector<CollidePointCollector> collector;
  77. inNarrowPhase.CollidePoint(inPosition, collector);
  78. CHECK(collector.mHits.size() == 1);
  79. CHECK(collector.mHits[0].mBodyID == inBodyID);
  80. }
  81. static void sTestMiss(const Shape *inShape, Vec3Arg inPosition)
  82. {
  83. AllHitCollisionCollector<CollidePointCollector> collector;
  84. inShape->CollidePoint(inPosition - inShape->GetCenterOfMass(), SubShapeIDCreator(), collector);
  85. CHECK(collector.mHits.empty());
  86. }
  87. static void sTestMiss(const NarrowPhaseQuery &inNarrowPhase, RVec3Arg inPosition)
  88. {
  89. AllHitCollisionCollector<CollidePointCollector> collector;
  90. inNarrowPhase.CollidePoint(inPosition, collector);
  91. CHECK(collector.mHits.empty());
  92. }
  93. TEST_CASE("TestCollidePointVsBox")
  94. {
  95. Vec3 half_box_size(0.1f, 0.2f, 0.3f);
  96. ShapeRefC shape = new BoxShape(half_box_size);
  97. // Hits
  98. for (Vec3 probe : cube_and_zero_probes)
  99. sTestHit(shape, 0.99f * half_box_size * probe);
  100. // Misses
  101. for (Vec3 probe : cube_probes)
  102. sTestMiss(shape, 1.01f * half_box_size * probe);
  103. }
  104. TEST_CASE("TestCollidePointVsSphere")
  105. {
  106. const float radius = 0.1f;
  107. ShapeRefC shape = new SphereShape(radius);
  108. // Hits
  109. for (Vec3 probe : cube_and_zero_probes)
  110. sTestHit(shape, 0.99f * Vec3::sReplicate(radius) * probe);
  111. // Misses
  112. for (Vec3 probe : cube_probes)
  113. sTestMiss(shape, 1.01f * Vec3::sReplicate(radius) * probe);
  114. }
  115. TEST_CASE("TestCollidePointVsCapsule")
  116. {
  117. const float half_height = 0.2f;
  118. const float radius = 0.1f;
  119. ShapeRefC shape = new CapsuleShape(half_height, radius);
  120. // Top hits
  121. for (Vec3 probe : xy_and_zero_probes)
  122. sTestHit(shape, 0.99f * radius * probe + Vec3(0, half_height, 0));
  123. // Center hit
  124. sTestHit(shape, Vec3::sZero());
  125. // Bottom hits
  126. for (Vec3 probe : xy_and_zero_probes)
  127. sTestHit(shape, 0.99f * radius * probe + Vec3(0, -half_height, 0));
  128. // Misses
  129. for (Vec3 probe : cube_probes)
  130. sTestMiss(shape, 1.01f * Vec3(radius, half_height + radius, radius) * probe);
  131. }
  132. TEST_CASE("TestCollidePointVsTaperedCapsule")
  133. {
  134. const float half_height = 0.4f;
  135. const float top_radius = 0.1f;
  136. const float bottom_radius = 0.2f;
  137. TaperedCapsuleShapeSettings settings(half_height, top_radius, bottom_radius);
  138. ShapeRefC shape = settings.Create().Get();
  139. // Top hits
  140. for (Vec3 probe : xy_and_zero_probes)
  141. sTestHit(shape, 0.99f * top_radius * probe + Vec3(0, half_height, 0));
  142. // Center hit
  143. sTestHit(shape, Vec3::sZero());
  144. // Bottom hits
  145. for (Vec3 probe : xy_and_zero_probes)
  146. sTestHit(shape, 0.99f * bottom_radius * probe + Vec3(0, -half_height, 0));
  147. // Top misses
  148. sTestMiss(shape, Vec3(0, half_height + top_radius + 0.01f, 0));
  149. for (Vec3 probe : xy_probes)
  150. sTestMiss(shape, 1.01f * top_radius * probe + Vec3(0, half_height, 0));
  151. // Bottom misses
  152. sTestMiss(shape, Vec3(0, -half_height - bottom_radius - 0.01f, 0));
  153. for (Vec3 probe : xy_probes)
  154. sTestMiss(shape, 1.01f * bottom_radius * probe + Vec3(0, -half_height, 0));
  155. }
  156. TEST_CASE("TestCollidePointVsCylinder")
  157. {
  158. const float half_height = 0.2f;
  159. const float radius = 0.1f;
  160. ShapeRefC shape = new CylinderShape(half_height, radius);
  161. // Top hits
  162. for (Vec3 probe : xy_and_zero_probes)
  163. sTestHit(shape, 0.99f * (radius * probe + Vec3(0, half_height, 0)));
  164. // Center hit
  165. sTestHit(shape, Vec3::sZero());
  166. // Bottom hits
  167. for (Vec3 probe : xy_and_zero_probes)
  168. sTestHit(shape, 0.99f * (radius * probe + Vec3(0, -half_height, 0)));
  169. // Misses
  170. for (Vec3 probe : cube_probes)
  171. sTestMiss(shape, 1.01f * Vec3(radius, half_height, radius) * probe);
  172. }
  173. TEST_CASE("TestCollidePointVsConvexHull")
  174. {
  175. Vec3 half_box_size(0.1f, 0.2f, 0.3f);
  176. Vec3 offset(10.0f, 11.0f, 12.0f);
  177. ConvexHullShapeSettings settings;
  178. for (uint i = 0; i < size(cube_vertices); ++i)
  179. settings.mPoints.push_back(offset + cube_vertices[i] * half_box_size);
  180. ShapeRefC shape = settings.Create().Get();
  181. // Hits
  182. for (Vec3 probe : cube_and_zero_probes)
  183. sTestHit(shape, offset + 0.99f * half_box_size * probe);
  184. // Misses
  185. for (Vec3 probe : cube_probes)
  186. sTestMiss(shape, offset + 1.01f * half_box_size * probe);
  187. }
  188. TEST_CASE("TestCollidePointVsRotatedTranslated")
  189. {
  190. Vec3 translation(10.0f, 11.0f, 12.0f);
  191. Quat rotation = Quat::sRotation(Vec3(1, 2, 3).Normalized(), 0.3f * JPH_PI);
  192. Mat44 transform = Mat44::sRotationTranslation(rotation, translation);
  193. Vec3 half_box_size(0.1f, 0.2f, 0.3f);
  194. RotatedTranslatedShapeSettings settings(translation, rotation, new BoxShape(half_box_size));
  195. ShapeRefC shape = settings.Create().Get();
  196. // Hits
  197. for (Vec3 probe : cube_and_zero_probes)
  198. sTestHit(shape, transform * (0.99f * half_box_size * probe));
  199. // Misses
  200. for (Vec3 probe : cube_probes)
  201. sTestMiss(shape, transform * (1.01f * half_box_size * probe));
  202. }
  203. TEST_CASE("TestCollidePointVsScaled")
  204. {
  205. Vec3 scale(2.0f, 3.0f, -4.0f);
  206. Vec3 half_box_size(0.1f, 0.2f, 0.3f);
  207. ShapeRefC shape = new ScaledShape(new BoxShape(half_box_size), scale);
  208. // Hits
  209. for (Vec3 probe : cube_and_zero_probes)
  210. sTestHit(shape, scale * (0.99f * half_box_size * probe));
  211. // Misses
  212. for (Vec3 probe : cube_probes)
  213. sTestMiss(shape, scale * (1.01f * half_box_size * probe));
  214. }
  215. TEST_CASE("TestCollidePointVsOffsetCenterOfMass")
  216. {
  217. Vec3 offset(10.0f, 11.0f, 12.0f);
  218. Vec3 half_box_size(0.1f, 0.2f, 0.3f);
  219. OffsetCenterOfMassShapeSettings settings(offset, new BoxShape(half_box_size));
  220. ShapeRefC shape = settings.Create().Get();
  221. // Hits
  222. for (Vec3 probe : cube_and_zero_probes)
  223. sTestHit(shape, 0.99f * half_box_size * probe);
  224. // Misses
  225. for (Vec3 probe : cube_probes)
  226. sTestMiss(shape, 1.01f * half_box_size * probe);
  227. }
  228. TEST_CASE("TestCollidePointVsStaticCompound")
  229. {
  230. Vec3 translation1(10.0f, 11.0f, 12.0f);
  231. Quat rotation1 = Quat::sRotation(Vec3(1, 2, 3).Normalized(), 0.3f * JPH_PI);
  232. Mat44 transform1 = Mat44::sRotationTranslation(rotation1, translation1);
  233. Vec3 translation2(-1.0f, -2.0f, -3.0f);
  234. Quat rotation2 = Quat::sRotation(Vec3(4, 5, 6).Normalized(), 0.2f * JPH_PI);
  235. Mat44 transform2 = Mat44::sRotationTranslation(rotation2, translation2);
  236. Vec3 half_box_size(0.1f, 0.2f, 0.3f);
  237. ShapeRefC box = new BoxShape(half_box_size);
  238. StaticCompoundShapeSettings settings;
  239. settings.AddShape(translation1, rotation1, box);
  240. settings.AddShape(translation2, rotation2, box);
  241. ShapeRefC shape = settings.Create().Get();
  242. // Hits
  243. for (Vec3 probe : cube_and_zero_probes)
  244. {
  245. Vec3 point = 0.99f * half_box_size * probe;
  246. sTestHit(shape, transform1 * point);
  247. sTestHit(shape, transform2 * point);
  248. }
  249. // Misses
  250. for (Vec3 probe : cube_probes)
  251. {
  252. Vec3 point = 1.01f * half_box_size * probe;
  253. sTestMiss(shape, transform1 * point);
  254. sTestMiss(shape, transform2 * point);
  255. }
  256. }
  257. TEST_CASE("TestCollidePointVsMutableCompound")
  258. {
  259. Vec3 translation1(10.0f, 11.0f, 12.0f);
  260. Quat rotation1 = Quat::sRotation(Vec3(1, 2, 3).Normalized(), 0.3f * JPH_PI);
  261. Mat44 transform1 = Mat44::sRotationTranslation(rotation1, translation1);
  262. Vec3 translation2(-1.0f, -2.0f, -3.0f);
  263. Quat rotation2 = Quat::sRotation(Vec3(4, 5, 6).Normalized(), 0.2f * JPH_PI);
  264. Mat44 transform2 = Mat44::sRotationTranslation(rotation2, translation2);
  265. Vec3 half_box_size(0.1f, 0.2f, 0.3f);
  266. ShapeRefC box = new BoxShape(half_box_size);
  267. MutableCompoundShapeSettings settings;
  268. settings.AddShape(translation1, rotation1, box);
  269. settings.AddShape(translation2, rotation2, box);
  270. ShapeRefC shape = settings.Create().Get();
  271. // Hits
  272. for (Vec3 probe : cube_and_zero_probes)
  273. {
  274. Vec3 point = 0.99f * half_box_size * probe;
  275. sTestHit(shape, transform1 * point);
  276. sTestHit(shape, transform2 * point);
  277. }
  278. // Misses
  279. for (Vec3 probe : cube_probes)
  280. {
  281. Vec3 point = 1.01f * half_box_size * probe;
  282. sTestMiss(shape, transform1 * point);
  283. sTestMiss(shape, transform2 * point);
  284. }
  285. }
  286. TEST_CASE("TestCollidePointVsMesh")
  287. {
  288. // Face indices of a cube
  289. int indices[][3] = {
  290. { 0, 1, 3 },
  291. { 0, 3, 2 },
  292. { 4, 7, 5 },
  293. { 4, 6, 7 },
  294. { 2, 3, 6 },
  295. { 3, 7, 6 },
  296. { 1, 0, 4 },
  297. { 1, 4, 5 },
  298. { 1, 7, 3 },
  299. { 1, 5, 7 },
  300. { 0, 2, 6 },
  301. { 0, 6, 4 }
  302. };
  303. const int grid_size = 2;
  304. UnitTestRandom random;
  305. uniform_real_distribution<float> range(0.1f, 0.3f);
  306. // Create a grid of closed shapes
  307. MeshShapeSettings settings;
  308. settings.SetEmbedded();
  309. int num_cubes = Cubed(2 * grid_size + 1);
  310. settings.mTriangleVertices.reserve(num_cubes * size(cube_vertices));
  311. settings.mIndexedTriangles.reserve(num_cubes * size(indices));
  312. for (int x = -grid_size; x <= grid_size; ++x)
  313. for (int y = -grid_size; y <= grid_size; ++y)
  314. for (int z = -grid_size; z <= grid_size; ++z)
  315. {
  316. Vec3 center((float)x, (float)y, (float)z);
  317. // Create vertices with randomness
  318. uint vtx = (uint)settings.mTriangleVertices.size();
  319. settings.mTriangleVertices.resize(vtx + size(cube_vertices));
  320. for (uint i = 0; i < size(cube_vertices); ++i)
  321. {
  322. Vec3 vertex(center + cube_vertices[i] * Vec3(range(random), range(random), range(random)));
  323. vertex.StoreFloat3(&settings.mTriangleVertices[vtx + i]);
  324. }
  325. // Flip inside out? (inside out shapes should act the same as normal shapes for CollidePoint)
  326. bool flip = (y & 1) == 0;
  327. // Create face indices
  328. uint idx = (uint)settings.mIndexedTriangles.size();
  329. settings.mIndexedTriangles.resize(idx + size(indices));
  330. for (uint i = 0; i < size(indices); ++i)
  331. settings.mIndexedTriangles[idx + i] = IndexedTriangle(vtx + indices[i][0], vtx + indices[i][flip? 2 : 1], vtx + indices[i][flip? 1 : 2]);
  332. }
  333. // Create body with random orientation
  334. PhysicsTestContext context;
  335. Body &mesh_body = context.CreateBody(&settings, RVec3(Vec3::sRandom(random)), Quat::sRandom(random), EMotionType::Static, EMotionQuality::Discrete, Layers::NON_MOVING, EActivation::DontActivate);
  336. // Get the shape
  337. ShapeRefC mesh_shape = mesh_body.GetShape();
  338. // Get narrow phase
  339. const NarrowPhaseQuery &narrow_phase = context.GetSystem()->GetNarrowPhaseQuery();
  340. // Get transform
  341. RMat44 body_transform = mesh_body.GetWorldTransform();
  342. CHECK(body_transform != RMat44::sIdentity());
  343. // Test points
  344. for (int x = -grid_size; x <= grid_size; ++x)
  345. for (int y = -grid_size; y <= grid_size; ++y)
  346. for (int z = -grid_size; z <= grid_size; ++z)
  347. {
  348. Vec3 center((float)x, (float)y, (float)z);
  349. // The center point should hit
  350. sTestHit(mesh_shape, center);
  351. sTestHit(narrow_phase, body_transform * center, mesh_body.GetID());
  352. // Points outside the hull should not hit
  353. for (Vec3 probe : cube_probes)
  354. {
  355. Vec3 point = center + 0.4f * probe;
  356. sTestMiss(mesh_shape, point);
  357. sTestMiss(narrow_phase, body_transform * point);
  358. }
  359. }
  360. }
  361. }