HighSpeedTest.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392
  1. // SPDX-FileCopyrightText: 2021 Jorrit Rouwe
  2. // SPDX-License-Identifier: MIT
  3. #include <TestFramework.h>
  4. #include <Tests/General/HighSpeedTest.h>
  5. #include <Physics/Collision/RayCast.h>
  6. #include <Physics/Collision/CastResult.h>
  7. #include <Physics/Collision/Shape/SphereShape.h>
  8. #include <Physics/Collision/Shape/ScaledShape.h>
  9. #include <Physics/Collision/Shape/BoxShape.h>
  10. #include <Physics/Collision/Shape/MeshShape.h>
  11. #include <Physics/Collision/Shape/StaticCompoundShape.h>
  12. #include <Physics/Collision/Shape/ConvexHullShape.h>
  13. #include <Physics/Body/BodyCreationSettings.h>
  14. #include <Physics/PhysicsScene.h>
  15. #include <ObjectStream/ObjectStreamIn.h>
  16. #include <Application/DebugUI.h>
  17. #include <Utils/Log.h>
  18. #include <Layers.h>
  19. JPH_IMPLEMENT_RTTI_VIRTUAL(HighSpeedTest)
  20. {
  21. JPH_ADD_BASE_CLASS(HighSpeedTest, Test)
  22. }
  23. const char *HighSpeedTest::sScenes[] =
  24. {
  25. "Simple",
  26. "Convex Hull On Large Triangles",
  27. "Convex Hull On Terrain1",
  28. };
  29. int HighSpeedTest::sSelectedScene = 0;
  30. void HighSpeedTest::CreateDominoBlocks(Vec3Arg inOffset, int inNumWalls, float inDensity, float inRadius)
  31. {
  32. BodyCreationSettings box_settings;
  33. Ref<BoxShape> box_shape = new BoxShape(Vec3(0.9f, 1.0f, 0.1f));
  34. box_shape->SetDensity(inDensity); // Make box more heavy so the bouncing ball keeps a higher velocity
  35. box_settings.SetShape(box_shape);
  36. box_settings.mObjectLayer = Layers::MOVING;
  37. // U shaped set of thin boxes
  38. for (int i = 0; i < inNumWalls; ++i)
  39. {
  40. {
  41. box_settings.mPosition = inOffset + Vec3(2.0f * i, 1, -1.1f - inRadius);
  42. Body &box = *mBodyInterface->CreateBody(box_settings);
  43. #ifdef _DEBUG
  44. box.SetDebugName("Neg Box " + ConvertToString(i));
  45. #endif
  46. mBodyInterface->AddBody(box.GetID(), EActivation::DontActivate);
  47. }
  48. {
  49. box_settings.mPosition = inOffset + Vec3(2.0f * i, 1, +1.1f + inRadius);
  50. Body &box = *mBodyInterface->CreateBody(box_settings);
  51. #ifdef _DEBUG
  52. box.SetDebugName("Pos Box " + ConvertToString(i));
  53. #endif
  54. mBodyInterface->AddBody(box.GetID(), EActivation::DontActivate);
  55. }
  56. }
  57. {
  58. box_settings.mPosition = inOffset + Vec3(-1.1f - inRadius, 1, 0);
  59. box_settings.mRotation = Quat::sRotation(Vec3::sAxisY(), 0.5f * JPH_PI);
  60. Body &box = *mBodyInterface->CreateBody(box_settings);
  61. #ifdef _DEBUG
  62. box.SetDebugName("End Box");
  63. #endif
  64. mBodyInterface->AddBody(box.GetID(), EActivation::DontActivate);
  65. }
  66. }
  67. void HighSpeedTest::CreateDynamicObject(const char *inName, Vec3 inPosition, Vec3 inVelocity, Shape *inShape, EMotionQuality inMotionQuality)
  68. {
  69. BodyCreationSettings creation_settings;
  70. creation_settings.SetShape(inShape);
  71. creation_settings.mFriction = 0.0f;
  72. creation_settings.mRestitution = 1.0f;
  73. creation_settings.mLinearDamping = 0.0f;
  74. creation_settings.mAngularDamping = 0.0f;
  75. creation_settings.mMotionQuality = inMotionQuality;
  76. creation_settings.mObjectLayer = Layers::MOVING;
  77. creation_settings.mPosition = inPosition;
  78. Body &body = *mBodyInterface->CreateBody(creation_settings);
  79. JPH_IF_DEBUG(body.SetDebugName(inName);)
  80. body.SetLinearVelocity(inVelocity);
  81. mBodyInterface->AddBody(body.GetID(), inVelocity.IsNearZero()? EActivation::DontActivate : EActivation::Activate);
  82. }
  83. void HighSpeedTest::CreateSimpleScene()
  84. {
  85. // Floor
  86. CreateFloor();
  87. const float radius = 0.1f;
  88. const int num_walls = 5;
  89. const float density = 2000.0f;
  90. const float speed = 240.0f;
  91. Vec3 offset(0, 0, -30);
  92. {
  93. // U shaped set of thin walls
  94. TriangleList triangles;
  95. for (int i = 0; i < num_walls; ++i)
  96. {
  97. triangles.push_back(Triangle(Float3(2.0f*i-1,0,-1-radius), Float3(2.0f*i+1,0,-1-radius), Float3(2.0f*i,2,-1-radius)));
  98. triangles.push_back(Triangle(Float3(2.0f*i-1,0,1+radius), Float3(2.0f*i,2,1+radius), Float3(2.0f*i+1,0,1+radius)));
  99. }
  100. triangles.push_back(Triangle(Float3(-1-radius,0,-1), Float3(-1-radius,2,0), Float3(-1-radius,0,1)));
  101. Body &walls = *mBodyInterface->CreateBody(BodyCreationSettings(new MeshShapeSettings(triangles), offset, Quat::sIdentity(), EMotionType::Static, Layers::NON_MOVING));
  102. JPH_IF_DEBUG(walls.SetDebugName("Walls");)
  103. walls.SetRestitution(1.0f);
  104. walls.SetFriction(0.0f);
  105. mBodyInterface->AddBody(walls.GetID(), EActivation::DontActivate);
  106. // Fast moving sphere against mesh
  107. CreateDynamicObject("Sphere Against Mesh", offset + Vec3(2.0f * num_walls - 1, 1, 0), Vec3(-speed, 0, -speed), new SphereShape(radius));
  108. }
  109. offset += Vec3(0, 0, 5);
  110. {
  111. // Create wall of domino blocks
  112. CreateDominoBlocks(offset, num_walls, density, radius);
  113. // Fast moving sphere against domino blocks
  114. CreateDynamicObject("Sphere Against Domino", offset + Vec3(2.0f * num_walls - 1, 1, 0), Vec3(-speed, 0, -speed), new SphereShape(radius));
  115. }
  116. offset += Vec3(0, 0, 5);
  117. {
  118. // Create wall of domino blocks
  119. CreateDominoBlocks(offset, num_walls, density, radius);
  120. // Fast moving scaled box against domino blocks
  121. CreateDynamicObject("Scaled Box Against Domino", offset + Vec3(2.0f * num_walls - 1, 1, 0), Vec3(-speed, 0, -speed), new ScaledShape(new BoxShape(Vec3::sReplicate(0.5f * radius), 0.01f), Vec3::sReplicate(2.0f)));
  122. }
  123. offset += Vec3(0, 0, 5);
  124. {
  125. // Fast moving box stuck in ground moving, one moving up, one moving down
  126. CreateDynamicObject("Stuck In Ground Up", offset + Vec3(-1, 0, 0), Vec3(0, speed, 0), new BoxShape(Vec3::sReplicate(radius)));
  127. CreateDynamicObject("Stuck In Ground Down", offset + Vec3(1, 0, 0), Vec3(0, -speed, 0), new BoxShape(Vec3::sReplicate(radius)));
  128. }
  129. offset += Vec3(0, 0, 5);
  130. {
  131. // Single shape that has 4 walls to surround fast moving sphere
  132. BodyCreationSettings enclosing_settings;
  133. Ref<BoxShapeSettings> box_shape = new BoxShapeSettings(Vec3(1.0f, 1.0f, 0.1f));
  134. Ref<StaticCompoundShapeSettings> enclosing_shape = new StaticCompoundShapeSettings();
  135. enclosing_shape->AddShape(Vec3(0, 0, 1), Quat::sIdentity(), box_shape);
  136. enclosing_shape->AddShape(Vec3(0, 0, -1), Quat::sIdentity(), box_shape);
  137. enclosing_shape->AddShape(Vec3(1, 0, 0), Quat::sRotation(Vec3::sAxisY(), 0.5f * JPH_PI), box_shape);
  138. enclosing_shape->AddShape(Vec3(-1, 0, 0), Quat::sRotation(Vec3::sAxisY(), 0.5f * JPH_PI), box_shape);
  139. enclosing_settings.SetShapeSettings(enclosing_shape);
  140. enclosing_settings.mMotionType = EMotionType::Kinematic;
  141. enclosing_settings.mObjectLayer = Layers::MOVING;
  142. enclosing_settings.mPosition = offset + Vec3(0, 1, 0);
  143. Body &enclosing = *mBodyInterface->CreateBody(enclosing_settings);
  144. JPH_IF_DEBUG(enclosing.SetDebugName("Enclosing Box");)
  145. mBodyInterface->AddBody(enclosing.GetID(), EActivation::Activate);
  146. // Fast moving sphere in box
  147. CreateDynamicObject("Sphere In Box", offset + Vec3(0, 0.5f, 0), Vec3(-speed, 0, -0.5f * speed), new SphereShape(radius));
  148. }
  149. offset += Vec3(0, 0, 5);
  150. {
  151. // Two boxes on a collision course
  152. CreateDynamicObject("Box Vs Box Centered 1", offset + Vec3(1, 0.5f, 0), Vec3(-speed, 0, 0), new BoxShape(Vec3::sReplicate(radius)));
  153. CreateDynamicObject("Box Vs Box Centered 2", offset + Vec3(-1, 0.5f, 0), Vec3(speed, 0, 0), new BoxShape(Vec3::sReplicate(radius)));
  154. }
  155. offset += Vec3(0, 0, 5);
  156. {
  157. // Two boxes on a collision course, off center
  158. CreateDynamicObject("Box Vs Box Off Center 1", offset + Vec3(1, 0.5f, 0), Vec3(-speed, 0, 0), new BoxShape(Vec3::sReplicate(radius)));
  159. CreateDynamicObject("Box Vs Box Off Center 2", offset + Vec3(-1, 0.5f, radius), Vec3(speed, 0, 0), new BoxShape(Vec3::sReplicate(radius)));
  160. }
  161. offset += Vec3(0, 0, 5);
  162. {
  163. // Two boxes on a collision course, one discrete
  164. CreateDynamicObject("Box Vs Discrete Box 1", offset + Vec3(1, 0.5f, 0), Vec3(-speed, 0, 0), new BoxShape(Vec3::sReplicate(radius)));
  165. CreateDynamicObject("Box Vs Discrete Box 2", offset + Vec3(-1, 0.5f, 0), Vec3(60.0f, 0, 0), new BoxShape(Vec3::sReplicate(radius)), EMotionQuality::Discrete);
  166. }
  167. offset += Vec3(0, 0, 5);
  168. {
  169. // Two boxes on a collision course, one inactive
  170. CreateDynamicObject("Box Vs Inactive Box 1", offset + Vec3(1, 0.5f, 0), Vec3(-speed, 0, 0), new BoxShape(Vec3::sReplicate(radius)));
  171. CreateDynamicObject("Box Vs Inactive Box 2", offset + Vec3(0, 0.5f, 0), Vec3::sZero(), new BoxShape(Vec3::sReplicate(radius)));
  172. }
  173. offset += Vec3(0, 0, 5);
  174. {
  175. // Two boxes on a collision course, one inactive and discrete
  176. CreateDynamicObject("Box Vs Inactive Discrete Box 1", offset + Vec3(1, 0.5f, 0), Vec3(-speed, 0, 0), new BoxShape(Vec3::sReplicate(radius)));
  177. CreateDynamicObject("Box Vs Inactive Discrete Box 2", offset + Vec3(0, 0.5f, 0), Vec3::sZero(), new BoxShape(Vec3::sReplicate(radius)), EMotionQuality::Discrete);
  178. }
  179. offset += Vec3(0, 0, 5);
  180. {
  181. // Create long thin shape
  182. BoxShapeSettings box_settings(Vec3(0.05f, 0.8f, 0.03f), 0.015f);
  183. box_settings.SetEmbedded();
  184. BodyCreationSettings body_settings(&box_settings, offset + Vec3(0, 1, 0), Quat::sRotation(Vec3::sAxisX(), 0.5f * JPH_PI), EMotionType::Dynamic, Layers::MOVING);
  185. body_settings.mMotionQuality = EMotionQuality::LinearCast;
  186. body_settings.mRestitution = 0.0f;
  187. body_settings.mFriction = 1.0f;
  188. Body &body = *mPhysicsSystem->GetBodyInterface().CreateBody(body_settings);
  189. body.SetLinearVelocity(Vec3(0, -100.0f, 0));
  190. mPhysicsSystem->GetBodyInterface().AddBody(body.GetID(), EActivation::Activate);
  191. }
  192. offset += Vec3(0, 0, 5);
  193. {
  194. // Create long thin shape under 45 degrees
  195. BoxShapeSettings box_settings(Vec3(0.05f, 0.8f, 0.03f), 0.015f);
  196. box_settings.SetEmbedded();
  197. BodyCreationSettings body_settings(&box_settings, offset + Vec3(0, 1, 0), Quat::sRotation(Vec3::sAxisX(), 0.25f * JPH_PI), EMotionType::Dynamic, Layers::MOVING);
  198. body_settings.mMotionQuality = EMotionQuality::LinearCast;
  199. body_settings.mRestitution = 0.0f;
  200. body_settings.mFriction = 1.0f;
  201. Body &body = *mPhysicsSystem->GetBodyInterface().CreateBody(body_settings);
  202. body.SetLinearVelocity(Vec3(0, -100.0f, 0));
  203. mPhysicsSystem->GetBodyInterface().AddBody(body.GetID(), EActivation::Activate);
  204. }
  205. offset += Vec3(0, 0, 5);
  206. {
  207. // Create long thin shape with restitution
  208. BoxShapeSettings box_settings(Vec3(0.05f, 0.8f, 0.03f), 0.015f);
  209. box_settings.SetEmbedded();
  210. BodyCreationSettings body_settings(&box_settings, offset + Vec3(0, 1, 0), Quat::sRotation(Vec3::sAxisX(), 0.5f * JPH_PI), EMotionType::Dynamic, Layers::MOVING);
  211. body_settings.mMotionQuality = EMotionQuality::LinearCast;
  212. body_settings.mRestitution = 1.0f;
  213. body_settings.mFriction = 1.0f;
  214. Body &body = *mPhysicsSystem->GetBodyInterface().CreateBody(body_settings);
  215. body.SetLinearVelocity(Vec3(0, -100.0f, 0));
  216. mPhysicsSystem->GetBodyInterface().AddBody(body.GetID(), EActivation::Activate);
  217. }
  218. offset += Vec3(0, 0, 5);
  219. {
  220. // Create long thin shape under 45 degrees with restitution
  221. BoxShapeSettings box_settings(Vec3(0.05f, 0.8f, 0.03f), 0.015f);
  222. box_settings.SetEmbedded();
  223. BodyCreationSettings body_settings(&box_settings, offset + Vec3(0, 1, 0), Quat::sRotation(Vec3::sAxisX(), 0.25f * JPH_PI), EMotionType::Dynamic, Layers::MOVING);
  224. body_settings.mMotionQuality = EMotionQuality::LinearCast;
  225. body_settings.mRestitution = 1.0f;
  226. body_settings.mFriction = 1.0f;
  227. Body &body = *mPhysicsSystem->GetBodyInterface().CreateBody(body_settings);
  228. body.SetLinearVelocity(Vec3(0, -100.0f, 0));
  229. mPhysicsSystem->GetBodyInterface().AddBody(body.GetID(), EActivation::Activate);
  230. }
  231. }
  232. void HighSpeedTest::CreateFastSmallConvexObjects()
  233. {
  234. // Create small convex hull
  235. vector<Vec3> vertices = {
  236. Vec3(-0.044661f, 0.001230f, 0.003877f),
  237. Vec3(-0.024743f, -0.042562f, 0.003877f),
  238. Vec3(-0.012336f, -0.021073f, 0.048484f),
  239. Vec3(0.016066f, 0.028121f, -0.049904f),
  240. Vec3(-0.023734f, 0.043275f, -0.024153f),
  241. Vec3(0.020812f, 0.036341f, -0.019530f),
  242. Vec3(0.012495f, 0.021936f, 0.045288f),
  243. Vec3(0.026750f, 0.001230f, 0.049273f),
  244. Vec3(0.045495f, 0.001230f, -0.022077f),
  245. Vec3(0.022193f, -0.036274f, -0.021126f),
  246. Vec3(0.022781f, -0.037291f, 0.029558f),
  247. Vec3(0.014691f, -0.023280f, 0.052897f),
  248. Vec3(-0.012187f, -0.020815f, -0.040214f),
  249. Vec3(0.000541f, 0.001230f, -0.056224f),
  250. Vec3(-0.039882f, 0.001230f, -0.019461f),
  251. Vec3(0.000541f, 0.001230f, 0.056022f),
  252. Vec3(-0.020614f, -0.035411f, -0.020551f),
  253. Vec3(-0.019485f, 0.035916f, 0.027001f),
  254. Vec3(-0.023968f, 0.043680f, 0.003877f),
  255. Vec3(-0.020051f, 0.001230f, 0.039543f),
  256. Vec3(0.026213f, 0.001230f, -0.040589f),
  257. Vec3(-0.010797f, 0.020868f, 0.043152f),
  258. Vec3(-0.012378f, 0.023607f, -0.040876f)
  259. };
  260. ConvexHullShapeSettings convex_settings(vertices);
  261. convex_settings.SetEmbedded();
  262. BodyCreationSettings body_settings(&convex_settings, Vec3::sZero(), Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING);
  263. body_settings.mMotionQuality = EMotionQuality::LinearCast;
  264. // Create many instances with high velocity
  265. default_random_engine rnd;
  266. uniform_real_distribution<float> restitution_distrib(0.0f, 0.1f);
  267. uniform_real_distribution<float> velocity_distrib(-10.0f, 10.0f);
  268. for (int x = -25; x < 25; ++x)
  269. for (int y = -25 ; y < 25; ++y)
  270. {
  271. // Cast a ray to find the terrain
  272. Vec3 origin(float(x), 100.0f, float(y));
  273. Vec3 direction(0, -100.0f, 0);
  274. RayCastResult hit;
  275. if (mPhysicsSystem->GetNarrowPhaseQuery().CastRay({ origin, direction }, hit, SpecifiedBroadPhaseLayerFilter(BroadPhaseLayers::NON_MOVING), SpecifiedObjectLayerFilter(Layers::NON_MOVING)))
  276. {
  277. // Place 10m above terrain
  278. body_settings.mPosition = origin + hit.mFraction * direction + Vec3(0, 10.0f, 0);
  279. body_settings.mRotation = Quat::sRandom(rnd);
  280. body_settings.mRestitution = restitution_distrib(rnd);
  281. Body &body = *mPhysicsSystem->GetBodyInterface().CreateBody(body_settings);
  282. body.SetLinearVelocity(Vec3(velocity_distrib(rnd), -100.0f, velocity_distrib(rnd)));
  283. JPH_IF_DEBUG(body.SetDebugName(StringFormat("%dx%d", x, y));)
  284. mPhysicsSystem->GetBodyInterface().AddBody(body.GetID(), EActivation::Activate);
  285. }
  286. }
  287. }
  288. void HighSpeedTest::CreateConvexOnLargeTriangles()
  289. {
  290. // Create floor
  291. CreateLargeTriangleFloor();
  292. CreateFastSmallConvexObjects();
  293. }
  294. void HighSpeedTest::CreateConvexOnTerrain1()
  295. {
  296. // Load scene
  297. Ref<PhysicsScene> scene;
  298. if (!ObjectStreamIn::sReadObject("Assets/terrain1.bof", scene))
  299. FatalError("Failed to load scene");
  300. for (BodyCreationSettings &body : scene->GetBodies())
  301. body.mObjectLayer = Layers::NON_MOVING;
  302. scene->FixInvalidScales();
  303. scene->CreateBodies(mPhysicsSystem);
  304. CreateFastSmallConvexObjects();
  305. }
  306. void HighSpeedTest::Initialize()
  307. {
  308. switch (sSelectedScene)
  309. {
  310. case 0:
  311. CreateSimpleScene();
  312. break;
  313. case 1:
  314. CreateConvexOnLargeTriangles();
  315. break;
  316. case 2:
  317. CreateConvexOnTerrain1();
  318. break;
  319. default:
  320. JPH_ASSERT(false);
  321. break;
  322. }
  323. }
  324. void HighSpeedTest::CreateSettingsMenu(DebugUI *inUI, UIElement *inSubMenu)
  325. {
  326. inUI->CreateTextButton(inSubMenu, "Select Scene", [this, inUI]() {
  327. UIElement *scene_name = inUI->CreateMenu();
  328. for (uint i = 0; i < size(sScenes); ++i)
  329. inUI->CreateTextButton(scene_name, sScenes[i], [this, i]() { sSelectedScene = i; RestartTest(); });
  330. inUI->ShowMenu(scene_name);
  331. });
  332. }