test_shapes.cpp 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789
  1. // Copyright (c) Amer Koleci and Contributors.
  2. // Licensed under the MIT License (MIT). See LICENSE in the repository root for more information.
  3. #include <gtest/gtest.h>
  4. #include <cmath>
  5. #include "joltc.h"
  6. class ShapesTest : public ::testing::Test {
  7. protected:
  8. void SetUp() override {
  9. ASSERT_TRUE(JPH_Init());
  10. }
  11. void TearDown() override {
  12. JPH_Shutdown();
  13. }
  14. };
  15. // ============================================================================
  16. // SphereShape Tests
  17. // ============================================================================
  18. TEST_F(ShapesTest, SphereShape_Create) {
  19. JPH_SphereShape* shape = JPH_SphereShape_Create(1.0f);
  20. ASSERT_NE(shape, nullptr);
  21. float radius = JPH_SphereShape_GetRadius(shape);
  22. EXPECT_FLOAT_EQ(radius, 1.0f);
  23. JPH_Shape_Destroy((JPH_Shape*)shape);
  24. }
  25. TEST_F(ShapesTest, SphereShape_CreateWithSettings) {
  26. JPH_SphereShapeSettings* settings = JPH_SphereShapeSettings_Create(2.0f);
  27. ASSERT_NE(settings, nullptr);
  28. float radius = JPH_SphereShapeSettings_GetRadius(settings);
  29. EXPECT_FLOAT_EQ(radius, 2.0f);
  30. JPH_SphereShape* shape = JPH_SphereShapeSettings_CreateShape(settings);
  31. ASSERT_NE(shape, nullptr);
  32. radius = JPH_SphereShape_GetRadius(shape);
  33. EXPECT_FLOAT_EQ(radius, 2.0f);
  34. JPH_Shape_Destroy((JPH_Shape*)shape);
  35. JPH_ShapeSettings_Destroy((JPH_ShapeSettings*)settings);
  36. }
  37. TEST_F(ShapesTest, SphereShapeSettings_SetRadius) {
  38. JPH_SphereShapeSettings* settings = JPH_SphereShapeSettings_Create(1.0f);
  39. ASSERT_NE(settings, nullptr);
  40. JPH_SphereShapeSettings_SetRadius(settings, 3.0f);
  41. float radius = JPH_SphereShapeSettings_GetRadius(settings);
  42. EXPECT_FLOAT_EQ(radius, 3.0f);
  43. JPH_ShapeSettings_Destroy((JPH_ShapeSettings*)settings);
  44. }
  45. TEST_F(ShapesTest, SphereShape_GetType) {
  46. JPH_SphereShape* shape = JPH_SphereShape_Create(1.0f);
  47. ASSERT_NE(shape, nullptr);
  48. JPH_ShapeType type = JPH_Shape_GetType((const JPH_Shape*)shape);
  49. EXPECT_EQ(type, JPH_ShapeType_Convex);
  50. JPH_ShapeSubType subType = JPH_Shape_GetSubType((const JPH_Shape*)shape);
  51. EXPECT_EQ(subType, JPH_ShapeSubType_Sphere);
  52. JPH_Shape_Destroy((JPH_Shape*)shape);
  53. }
  54. TEST_F(ShapesTest, SphereShape_GetVolume) {
  55. JPH_SphereShape* shape = JPH_SphereShape_Create(1.0f);
  56. ASSERT_NE(shape, nullptr);
  57. float volume = JPH_Shape_GetVolume((const JPH_Shape*)shape);
  58. // Volume of sphere = (4/3) * PI * r^3
  59. float expectedVolume = (4.0f / 3.0f) * JPH_M_PI * 1.0f * 1.0f * 1.0f;
  60. EXPECT_NEAR(volume, expectedVolume, 0.001f);
  61. JPH_Shape_Destroy((JPH_Shape*)shape);
  62. }
  63. // ============================================================================
  64. // BoxShape Tests
  65. // ============================================================================
  66. TEST_F(ShapesTest, BoxShape_Create) {
  67. JPH_Vec3 halfExtent = {1.0f, 2.0f, 3.0f};
  68. JPH_BoxShape* shape = JPH_BoxShape_Create(&halfExtent, JPH_DEFAULT_CONVEX_RADIUS);
  69. ASSERT_NE(shape, nullptr);
  70. JPH_Vec3 result;
  71. JPH_BoxShape_GetHalfExtent(shape, &result);
  72. EXPECT_FLOAT_EQ(result.x, 1.0f);
  73. EXPECT_FLOAT_EQ(result.y, 2.0f);
  74. EXPECT_FLOAT_EQ(result.z, 3.0f);
  75. JPH_Shape_Destroy((JPH_Shape*)shape);
  76. }
  77. TEST_F(ShapesTest, BoxShape_CreateWithSettings) {
  78. JPH_Vec3 halfExtent = {2.0f, 2.0f, 2.0f};
  79. JPH_BoxShapeSettings* settings = JPH_BoxShapeSettings_Create(&halfExtent, JPH_DEFAULT_CONVEX_RADIUS);
  80. ASSERT_NE(settings, nullptr);
  81. JPH_BoxShape* shape = JPH_BoxShapeSettings_CreateShape(settings);
  82. ASSERT_NE(shape, nullptr);
  83. JPH_Vec3 result;
  84. JPH_BoxShape_GetHalfExtent(shape, &result);
  85. EXPECT_FLOAT_EQ(result.x, 2.0f);
  86. EXPECT_FLOAT_EQ(result.y, 2.0f);
  87. EXPECT_FLOAT_EQ(result.z, 2.0f);
  88. JPH_Shape_Destroy((JPH_Shape*)shape);
  89. JPH_ShapeSettings_Destroy((JPH_ShapeSettings*)settings);
  90. }
  91. TEST_F(ShapesTest, BoxShape_GetConvexRadius) {
  92. JPH_Vec3 halfExtent = {1.0f, 1.0f, 1.0f};
  93. JPH_BoxShape* shape = JPH_BoxShape_Create(&halfExtent, 0.1f);
  94. ASSERT_NE(shape, nullptr);
  95. float convexRadius = JPH_BoxShape_GetConvexRadius(shape);
  96. EXPECT_FLOAT_EQ(convexRadius, 0.1f);
  97. JPH_Shape_Destroy((JPH_Shape*)shape);
  98. }
  99. TEST_F(ShapesTest, BoxShape_GetType) {
  100. JPH_Vec3 halfExtent = {1.0f, 1.0f, 1.0f};
  101. JPH_BoxShape* shape = JPH_BoxShape_Create(&halfExtent, JPH_DEFAULT_CONVEX_RADIUS);
  102. ASSERT_NE(shape, nullptr);
  103. JPH_ShapeType type = JPH_Shape_GetType((const JPH_Shape*)shape);
  104. EXPECT_EQ(type, JPH_ShapeType_Convex);
  105. JPH_ShapeSubType subType = JPH_Shape_GetSubType((const JPH_Shape*)shape);
  106. EXPECT_EQ(subType, JPH_ShapeSubType_Box);
  107. JPH_Shape_Destroy((JPH_Shape*)shape);
  108. }
  109. // ============================================================================
  110. // CapsuleShape Tests
  111. // ============================================================================
  112. TEST_F(ShapesTest, CapsuleShape_Create) {
  113. JPH_CapsuleShape* shape = JPH_CapsuleShape_Create(1.0f, 0.5f);
  114. ASSERT_NE(shape, nullptr);
  115. float halfHeight = JPH_CapsuleShape_GetHalfHeightOfCylinder(shape);
  116. float radius = JPH_CapsuleShape_GetRadius(shape);
  117. EXPECT_FLOAT_EQ(halfHeight, 1.0f);
  118. EXPECT_FLOAT_EQ(radius, 0.5f);
  119. JPH_Shape_Destroy((JPH_Shape*)shape);
  120. }
  121. TEST_F(ShapesTest, CapsuleShape_CreateWithSettings) {
  122. JPH_CapsuleShapeSettings* settings = JPH_CapsuleShapeSettings_Create(2.0f, 1.0f);
  123. ASSERT_NE(settings, nullptr);
  124. JPH_CapsuleShape* shape = JPH_CapsuleShapeSettings_CreateShape(settings);
  125. ASSERT_NE(shape, nullptr);
  126. float halfHeight = JPH_CapsuleShape_GetHalfHeightOfCylinder(shape);
  127. float radius = JPH_CapsuleShape_GetRadius(shape);
  128. EXPECT_FLOAT_EQ(halfHeight, 2.0f);
  129. EXPECT_FLOAT_EQ(radius, 1.0f);
  130. JPH_Shape_Destroy((JPH_Shape*)shape);
  131. JPH_ShapeSettings_Destroy((JPH_ShapeSettings*)settings);
  132. }
  133. TEST_F(ShapesTest, CapsuleShape_GetType) {
  134. JPH_CapsuleShape* shape = JPH_CapsuleShape_Create(1.0f, 0.5f);
  135. ASSERT_NE(shape, nullptr);
  136. JPH_ShapeSubType subType = JPH_Shape_GetSubType((const JPH_Shape*)shape);
  137. EXPECT_EQ(subType, JPH_ShapeSubType_Capsule);
  138. JPH_Shape_Destroy((JPH_Shape*)shape);
  139. }
  140. // ============================================================================
  141. // CylinderShape Tests
  142. // ============================================================================
  143. TEST_F(ShapesTest, CylinderShape_Create) {
  144. JPH_CylinderShape* shape = JPH_CylinderShape_Create(1.0f, 0.5f);
  145. ASSERT_NE(shape, nullptr);
  146. float halfHeight = JPH_CylinderShape_GetHalfHeight(shape);
  147. float radius = JPH_CylinderShape_GetRadius(shape);
  148. EXPECT_FLOAT_EQ(halfHeight, 1.0f);
  149. EXPECT_FLOAT_EQ(radius, 0.5f);
  150. JPH_Shape_Destroy((JPH_Shape*)shape);
  151. }
  152. TEST_F(ShapesTest, CylinderShape_CreateWithSettings) {
  153. JPH_CylinderShapeSettings* settings = JPH_CylinderShapeSettings_Create(2.0f, 1.0f, JPH_DEFAULT_CONVEX_RADIUS);
  154. ASSERT_NE(settings, nullptr);
  155. JPH_CylinderShape* shape = JPH_CylinderShapeSettings_CreateShape(settings);
  156. ASSERT_NE(shape, nullptr);
  157. float halfHeight = JPH_CylinderShape_GetHalfHeight(shape);
  158. float radius = JPH_CylinderShape_GetRadius(shape);
  159. EXPECT_FLOAT_EQ(halfHeight, 2.0f);
  160. EXPECT_FLOAT_EQ(radius, 1.0f);
  161. JPH_Shape_Destroy((JPH_Shape*)shape);
  162. JPH_ShapeSettings_Destroy((JPH_ShapeSettings*)settings);
  163. }
  164. TEST_F(ShapesTest, CylinderShape_GetType) {
  165. JPH_CylinderShape* shape = JPH_CylinderShape_Create(1.0f, 0.5f);
  166. ASSERT_NE(shape, nullptr);
  167. JPH_ShapeSubType subType = JPH_Shape_GetSubType((const JPH_Shape*)shape);
  168. EXPECT_EQ(subType, JPH_ShapeSubType_Cylinder);
  169. JPH_Shape_Destroy((JPH_Shape*)shape);
  170. }
  171. // ============================================================================
  172. // TriangleShape Tests
  173. // ============================================================================
  174. TEST_F(ShapesTest, TriangleShape_Create) {
  175. JPH_Vec3 v1 = {0.0f, 0.0f, 0.0f};
  176. JPH_Vec3 v2 = {1.0f, 0.0f, 0.0f};
  177. JPH_Vec3 v3 = {0.0f, 1.0f, 0.0f};
  178. JPH_TriangleShape* shape = JPH_TriangleShape_Create(&v1, &v2, &v3, 0.0f);
  179. ASSERT_NE(shape, nullptr);
  180. JPH_Vec3 result;
  181. JPH_TriangleShape_GetVertex1(shape, &result);
  182. EXPECT_FLOAT_EQ(result.x, 0.0f);
  183. EXPECT_FLOAT_EQ(result.y, 0.0f);
  184. EXPECT_FLOAT_EQ(result.z, 0.0f);
  185. JPH_TriangleShape_GetVertex2(shape, &result);
  186. EXPECT_FLOAT_EQ(result.x, 1.0f);
  187. EXPECT_FLOAT_EQ(result.y, 0.0f);
  188. EXPECT_FLOAT_EQ(result.z, 0.0f);
  189. JPH_TriangleShape_GetVertex3(shape, &result);
  190. EXPECT_FLOAT_EQ(result.x, 0.0f);
  191. EXPECT_FLOAT_EQ(result.y, 1.0f);
  192. EXPECT_FLOAT_EQ(result.z, 0.0f);
  193. JPH_Shape_Destroy((JPH_Shape*)shape);
  194. }
  195. TEST_F(ShapesTest, TriangleShape_GetType) {
  196. JPH_Vec3 v1 = {0.0f, 0.0f, 0.0f};
  197. JPH_Vec3 v2 = {1.0f, 0.0f, 0.0f};
  198. JPH_Vec3 v3 = {0.0f, 1.0f, 0.0f};
  199. JPH_TriangleShape* shape = JPH_TriangleShape_Create(&v1, &v2, &v3, 0.0f);
  200. ASSERT_NE(shape, nullptr);
  201. JPH_ShapeSubType subType = JPH_Shape_GetSubType((const JPH_Shape*)shape);
  202. EXPECT_EQ(subType, JPH_ShapeSubType_Triangle);
  203. JPH_Shape_Destroy((JPH_Shape*)shape);
  204. }
  205. // ============================================================================
  206. // TaperedCapsuleShape Tests
  207. // ============================================================================
  208. TEST_F(ShapesTest, TaperedCapsuleShape_Create) {
  209. JPH_TaperedCapsuleShapeSettings* settings = JPH_TaperedCapsuleShapeSettings_Create(1.0f, 0.5f, 0.3f);
  210. ASSERT_NE(settings, nullptr);
  211. JPH_TaperedCapsuleShape* shape = JPH_TaperedCapsuleShapeSettings_CreateShape(settings);
  212. ASSERT_NE(shape, nullptr);
  213. float topRadius = JPH_TaperedCapsuleShape_GetTopRadius(shape);
  214. float bottomRadius = JPH_TaperedCapsuleShape_GetBottomRadius(shape);
  215. float halfHeight = JPH_TaperedCapsuleShape_GetHalfHeight(shape);
  216. EXPECT_FLOAT_EQ(topRadius, 0.5f);
  217. EXPECT_FLOAT_EQ(bottomRadius, 0.3f);
  218. EXPECT_FLOAT_EQ(halfHeight, 1.0f);
  219. JPH_Shape_Destroy((JPH_Shape*)shape);
  220. JPH_ShapeSettings_Destroy((JPH_ShapeSettings*)settings);
  221. }
  222. TEST_F(ShapesTest, TaperedCapsuleShape_GetType) {
  223. JPH_TaperedCapsuleShapeSettings* settings = JPH_TaperedCapsuleShapeSettings_Create(1.0f, 0.5f, 0.3f);
  224. ASSERT_NE(settings, nullptr);
  225. JPH_TaperedCapsuleShape* shape = JPH_TaperedCapsuleShapeSettings_CreateShape(settings);
  226. ASSERT_NE(shape, nullptr);
  227. JPH_ShapeSubType subType = JPH_Shape_GetSubType((const JPH_Shape*)shape);
  228. EXPECT_EQ(subType, JPH_ShapeSubType_TaperedCapsule);
  229. JPH_Shape_Destroy((JPH_Shape*)shape);
  230. JPH_ShapeSettings_Destroy((JPH_ShapeSettings*)settings);
  231. }
  232. // ============================================================================
  233. // ConvexHullShape Tests
  234. // ============================================================================
  235. TEST_F(ShapesTest, ConvexHullShape_Create) {
  236. // Create a simple tetrahedron
  237. JPH_Vec3 points[] = {
  238. {0.0f, 0.0f, 0.0f},
  239. {1.0f, 0.0f, 0.0f},
  240. {0.5f, 1.0f, 0.0f},
  241. {0.5f, 0.5f, 1.0f}
  242. };
  243. JPH_ConvexHullShapeSettings* settings = JPH_ConvexHullShapeSettings_Create(points, 4, JPH_DEFAULT_CONVEX_RADIUS);
  244. ASSERT_NE(settings, nullptr);
  245. JPH_ConvexHullShape* shape = JPH_ConvexHullShapeSettings_CreateShape(settings);
  246. ASSERT_NE(shape, nullptr);
  247. uint32_t numPoints = JPH_ConvexHullShape_GetNumPoints(shape);
  248. EXPECT_EQ(numPoints, 4u);
  249. uint32_t numFaces = JPH_ConvexHullShape_GetNumFaces(shape);
  250. EXPECT_EQ(numFaces, 4u); // Tetrahedron has 4 faces
  251. JPH_Shape_Destroy((JPH_Shape*)shape);
  252. JPH_ShapeSettings_Destroy((JPH_ShapeSettings*)settings);
  253. }
  254. TEST_F(ShapesTest, ConvexHullShape_GetPoint) {
  255. JPH_Vec3 points[] = {
  256. {0.0f, 0.0f, 0.0f},
  257. {1.0f, 0.0f, 0.0f},
  258. {0.5f, 1.0f, 0.0f},
  259. {0.5f, 0.5f, 1.0f}
  260. };
  261. JPH_ConvexHullShapeSettings* settings = JPH_ConvexHullShapeSettings_Create(points, 4, JPH_DEFAULT_CONVEX_RADIUS);
  262. JPH_ConvexHullShape* shape = JPH_ConvexHullShapeSettings_CreateShape(settings);
  263. ASSERT_NE(shape, nullptr);
  264. JPH_Vec3 result;
  265. JPH_ConvexHullShape_GetPoint(shape, 0, &result);
  266. // Points may be reordered, so just check they're valid
  267. EXPECT_FALSE(std::isnan(result.x));
  268. EXPECT_FALSE(std::isnan(result.y));
  269. EXPECT_FALSE(std::isnan(result.z));
  270. JPH_Shape_Destroy((JPH_Shape*)shape);
  271. JPH_ShapeSettings_Destroy((JPH_ShapeSettings*)settings);
  272. }
  273. // ============================================================================
  274. // PlaneShape Tests
  275. // ============================================================================
  276. TEST_F(ShapesTest, PlaneShape_Create) {
  277. JPH_Plane plane = {{0.0f, 1.0f, 0.0f}, 0.0f}; // XZ plane at origin
  278. JPH_PlaneShape* shape = JPH_PlaneShape_Create(&plane, nullptr, 1000.0f);
  279. ASSERT_NE(shape, nullptr);
  280. JPH_Plane result;
  281. JPH_PlaneShape_GetPlane(shape, &result);
  282. EXPECT_FLOAT_EQ(result.normal.x, 0.0f);
  283. EXPECT_FLOAT_EQ(result.normal.y, 1.0f);
  284. EXPECT_FLOAT_EQ(result.normal.z, 0.0f);
  285. EXPECT_FLOAT_EQ(result.distance, 0.0f);
  286. float halfExtent = JPH_PlaneShape_GetHalfExtent(shape);
  287. EXPECT_FLOAT_EQ(halfExtent, 1000.0f);
  288. JPH_Shape_Destroy((JPH_Shape*)shape);
  289. }
  290. // ============================================================================
  291. // Shape Common API Tests
  292. // ============================================================================
  293. TEST_F(ShapesTest, Shape_UserData) {
  294. JPH_SphereShape* shape = JPH_SphereShape_Create(1.0f);
  295. ASSERT_NE(shape, nullptr);
  296. JPH_Shape_SetUserData((JPH_Shape*)shape, 12345);
  297. uint64_t userData = JPH_Shape_GetUserData((const JPH_Shape*)shape);
  298. EXPECT_EQ(userData, 12345u);
  299. JPH_Shape_Destroy((JPH_Shape*)shape);
  300. }
  301. TEST_F(ShapesTest, Shape_GetLocalBounds) {
  302. JPH_SphereShape* shape = JPH_SphereShape_Create(1.0f);
  303. ASSERT_NE(shape, nullptr);
  304. JPH_AABox bounds;
  305. JPH_Shape_GetLocalBounds((const JPH_Shape*)shape, &bounds);
  306. // Sphere of radius 1 should have bounds from (-1,-1,-1) to (1,1,1)
  307. EXPECT_FLOAT_EQ(bounds.min.x, -1.0f);
  308. EXPECT_FLOAT_EQ(bounds.min.y, -1.0f);
  309. EXPECT_FLOAT_EQ(bounds.min.z, -1.0f);
  310. EXPECT_FLOAT_EQ(bounds.max.x, 1.0f);
  311. EXPECT_FLOAT_EQ(bounds.max.y, 1.0f);
  312. EXPECT_FLOAT_EQ(bounds.max.z, 1.0f);
  313. JPH_Shape_Destroy((JPH_Shape*)shape);
  314. }
  315. TEST_F(ShapesTest, Shape_GetCenterOfMass) {
  316. JPH_SphereShape* shape = JPH_SphereShape_Create(1.0f);
  317. ASSERT_NE(shape, nullptr);
  318. JPH_Vec3 com;
  319. JPH_Shape_GetCenterOfMass((const JPH_Shape*)shape, &com);
  320. // Center of mass for sphere is at origin
  321. EXPECT_FLOAT_EQ(com.x, 0.0f);
  322. EXPECT_FLOAT_EQ(com.y, 0.0f);
  323. EXPECT_FLOAT_EQ(com.z, 0.0f);
  324. JPH_Shape_Destroy((JPH_Shape*)shape);
  325. }
  326. TEST_F(ShapesTest, Shape_GetInnerRadius) {
  327. JPH_SphereShape* shape = JPH_SphereShape_Create(1.0f);
  328. ASSERT_NE(shape, nullptr);
  329. float innerRadius = JPH_Shape_GetInnerRadius((const JPH_Shape*)shape);
  330. EXPECT_FLOAT_EQ(innerRadius, 1.0f);
  331. JPH_Shape_Destroy((JPH_Shape*)shape);
  332. }
  333. TEST_F(ShapesTest, Shape_GetMassProperties) {
  334. JPH_SphereShape* shape = JPH_SphereShape_Create(1.0f);
  335. ASSERT_NE(shape, nullptr);
  336. JPH_MassProperties massProps;
  337. JPH_Shape_GetMassProperties((const JPH_Shape*)shape, &massProps);
  338. // Mass should be positive (depends on density)
  339. EXPECT_GT(massProps.mass, 0.0f);
  340. JPH_Shape_Destroy((JPH_Shape*)shape);
  341. }
  342. TEST_F(ShapesTest, Shape_MustBeStatic) {
  343. // Regular sphere doesn't need to be static
  344. JPH_SphereShape* sphere = JPH_SphereShape_Create(1.0f);
  345. EXPECT_FALSE(JPH_Shape_MustBeStatic((const JPH_Shape*)sphere));
  346. JPH_Shape_Destroy((JPH_Shape*)sphere);
  347. // Plane must be static
  348. JPH_Plane plane = {{0.0f, 1.0f, 0.0f}, 0.0f};
  349. JPH_PlaneShape* planeShape = JPH_PlaneShape_Create(&plane, nullptr, 1000.0f);
  350. EXPECT_TRUE(JPH_Shape_MustBeStatic((const JPH_Shape*)planeShape));
  351. JPH_Shape_Destroy((JPH_Shape*)planeShape);
  352. }
  353. // ============================================================================
  354. // Convex Shape Density Tests
  355. // ============================================================================
  356. TEST_F(ShapesTest, ConvexShape_Density) {
  357. JPH_SphereShapeSettings* settings = JPH_SphereShapeSettings_Create(1.0f);
  358. ASSERT_NE(settings, nullptr);
  359. // Get default density
  360. float defaultDensity = JPH_ConvexShapeSettings_GetDensity((const JPH_ConvexShapeSettings*)settings);
  361. EXPECT_GT(defaultDensity, 0.0f);
  362. // Set new density
  363. JPH_ConvexShapeSettings_SetDensity((JPH_ConvexShapeSettings*)settings, 2000.0f);
  364. float newDensity = JPH_ConvexShapeSettings_GetDensity((const JPH_ConvexShapeSettings*)settings);
  365. EXPECT_FLOAT_EQ(newDensity, 2000.0f);
  366. JPH_ShapeSettings_Destroy((JPH_ShapeSettings*)settings);
  367. }
  368. TEST_F(ShapesTest, ConvexShape_SetDensity) {
  369. JPH_SphereShape* shape = JPH_SphereShape_Create(1.0f);
  370. ASSERT_NE(shape, nullptr);
  371. // Get default density
  372. float defaultDensity = JPH_ConvexShape_GetDensity((const JPH_ConvexShape*)shape);
  373. EXPECT_GT(defaultDensity, 0.0f);
  374. // Set new density
  375. JPH_ConvexShape_SetDensity((JPH_ConvexShape*)shape, 5000.0f);
  376. float newDensity = JPH_ConvexShape_GetDensity((const JPH_ConvexShape*)shape);
  377. EXPECT_FLOAT_EQ(newDensity, 5000.0f);
  378. JPH_Shape_Destroy((JPH_Shape*)shape);
  379. }
  380. // ============================================================================
  381. // ShapeSettings UserData Tests
  382. // ============================================================================
  383. TEST_F(ShapesTest, ShapeSettings_UserData) {
  384. JPH_SphereShapeSettings* settings = JPH_SphereShapeSettings_Create(1.0f);
  385. ASSERT_NE(settings, nullptr);
  386. JPH_ShapeSettings_SetUserData((JPH_ShapeSettings*)settings, 99999);
  387. uint64_t userData = JPH_ShapeSettings_GetUserData((const JPH_ShapeSettings*)settings);
  388. EXPECT_EQ(userData, 99999u);
  389. JPH_ShapeSettings_Destroy((JPH_ShapeSettings*)settings);
  390. }
  391. // ============================================================================
  392. // Decorated Shape Tests
  393. // ============================================================================
  394. TEST_F(ShapesTest, RotatedTranslatedShape_Create) {
  395. JPH_SphereShape* innerShape = JPH_SphereShape_Create(1.0f);
  396. ASSERT_NE(innerShape, nullptr);
  397. JPH_Vec3 position = {1.0f, 2.0f, 3.0f};
  398. JPH_Quat rotation = {0.0f, 0.0f, 0.0f, 1.0f}; // Identity
  399. JPH_RotatedTranslatedShape* shape = JPH_RotatedTranslatedShape_Create(&position, &rotation, (const JPH_Shape*)innerShape);
  400. ASSERT_NE(shape, nullptr);
  401. JPH_Vec3 resultPos;
  402. JPH_RotatedTranslatedShape_GetPosition(shape, &resultPos);
  403. EXPECT_FLOAT_EQ(resultPos.x, 1.0f);
  404. EXPECT_FLOAT_EQ(resultPos.y, 2.0f);
  405. EXPECT_FLOAT_EQ(resultPos.z, 3.0f);
  406. JPH_Quat resultRot;
  407. JPH_RotatedTranslatedShape_GetRotation(shape, &resultRot);
  408. EXPECT_FLOAT_EQ(resultRot.w, 1.0f);
  409. // Get inner shape
  410. const JPH_Shape* inner = JPH_DecoratedShape_GetInnerShape((const JPH_DecoratedShape*)shape);
  411. EXPECT_EQ(inner, (const JPH_Shape*)innerShape);
  412. JPH_Shape_Destroy((JPH_Shape*)shape);
  413. // Note: innerShape is owned by the decorated shape and will be destroyed with it
  414. }
  415. TEST_F(ShapesTest, ScaledShape_Create) {
  416. JPH_SphereShape* innerShape = JPH_SphereShape_Create(1.0f);
  417. ASSERT_NE(innerShape, nullptr);
  418. JPH_Vec3 scale = {2.0f, 2.0f, 2.0f};
  419. JPH_ScaledShape* shape = JPH_ScaledShape_Create((const JPH_Shape*)innerShape, &scale);
  420. ASSERT_NE(shape, nullptr);
  421. JPH_Vec3 resultScale;
  422. JPH_ScaledShape_GetScale(shape, &resultScale);
  423. EXPECT_FLOAT_EQ(resultScale.x, 2.0f);
  424. EXPECT_FLOAT_EQ(resultScale.y, 2.0f);
  425. EXPECT_FLOAT_EQ(resultScale.z, 2.0f);
  426. JPH_Shape_Destroy((JPH_Shape*)shape);
  427. }
  428. TEST_F(ShapesTest, OffsetCenterOfMassShape_Create) {
  429. JPH_SphereShape* innerShape = JPH_SphereShape_Create(1.0f);
  430. ASSERT_NE(innerShape, nullptr);
  431. JPH_Vec3 offset = {0.0f, 1.0f, 0.0f};
  432. JPH_OffsetCenterOfMassShape* shape = JPH_OffsetCenterOfMassShape_Create(&offset, (const JPH_Shape*)innerShape);
  433. ASSERT_NE(shape, nullptr);
  434. JPH_Vec3 resultOffset;
  435. JPH_OffsetCenterOfMassShape_GetOffset(shape, &resultOffset);
  436. EXPECT_FLOAT_EQ(resultOffset.x, 0.0f);
  437. EXPECT_FLOAT_EQ(resultOffset.y, 1.0f);
  438. EXPECT_FLOAT_EQ(resultOffset.z, 0.0f);
  439. JPH_Shape_Destroy((JPH_Shape*)shape);
  440. }
  441. // ============================================================================
  442. // Compound Shape Tests
  443. // ============================================================================
  444. TEST_F(ShapesTest, StaticCompoundShape_Create) {
  445. JPH_StaticCompoundShapeSettings* settings = JPH_StaticCompoundShapeSettings_Create();
  446. ASSERT_NE(settings, nullptr);
  447. // Create two sphere shapes
  448. JPH_SphereShape* sphere1 = JPH_SphereShape_Create(1.0f);
  449. JPH_SphereShape* sphere2 = JPH_SphereShape_Create(0.5f);
  450. JPH_Vec3 pos1 = {-1.0f, 0.0f, 0.0f};
  451. JPH_Vec3 pos2 = {1.0f, 0.0f, 0.0f};
  452. JPH_Quat rot = {0.0f, 0.0f, 0.0f, 1.0f};
  453. JPH_CompoundShapeSettings_AddShape2((JPH_CompoundShapeSettings*)settings, &pos1, &rot, (const JPH_Shape*)sphere1, 0);
  454. JPH_CompoundShapeSettings_AddShape2((JPH_CompoundShapeSettings*)settings, &pos2, &rot, (const JPH_Shape*)sphere2, 0);
  455. JPH_StaticCompoundShape* shape = JPH_StaticCompoundShape_Create(settings);
  456. ASSERT_NE(shape, nullptr);
  457. uint32_t numSubShapes = JPH_CompoundShape_GetNumSubShapes((const JPH_CompoundShape*)shape);
  458. EXPECT_EQ(numSubShapes, 2u);
  459. JPH_Shape_Destroy((JPH_Shape*)shape);
  460. JPH_ShapeSettings_Destroy((JPH_ShapeSettings*)settings);
  461. }
  462. TEST_F(ShapesTest, MutableCompoundShape_Create) {
  463. JPH_MutableCompoundShapeSettings* settings = JPH_MutableCompoundShapeSettings_Create();
  464. ASSERT_NE(settings, nullptr);
  465. JPH_MutableCompoundShape* shape = JPH_MutableCompoundShape_Create(settings);
  466. ASSERT_NE(shape, nullptr);
  467. // Add a sphere
  468. JPH_SphereShape* sphere = JPH_SphereShape_Create(1.0f);
  469. JPH_Vec3 pos = {0.0f, 0.0f, 0.0f};
  470. JPH_Quat rot = {0.0f, 0.0f, 0.0f, 1.0f};
  471. uint32_t index = JPH_MutableCompoundShape_AddShape(shape, &pos, &rot, (const JPH_Shape*)sphere, 0, UINT32_MAX);
  472. EXPECT_EQ(index, 0u);
  473. uint32_t numSubShapes = JPH_CompoundShape_GetNumSubShapes((const JPH_CompoundShape*)shape);
  474. EXPECT_EQ(numSubShapes, 1u);
  475. // Remove the shape
  476. JPH_MutableCompoundShape_RemoveShape(shape, 0);
  477. numSubShapes = JPH_CompoundShape_GetNumSubShapes((const JPH_CompoundShape*)shape);
  478. EXPECT_EQ(numSubShapes, 0u);
  479. JPH_Shape_Destroy((JPH_Shape*)shape);
  480. JPH_ShapeSettings_Destroy((JPH_ShapeSettings*)settings);
  481. }
  482. // ============================================================================
  483. // EmptyShape Tests
  484. // ============================================================================
  485. TEST_F(ShapesTest, EmptyShape_Create) {
  486. JPH_Vec3 centerOfMass = {0.0f, 0.0f, 0.0f};
  487. JPH_EmptyShapeSettings* settings = JPH_EmptyShapeSettings_Create(&centerOfMass);
  488. ASSERT_NE(settings, nullptr);
  489. JPH_EmptyShape* shape = JPH_EmptyShapeSettings_CreateShape(settings);
  490. ASSERT_NE(shape, nullptr);
  491. // Empty shape should have zero volume
  492. float volume = JPH_Shape_GetVolume((const JPH_Shape*)shape);
  493. EXPECT_FLOAT_EQ(volume, 0.0f);
  494. JPH_Shape_Destroy((JPH_Shape*)shape);
  495. JPH_ShapeSettings_Destroy((JPH_ShapeSettings*)settings);
  496. }
  497. // ============================================================================
  498. // Shape RayCast Tests
  499. // ============================================================================
  500. TEST_F(ShapesTest, Shape_CastRay) {
  501. JPH_SphereShape* shape = JPH_SphereShape_Create(1.0f);
  502. ASSERT_NE(shape, nullptr);
  503. // Cast ray from outside the sphere toward its center
  504. JPH_Vec3 origin = {-5.0f, 0.0f, 0.0f};
  505. JPH_Vec3 direction = {10.0f, 0.0f, 0.0f}; // Direction, not normalized
  506. JPH_RayCastResult hit;
  507. bool hasHit = JPH_Shape_CastRay((const JPH_Shape*)shape, &origin, &direction, &hit);
  508. EXPECT_TRUE(hasHit);
  509. // The hit should be at fraction ~0.4 (when ray hits sphere at x=-1)
  510. // fraction = (5-1)/10 = 0.4
  511. EXPECT_NEAR(hit.fraction, 0.4f, 0.01f);
  512. JPH_Shape_Destroy((JPH_Shape*)shape);
  513. }
  514. TEST_F(ShapesTest, Shape_CastRay_Miss) {
  515. JPH_SphereShape* shape = JPH_SphereShape_Create(1.0f);
  516. ASSERT_NE(shape, nullptr);
  517. // Cast ray that misses the sphere
  518. JPH_Vec3 origin = {0.0f, 5.0f, 0.0f};
  519. JPH_Vec3 direction = {1.0f, 0.0f, 0.0f}; // Parallel to X axis, above sphere
  520. JPH_RayCastResult hit;
  521. bool hasHit = JPH_Shape_CastRay((const JPH_Shape*)shape, &origin, &direction, &hit);
  522. EXPECT_FALSE(hasHit);
  523. JPH_Shape_Destroy((JPH_Shape*)shape);
  524. }
  525. // ============================================================================
  526. // Shape CollidePoint Tests
  527. // ============================================================================
  528. TEST_F(ShapesTest, Shape_CollidePoint_Inside) {
  529. JPH_SphereShape* shape = JPH_SphereShape_Create(1.0f);
  530. ASSERT_NE(shape, nullptr);
  531. // Point inside sphere
  532. JPH_Vec3 point = {0.0f, 0.0f, 0.0f};
  533. bool isInside = JPH_Shape_CollidePoint((const JPH_Shape*)shape, &point, nullptr);
  534. EXPECT_TRUE(isInside);
  535. JPH_Shape_Destroy((JPH_Shape*)shape);
  536. }
  537. TEST_F(ShapesTest, Shape_CollidePoint_Outside) {
  538. JPH_SphereShape* shape = JPH_SphereShape_Create(1.0f);
  539. ASSERT_NE(shape, nullptr);
  540. // Point outside sphere
  541. JPH_Vec3 point = {5.0f, 0.0f, 0.0f};
  542. bool isInside = JPH_Shape_CollidePoint((const JPH_Shape*)shape, &point, nullptr);
  543. EXPECT_FALSE(isInside);
  544. JPH_Shape_Destroy((JPH_Shape*)shape);
  545. }
  546. // ============================================================================
  547. // TaperedCylinderShape Tests
  548. // ============================================================================
  549. TEST_F(ShapesTest, TaperedCylinderShape_Create) {
  550. JPH_TaperedCylinderShapeSettings* settings = JPH_TaperedCylinderShapeSettings_Create(
  551. 1.0f, // halfHeight
  552. 0.5f, // topRadius
  553. 1.0f, // bottomRadius
  554. JPH_DEFAULT_CONVEX_RADIUS,
  555. nullptr
  556. );
  557. ASSERT_NE(settings, nullptr);
  558. JPH_TaperedCylinderShape* shape = JPH_TaperedCylinderShapeSettings_CreateShape(settings);
  559. ASSERT_NE(shape, nullptr);
  560. float topRadius = JPH_TaperedCylinderShape_GetTopRadius(shape);
  561. float bottomRadius = JPH_TaperedCylinderShape_GetBottomRadius(shape);
  562. float halfHeight = JPH_TaperedCylinderShape_GetHalfHeight(shape);
  563. EXPECT_FLOAT_EQ(topRadius, 0.5f);
  564. EXPECT_FLOAT_EQ(bottomRadius, 1.0f);
  565. EXPECT_FLOAT_EQ(halfHeight, 1.0f);
  566. JPH_Shape_Destroy((JPH_Shape*)shape);
  567. JPH_ShapeSettings_Destroy((JPH_ShapeSettings*)settings);
  568. }
  569. // ============================================================================
  570. // Shape Scale Validation Tests
  571. // ============================================================================
  572. TEST_F(ShapesTest, Shape_IsValidScale) {
  573. JPH_SphereShape* shape = JPH_SphereShape_Create(1.0f);
  574. ASSERT_NE(shape, nullptr);
  575. // Uniform scale is valid for sphere
  576. JPH_Vec3 uniformScale = {2.0f, 2.0f, 2.0f};
  577. EXPECT_TRUE(JPH_Shape_IsValidScale((const JPH_Shape*)shape, &uniformScale));
  578. // Non-uniform scale is NOT valid for sphere (spheres only support uniform scale)
  579. JPH_Vec3 nonUniformScale = {2.0f, 1.0f, 1.0f};
  580. EXPECT_FALSE(JPH_Shape_IsValidScale((const JPH_Shape*)shape, &nonUniformScale));
  581. JPH_Shape_Destroy((JPH_Shape*)shape);
  582. }
  583. TEST_F(ShapesTest, Shape_MakeScaleValid) {
  584. JPH_SphereShape* shape = JPH_SphereShape_Create(1.0f);
  585. ASSERT_NE(shape, nullptr);
  586. JPH_Vec3 nonUniformScale = {2.0f, 1.0f, 1.0f};
  587. JPH_Vec3 result;
  588. JPH_Shape_MakeScaleValid((const JPH_Shape*)shape, &nonUniformScale, &result);
  589. // Result should be a valid (uniform) scale
  590. EXPECT_TRUE(JPH_Shape_IsValidScale((const JPH_Shape*)shape, &result));
  591. JPH_Shape_Destroy((JPH_Shape*)shape);
  592. }