diff --git a/crates/bevy_xpbd_3d/snapshots/bevy_xpbd_3d__tests__cubes_simulation_is_deterministic_across_machines.snap b/crates/bevy_xpbd_3d/snapshots/bevy_xpbd_3d__tests__cubes_simulation_is_deterministic_across_machines.snap index e4a8312d..64a4e3e4 100644 --- a/crates/bevy_xpbd_3d/snapshots/bevy_xpbd_3d__tests__cubes_simulation_is_deterministic_across_machines.snap +++ b/crates/bevy_xpbd_3d/snapshots/bevy_xpbd_3d__tests__cubes_simulation_is_deterministic_across_machines.snap @@ -9,15 +9,15 @@ expression: bodies ), Transform { translation: Vec3( - -7.491103, - 0.49970052, - -7.4250216, + -7.4559937, + 0.49969733, + -7.406183, ), rotation: Quat( - 0.000100662895, - 0.0125991665, - -4.975613e-5, - 0.99992067, + 0.00012730215, + 0.012526239, + -2.534363e-5, + 0.99992156, ), scale: Vec3( 1.0, @@ -32,15 +32,15 @@ expression: bodies ), Transform { translation: Vec3( - -5.117274, - 0.49962047, - -2.6372097, + -5.1171, + 0.4996907, + -2.638564, ), rotation: Quat( - -1.0786788e-5, - -0.029479325, - -3.2006243e-5, - 0.9995654, + 1.5275384e-5, + -0.027301952, + -6.0154813e-5, + 0.99962723, ), scale: Vec3( 1.0, @@ -55,15 +55,15 @@ expression: bodies ), Transform { translation: Vec3( - -4.5140595, - 0.4996839, - 0.013087435, + -4.499176, + 0.49964926, + -0.008331291, ), rotation: Quat( - -6.050062e-6, - -0.01593448, - -2.7127639e-5, - 0.99987304, + 3.1289226e-5, + -0.028124359, + 2.1045524e-5, + 0.99960446, ), scale: Vec3( 1.0, @@ -78,15 +78,15 @@ expression: bodies ), Transform { translation: Vec3( - -5.050511, - 0.4997201, - 2.452069, + -5.052091, + 0.49964485, + 2.4376268, ), rotation: Quat( - 1.5094273e-5, - 0.059477776, - -6.74418e-5, - 0.9982296, + -2.3089384e-5, + 0.057819452, + -1.7197267e-6, + 0.9983271, ), scale: Vec3( 1.0, @@ -101,15 +101,15 @@ expression: bodies ), Transform { translation: Vec3( - -2.4637558, - 0.49913877, - -5.3092375, + -2.4817648, + 0.49957314, + -5.271468, ), rotation: Quat( - -0.00017586688, - 0.034640536, - -0.00015322042, - 0.9993999, + 6.230139e-5, + 0.036112916, + -7.06728e-5, + 0.99934775, ), scale: Vec3( 1.0, @@ -124,15 +124,15 @@ expression: bodies ), Transform { translation: Vec3( - -2.7243629, - 0.49928263, - -2.740171, + -2.7214377, + 0.49941108, + -2.7184021, ), rotation: Quat( - 5.0723855e-5, - 0.02059857, - 0.00010048945, - 0.99978787, + -2.6408856e-5, + 0.0044332645, + 2.4121176e-5, + 0.9999902, ), scale: Vec3( 1.0, @@ -147,15 +147,15 @@ expression: bodies ), Transform { translation: Vec3( - -2.3385415, - 0.49923623, - -0.2326097, + -2.3349626, + 0.4992913, + -0.25004533, ), rotation: Quat( - 6.349044e-5, - -0.018455269, - 1.08689865e-5, - 0.9998297, + -1.9658559e-5, + -0.017039465, + 2.4970773e-6, + 0.99985486, ), scale: Vec3( 1.0, @@ -170,15 +170,15 @@ expression: bodies ), Transform { translation: Vec3( - -2.7127717, - 0.499459, - 2.920587, + -2.6870997, + 0.49959734, + 2.9144728, ), rotation: Quat( - -4.331521e-5, - -0.041756187, - 0.00011305308, - 0.99912786, + 1.63352e-5, + -0.03993375, + 2.5960855e-5, + 0.9992024, ), scale: Vec3( 1.0, @@ -193,15 +193,15 @@ expression: bodies ), Transform { translation: Vec3( - -0.08789223, - 0.4992498, - -4.4728193, + -0.08431798, + 0.49925387, + -4.4613953, ), rotation: Quat( - -3.923024e-5, - -0.03378269, - -8.341528e-5, - 0.9994292, + 9.539627e-6, + -0.033075668, + -7.855077e-5, + 0.9994529, ), scale: Vec3( 1.0, @@ -216,15 +216,15 @@ expression: bodies ), Transform { translation: Vec3( - -0.22180879, - 0.49907473, - -2.3462832, + -0.23088187, + 0.49942008, + -2.3143005, ), rotation: Quat( - -5.6017718e-5, - 0.0053683626, - 5.260491e-5, - 0.9999857, + 1.6296532e-5, + 0.007629119, + -2.4430146e-6, + 0.9999709, ), scale: Vec3( 1.0, @@ -239,15 +239,15 @@ expression: bodies ), Transform { translation: Vec3( - -0.01801183, - 0.49909836, - 0.043703828, + -0.02517712, + 0.499679, + 0.055104323, ), rotation: Quat( - 7.864665e-6, - 0.06285102, - -3.0683415e-5, - 0.99802303, + -4.21777e-5, + 0.051062137, + 1.6686328e-5, + 0.9986955, ), scale: Vec3( 1.0, @@ -262,15 +262,15 @@ expression: bodies ), Transform { translation: Vec3( - -0.109860785, - 0.49875805, - 2.3586721, + -0.12417784, + 0.49944255, + 2.3368204, ), rotation: Quat( - -3.2102595e-5, - 0.06635678, - 5.024871e-5, - 0.9977961, + 1.7667371e-5, + 0.052766006, + 1.2994585e-5, + 0.9986069, ), scale: Vec3( 1.0, @@ -285,15 +285,15 @@ expression: bodies ), Transform { translation: Vec3( - 2.4904108, - 0.49937657, - -5.1620445, + 2.485032, + 0.49938887, + -5.1308794, ), rotation: Quat( - 1.6732487e-6, - -0.059768517, - -4.270056e-5, - 0.9982123, + -0.00010083063, + -0.063369736, + -1.0706835e-5, + 0.99799013, ), scale: Vec3( 1.0, @@ -308,15 +308,15 @@ expression: bodies ), Transform { translation: Vec3( - 2.9670472, - 0.49928162, - -2.7516406, + 2.929946, + 0.49852893, + -2.723172, ), rotation: Quat( - 7.9470956e-5, - 0.033082373, - 3.5626974e-5, - 0.99945265, + 0.00021523339, + 0.033556916, + -2.465117e-5, + 0.99943686, ), scale: Vec3( 1.0, @@ -331,15 +331,15 @@ expression: bodies ), Transform { translation: Vec3( - 2.4516566, - 0.49934626, - -0.22472757, + 2.4648001, + 0.49899477, + -0.23346399, ), rotation: Quat( - 2.5784775e-6, - 0.009299282, - -0.00011730824, - 0.9999568, + -0.00015939993, + 0.007093024, + -2.7380043e-5, + 0.9999749, ), scale: Vec3( 1.0, @@ -354,15 +354,15 @@ expression: bodies ), Transform { translation: Vec3( - 2.9239776, - 0.49931955, - 2.9076083, + 2.923295, + 0.4993513, + 2.9048297, ), rotation: Quat( - 2.0695012e-5, - 0.0062594474, - -5.32606e-6, - 0.99998045, + 0.00024088702, + -0.0013322249, + -5.08928e-5, + 0.9999991, ), scale: Vec3( 1.0, @@ -377,15 +377,15 @@ expression: bodies ), Transform { translation: Vec3( - -5.856615, - 0.4997015, - -5.4403524, + -5.9099035, + 0.49969828, + -5.437266, ), rotation: Quat( - -0.7070474, - 0.01198206, - 0.011852235, - 0.7069653, + -0.70690084, + 0.020139085, + 0.020089086, + 0.7067405, ), scale: Vec3( 1.0, @@ -400,15 +400,15 @@ expression: bodies ), Transform { translation: Vec3( - -4.62757, - 2.4996388, - -2.1733255, + -4.656941, + 2.4996102, + -2.1990714, ), rotation: Quat( - 1.5309028e-5, - 0.0756463, - -4.8231577e-5, - 0.9971347, + 2.9881936e-5, + 0.07519428, + -6.8853e-5, + 0.9971689, ), scale: Vec3( 1.0, @@ -423,15 +423,15 @@ expression: bodies ), Transform { translation: Vec3( - -4.4637957, - 2.4996216, - 0.26711982, + -4.427765, + 2.499651, + 0.25970435, ), rotation: Quat( - 5.877175e-5, - -0.07269292, - 5.556364e-5, - 0.9973544, + 6.379787e-5, + -0.091451466, + -2.2172422e-5, + 0.99580956, ), scale: Vec3( 1.0, @@ -446,15 +446,15 @@ expression: bodies ), Transform { translation: Vec3( - -4.3766203, - 2.499743, - 2.4557016, + -4.404293, + 2.4996462, + 2.4337108, ), rotation: Quat( - 4.3842207e-5, - -0.02313916, - -6.248184e-5, - 0.99973226, + -2.8240129e-5, + -0.011989778, + -1.0082456e-5, + 0.9999281, ), scale: Vec3( 1.0, @@ -469,15 +469,15 @@ expression: bodies ), Transform { translation: Vec3( - -2.2004151, - 2.4992418, - -4.581829, + -2.2375886, + 2.499562, + -4.5653915, ), rotation: Quat( - -0.00022045623, - -0.09338757, - 0.00013676607, - 0.99562985, + -2.0587247e-5, + -0.0762269, + -8.881272e-6, + 0.9970905, ), scale: Vec3( 1.0, @@ -492,15 +492,15 @@ expression: bodies ), Transform { translation: Vec3( - -2.13236, - 2.4991019, - -1.9749995, + -2.1516783, + 2.4994493, + -2.0122275, ), rotation: Quat( - -0.0001523936, - -0.036166392, - -4.0870404e-5, - 0.9993458, + 3.411997e-5, + -0.04878974, + 4.176063e-5, + 0.9988091, ), scale: Vec3( 1.0, @@ -515,15 +515,15 @@ expression: bodies ), Transform { translation: Vec3( - -2.0392826, - 2.49911, - 0.64758754, + -2.0848567, + 2.4992769, + 0.5532737, ), rotation: Quat( - 8.8335815e-5, - 0.008508083, - 3.747557e-5, - 0.9999638, + -2.3580358e-5, + -0.0015443492, + 8.592678e-5, + 0.9999988, ), scale: Vec3( 1.0, @@ -538,15 +538,15 @@ expression: bodies ), Transform { translation: Vec3( - -2.0155404, - 2.4993477, - 2.9632459, + -2.026127, + 2.4996588, + 2.8813596, ), rotation: Quat( - -7.8026824e-5, - 0.06883067, - -1.1049986e-5, - 0.99762833, + 1.5648206e-5, + 0.076091595, + 1.2882005e-5, + 0.99710083, ), scale: Vec3( 1.0, @@ -561,15 +561,15 @@ expression: bodies ), Transform { translation: Vec3( - 0.11152353, - 2.4989862, - -4.957874, + 0.09472121, + 2.498852, + -4.8981414, ), rotation: Quat( - 4.978257e-5, - -0.03946816, - 8.753684e-5, - 0.99922085, + 6.3092964e-5, + -0.039511926, + -8.6972235e-5, + 0.9992191, ), scale: Vec3( 1.0, @@ -584,15 +584,15 @@ expression: bodies ), Transform { translation: Vec3( - 0.30485, - 2.499013, - -1.9726697, + 0.22883289, + 2.4995165, + -2.0097587, ), rotation: Quat( - -0.00022108105, - -0.05666972, - -8.332071e-5, - 0.998393, + -6.388899e-5, + -0.029711153, + -1.1642905e-5, + 0.9995585, ), scale: Vec3( 1.0, @@ -607,15 +607,15 @@ expression: bodies ), Transform { translation: Vec3( - 0.3537741, - 2.4989145, - 0.4019827, + 0.25519213, + 2.4997554, + 0.31895953, ), rotation: Quat( - -0.0001331907, - 0.07932223, - 7.243894e-5, - 0.99684906, + 4.3164593e-5, + 0.07926497, + 4.2795105e-5, + 0.9968536, ), scale: Vec3( 1.0, @@ -630,15 +630,15 @@ expression: bodies ), Transform { translation: Vec3( - 0.45713478, - 2.498639, - 2.8997712, + 0.3807087, + 2.499595, + 2.7723255, ), rotation: Quat( - 5.676384e-5, - -0.08800477, - 4.7969257e-5, - 0.9961201, + -7.66727e-6, + -0.09800275, + 4.2200012e-5, + 0.99518615, ), scale: Vec3( 1.0, @@ -653,15 +653,15 @@ expression: bodies ), Transform { translation: Vec3( - 2.1550486, - 2.4993088, - -4.558585, + 2.161761, + 2.4995956, + -4.516328, ), rotation: Quat( - -7.8351935e-5, - -0.008758627, - 5.87236e-5, - 0.9999617, + -0.0001466486, + -0.0014304257, + 7.6185734e-7, + 0.999999, ), scale: Vec3( 1.0, @@ -676,15 +676,15 @@ expression: bodies ), Transform { translation: Vec3( - 3.1127458, - 2.4993677, - -1.7044672, + 2.9706578, + 2.4985588, + -1.8014814, ), rotation: Quat( - -3.515524e-5, - 0.002616378, - 2.5534086e-5, - 0.9999966, + -6.5935215e-5, + 0.021796612, + -5.4511438e-5, + 0.9997624, ), scale: Vec3( 1.0, @@ -699,15 +699,15 @@ expression: bodies ), Transform { translation: Vec3( - 2.6639168, - 2.499025, - 0.7264531, + 2.6460724, + 2.4998014, + 0.6658953, ), rotation: Quat( - 0.00014442853, - -0.005654536, - -0.00019821663, - 0.99998397, + 0.000101340265, + -0.01419946, + -7.210292e-6, + 0.99989915, ), scale: Vec3( 1.0, @@ -722,15 +722,15 @@ expression: bodies ), Transform { translation: Vec3( - 3.153368, - 2.4989915, - 3.0121055, + 3.1310205, + 2.4987972, + 2.9939919, ), rotation: Quat( - 7.6585726e-5, - 0.024918636, - -1.4637226e-6, - 0.9996895, + 0.000104709514, + 0.0035360383, + -9.583718e-5, + 0.99999374, ), scale: Vec3( 1.0, @@ -745,15 +745,15 @@ expression: bodies ), Transform { translation: Vec3( - -5.239306, - 0.499743, - -8.633497, + -5.219633, + 0.49971282, + -8.6560135, ), rotation: Quat( - -0.70085853, - -0.09437523, - -0.09436631, - 0.7007037, + -0.70172936, + -0.08678492, + -0.08666368, + 0.70180744, ), scale: Vec3( 1.0, @@ -768,15 +768,15 @@ expression: bodies ), Transform { translation: Vec3( - -7.6945043, - 0.49972484, - -2.496247, + -7.6334453, + 0.4996158, + -2.014228, ), rotation: Quat( - 0.005883649, - 3.740373e-5, - 0.9999827, - 0.000100572295, + -0.09465276, + -8.626257e-5, + 0.99551034, + -4.2482105e-5, ), scale: Vec3( 1.0, @@ -791,15 +791,15 @@ expression: bodies ), Transform { translation: Vec3( - -7.425155, - 1.3318676, - -0.02214315, + -6.83501, + 1.6638923, + 0.34301078, ), rotation: Quat( - -0.22851808, - 0.27577013, - 0.7768336, - 0.5179381, + 0.15592058, + -0.41919777, + 0.5803524, + 0.6805535, ), scale: Vec3( 1.0, @@ -814,15 +814,15 @@ expression: bodies ), Transform { translation: Vec3( - -7.7659793, - 0.49970138, - 2.5905328, + -7.858676, + 0.49970815, + 2.836737, ), rotation: Quat( - 0.13727829, - -8.201868e-5, - 0.9905325, - 7.035393e-5, + 0.13557771, + 2.4782128e-5, + 0.9907667, + -9.49872e-5, ), scale: Vec3( 1.0, @@ -837,15 +837,15 @@ expression: bodies ), Transform { translation: Vec3( - -2.3974323, - 4.498533, - -4.9579797, + -2.4617047, + 4.4994144, + -4.8359556, ), rotation: Quat( - -2.5362564e-5, - -0.061997432, - -1.5741152e-5, - 0.9980763, + 3.6959605e-5, + -0.045716062, + -4.4114797e-5, + 0.9989545, ), scale: Vec3( 1.0, @@ -860,15 +860,15 @@ expression: bodies ), Transform { translation: Vec3( - -2.4116886, - 4.498747, - -2.494917, + -2.4751902, + 4.499276, + -2.526964, ), rotation: Quat( - -1.841009e-5, - -0.065193295, - 0.00015286745, - 0.99787265, + -7.2639385e-5, + -0.05775277, + 2.2835793e-5, + 0.9983309, ), scale: Vec3( 1.0, @@ -883,15 +883,15 @@ expression: bodies ), Transform { translation: Vec3( - -1.8854243, - 4.499175, - -0.08373339, + -1.9193122, + 4.499296, + -0.14918408, ), rotation: Quat( - 0.0001494736, - 0.026927918, - 8.6328946e-5, - 0.99963737, + 2.7735581e-5, + 0.030772645, + 6.897546e-5, + 0.9995264, ), scale: Vec3( 1.0, @@ -906,15 +906,15 @@ expression: bodies ), Transform { translation: Vec3( - -2.2298248, - 4.499217, - 2.1775942, + -2.2398627, + 4.4994173, + 2.0569596, ), rotation: Quat( - 1.6814138e-5, - 0.03743778, - 8.446652e-5, - 0.999299, + -1.4813115e-5, + 0.04206852, + 6.240855e-5, + 0.99911475, ), scale: Vec3( 1.0, @@ -929,15 +929,15 @@ expression: bodies ), Transform { translation: Vec3( - 0.11318978, - 4.4987197, - -4.5014095, + 0.05743592, + 4.4988194, + -4.489655, ), rotation: Quat( - 9.6565476e-5, - -0.01742931, - -4.6305147e-5, - 0.9998481, + -9.094167e-5, + -0.020092562, + 3.6281094e-6, + 0.9997981, ), scale: Vec3( 1.0, @@ -952,15 +952,15 @@ expression: bodies ), Transform { translation: Vec3( - -0.29200372, - 4.498738, - -2.1948102, + -0.3808061, + 4.499269, + -2.220193, ), rotation: Quat( - -5.8812337e-5, - -0.06257413, - -0.00013417448, - 0.9980403, + -5.054149e-5, + -0.049860172, + 3.964918e-5, + 0.99875623, ), scale: Vec3( 1.0, @@ -975,15 +975,15 @@ expression: bodies ), Transform { translation: Vec3( - 0.42965755, - 4.498358, - 0.2802253, + 0.29143196, + 4.4997396, + 0.22118911, ), rotation: Quat( - -4.2285035e-5, - 0.062136855, - -4.6680318e-5, - 0.9980677, + 4.367242e-6, + 0.07318499, + -8.097762e-6, + 0.9973184, ), scale: Vec3( 1.0, @@ -998,15 +998,15 @@ expression: bodies ), Transform { translation: Vec3( - 0.52938825, - 4.498385, - 2.855715, + 0.45188135, + 4.4994693, + 2.6869667, ), rotation: Quat( - -5.8683778e-5, - -0.099459514, - 5.5112105e-5, - 0.9950416, + 2.5042957e-5, + -0.10298179, + 2.1389602e-5, + 0.99468327, ), scale: Vec3( 1.0, @@ -1021,15 +1021,15 @@ expression: bodies ), Transform { translation: Vec3( - 2.8791473, - 4.49893, - -4.7825255, + 2.8360336, + 4.499717, + -4.6560807, ), rotation: Quat( - -0.00013554185, - 0.056231003, - -9.947635e-6, - 0.9984178, + -7.80365e-5, + 0.06895293, + -3.9522627e-5, + 0.9976199, ), scale: Vec3( 1.0, @@ -1044,15 +1044,15 @@ expression: bodies ), Transform { translation: Vec3( - 2.0879066, - 4.499028, - -1.932323, + 1.9975375, + 4.49907, + -2.0348773, ), rotation: Quat( - -0.00017600664, - -0.093940765, - 2.895729e-5, - 0.9955778, + -6.0351784e-5, + -0.05606416, + -0.0002664459, + 0.99842715, ), scale: Vec3( 1.0, @@ -1067,15 +1067,15 @@ expression: bodies ), Transform { translation: Vec3( - 3.1672993, - 4.498671, - 0.77680594, + 3.1457608, + 4.4997907, + 0.73316985, ), rotation: Quat( - 0.000118772085, - 0.101812415, - -9.6374955e-5, - 0.9948036, + 0.000109125715, + 0.09861481, + -1.4294048e-5, + 0.9951257, ), scale: Vec3( 1.0, @@ -1090,15 +1090,15 @@ expression: bodies ), Transform { translation: Vec3( - 3.5426705, - 4.498845, - 3.5086546, + 3.4554257, + 4.4986167, + 3.4815705, ), rotation: Quat( - 9.300578e-5, - 0.04410575, - 2.6937536e-5, - 0.9990269, + 0.00016424226, + 0.013058077, + -4.576529e-5, + 0.9999147, ), scale: Vec3( 1.0, @@ -1113,15 +1113,15 @@ expression: bodies ), Transform { translation: Vec3( - -7.285391, - 0.49969783, - -18.658655, + -7.296991, + 0.49972034, + -18.657724, ), rotation: Quat( - 0.27203226, - -0.27189365, - 0.6526613, - -0.6527676, + 0.27192494, + -0.27199364, + 0.6526262, + -0.65280575, ), scale: Vec3( 1.0, @@ -1136,15 +1136,15 @@ expression: bodies ), Transform { translation: Vec3( - -10.220359, - 0.49969655, - -2.5950935, + -9.916793, + 0.49967453, + -1.3412255, ), rotation: Quat( - -0.050493516, - -0.050302267, - 0.705291, - 0.705326, + 0.04744001, + 0.0473617, + 0.70555407, + 0.7054784, ), scale: Vec3( 1.0, @@ -1159,15 +1159,15 @@ expression: bodies ), Transform { translation: Vec3( - -9.902753, - 0.49969923, - 1.1578505, + -9.233273, + 2.4997437, + -0.89871705, ), rotation: Quat( - -0.33846053, - 0.6207668, - 0.6209316, - 0.33843312, + -0.6336368, + 0.63359606, + 0.31391883, + -0.3138717, ), scale: Vec3( 1.0, @@ -1182,15 +1182,15 @@ expression: bodies ), Transform { translation: Vec3( - -9.810901, - 0.49969834, - 4.09491, + -10.112035, + 0.49982226, + 3.5313923, ), rotation: Quat( - 0.14995503, - 0.1500971, - 0.6910521, - 0.6909641, + -0.08036921, + -0.08045656, + 0.70250475, + 0.70253444, ), scale: Vec3( 1.0, @@ -1205,15 +1205,15 @@ expression: bodies ), Transform { translation: Vec3( - -2.6859357, - 6.4983416, - -4.5752287, + -2.7385964, + 6.4994802, + -4.44351, ), rotation: Quat( - -0.00012949374, - 0.13560651, - 0.00010065739, - 0.99076277, + -3.890464e-5, + 0.13557632, + -1.2494161e-5, + 0.9907669, ), scale: Vec3( 1.0, @@ -1228,15 +1228,15 @@ expression: bodies ), Transform { translation: Vec3( - -2.304347, - 6.498738, - -2.0658116, + -2.3802423, + 6.4992795, + -2.1561701, ), rotation: Quat( - -0.00011902717, - 0.028138625, - 5.0904888e-5, - 0.99960405, + -0.0001286107, + 0.02996819, + -3.087238e-5, + 0.9995508, ), scale: Vec3( 1.0, @@ -1251,15 +1251,15 @@ expression: bodies ), Transform { translation: Vec3( - -1.935037, - 6.498976, - 0.035400026, + -1.9543732, + 6.499232, + -0.036781806, ), rotation: Quat( - 8.334789e-5, - -0.035123285, - 6.687111e-5, - 0.999383, + 4.9220438e-5, + -0.029028252, + 5.8214988e-5, + 0.9995786, ), scale: Vec3( 1.0, @@ -1274,15 +1274,15 @@ expression: bodies ), Transform { translation: Vec3( - -2.1851912, - 6.498916, - 2.1349611, + -2.1918676, + 6.4990826, + 2.0152183, ), rotation: Quat( - 2.1342537e-5, - -0.0062634842, - 4.6040102e-5, - 0.9999804, + -4.0499337e-5, + -0.004311105, + 4.6480982e-5, + 0.9999907, ), scale: Vec3( 1.0, @@ -1297,15 +1297,15 @@ expression: bodies ), Transform { translation: Vec3( - -0.11945735, - 6.4986844, - -4.7256284, + -0.155595, + 6.498452, + -4.711765, ), rotation: Quat( - 0.00010095695, - 0.048960462, - 8.201494e-5, - 0.9988007, + 1.90645e-5, + 0.05434508, + 2.0130545e-5, + 0.9985222, ), scale: Vec3( 1.0, @@ -1320,15 +1320,15 @@ expression: bodies ), Transform { translation: Vec3( - -0.15548754, - 6.498434, - -2.3823032, + -0.25857157, + 6.499125, + -2.3764043, ), rotation: Quat( - -0.00016357194, - -0.03815112, - -9.166359e-5, - 0.999272, + -4.4595374e-5, + -0.021183984, + -1.1292277e-5, + 0.9997756, ), scale: Vec3( 1.0, @@ -1343,15 +1343,15 @@ expression: bodies ), Transform { translation: Vec3( - 0.6915059, - 6.4982896, - -0.0077627436, + 0.50207174, + 6.4996257, + -0.08407934, ), rotation: Quat( - -7.166323e-5, - -0.03713825, - -6.737408e-5, - 0.99931014, + 5.34887e-5, + -0.012150269, + 1.5265025e-5, + 0.9999262, ), scale: Vec3( 1.0, @@ -1366,15 +1366,15 @@ expression: bodies ), Transform { translation: Vec3( - 0.22420824, - 6.498304, - 2.36405, + 0.14222027, + 6.4995503, + 2.1460233, ), rotation: Quat( - 7.046748e-5, - -0.02318879, - 1.3133379e-5, - 0.9997311, + 7.517343e-5, + -0.014461446, + 1.1289261e-5, + 0.99989545, ), scale: Vec3( 1.0, @@ -1389,15 +1389,15 @@ expression: bodies ), Transform { translation: Vec3( - 2.1562207, - 6.4986506, - -4.616957, + 2.1343179, + 6.499654, + -4.473686, ), rotation: Quat( - -2.2968494e-5, - -0.010945058, - 6.470741e-5, - 0.9999401, + -3.6236834e-5, + 0.0043190275, + -5.181014e-6, + 0.9999907, ), scale: Vec3( 1.0, @@ -1412,15 +1412,15 @@ expression: bodies ), Transform { translation: Vec3( - 1.9719344, - 6.4987464, - -2.1883187, + 1.9165581, + 6.4990935, + -2.300878, ), rotation: Quat( - -6.123904e-5, - -0.009509043, - -8.326136e-5, - 0.99995476, + -3.8320337e-5, + 0.028554719, + -0.00028096949, + 0.9995922, ), scale: Vec3( 1.0, @@ -1435,15 +1435,15 @@ expression: bodies ), Transform { translation: Vec3( - 2.927711, - 6.498822, - 0.25208122, + 2.9184437, + 6.498636, + 0.21267967, ), rotation: Quat( - 7.620432e-5, - 0.111280076, - -4.308047e-5, - 0.9937891, + 3.2166976e-5, + 0.11012593, + -5.633039e-5, + 0.99391764, ), scale: Vec3( 1.0, @@ -1458,15 +1458,15 @@ expression: bodies ), Transform { translation: Vec3( - 2.912033, - 6.498755, - 2.828739, + 2.8797495, + 6.498658, + 2.7826607, ), rotation: Quat( - -2.8328708e-5, - 0.0858826, - 6.36985e-5, - 0.9963053, + 4.3747117e-5, + 0.077276126, + 1.3033909e-5, + 0.99700975, ), scale: Vec3( 1.0, diff --git a/src/bundles.rs b/src/bundles.rs index 6ee7c952..9f043a07 100644 --- a/src/bundles.rs +++ b/src/bundles.rs @@ -54,6 +54,8 @@ pub struct RigidBodyBundle { pub inertia: Inertia, pub inv_inertia: InvInertia, pub local_center_of_mass: LocalCom, + + pub time_sleeping: TimeSleeping, } impl RigidBodyBundle { diff --git a/src/components/mod.rs b/src/components/mod.rs index 2e6fb5eb..128c5732 100644 --- a/src/components/mod.rs +++ b/src/components/mod.rs @@ -54,6 +54,35 @@ impl RigidBody { } } +/// Indicates that a body is not simulated by the physics engine until woken up again. +/// This is done to improve performance and to help prevent small jitter that is typically present in collisions. +/// +/// Bodies are marked as sleeping when their linear and angular velocity is below the [`SleepingThreshold`] for a time +/// indicated by [`DeactivationTime`]. A sleeping body is woken up when an active body interacts with it through +/// collisions or other constraints, or when gravity changes, or when the body's +/// position, rotation, velocity, or external forces are modified. +/// +/// Note that sleeping can cause unrealistic behaviour in some cases. +/// For example, removing the floor under sleeping bodies can leave them floating in the air. +/// Sleeping can be disabled for specific entities with the [`SleepingDisabled`] component, +/// or for all entities by setting the [`SleepingThreshold`] to a negative value. +#[derive(Reflect, Clone, Copy, Component, Debug, Default, PartialEq, Eq, From)] +#[reflect(Component)] +pub struct Sleeping; + +/// How long the velocity of the body has been below the [`SleepingThreshold`], +/// i.e. how long the body has been able to sleep. +/// +/// See [`Sleeping`] for further information. +#[derive(Reflect, Clone, Copy, Component, Debug, Default, PartialEq, From)] +#[reflect(Component)] +pub struct TimeSleeping(pub Scalar); + +/// Indicates that the body can not be deactivated by the physics engine. See [`Sleeping`] for information about sleeping. +#[derive(Reflect, Clone, Copy, Component, Debug, Default, PartialEq, Eq, From)] +#[reflect(Component)] +pub struct SleepingDisabled; + /// The position of a body. #[derive(Reflect, Clone, Copy, Component, Debug, Default, Deref, DerefMut, PartialEq, From)] #[reflect(Component)] @@ -83,6 +112,10 @@ pub struct PrevPos(pub Vector); #[reflect(Component)] pub struct LinVel(pub Vector); +impl LinVel { + pub const ZERO: LinVel = LinVel(Vector::ZERO); +} + #[cfg(all(feature = "2d", feature = "f64"))] impl From for LinVel { fn from(value: Vec2) -> Self { @@ -114,6 +147,13 @@ pub struct AngVel(pub Scalar); #[reflect(Component)] pub struct AngVel(pub Vector); +impl AngVel { + #[cfg(feature = "2d")] + pub const ZERO: AngVel = AngVel(0.0); + #[cfg(feature = "3d")] + pub const ZERO: AngVel = AngVel(Vector::ZERO); +} + #[cfg(all(feature = "3d", feature = "f64"))] impl From for AngVel { fn from(value: Vec3) -> Self { diff --git a/src/lib.rs b/src/lib.rs index 17674b4f..22bdaf34 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -85,6 +85,7 @@ struct FixedUpdateSet; /// - Constraint projection ([`SolverPlugin`], [`SubsteppingSet::SolvePos`]) /// - Velocity updates ([`SolverPlugin`], [`SubsteppingSet::UpdateVel`]) /// - Velocity solve ([`SolverPlugin`], [`SubsteppingSet::SolveVel`]) +/// - Control physics sleeping ([`SleepingPlugin`], [`PhysicsSet::Sleeping`]) /// - Synchronize physics with Bevy ([`SyncPlugin`], [`PhysicsSet::Sync`]) pub struct XpbdPlugin; @@ -105,6 +106,8 @@ impl Plugin for XpbdPlugin { .init_resource::() .init_resource::() .init_resource::() + .init_resource::() + .init_resource::() .init_resource::() .init_resource::() .register_type::() @@ -112,9 +115,14 @@ impl Plugin for XpbdPlugin { .register_type::() .register_type::() .register_type::() + .register_type::() + .register_type::() .register_type::() .register_type::() .register_type::() + .register_type::() + .register_type::() + .register_type::() .register_type::() .register_type::() .register_type::() @@ -145,6 +153,7 @@ impl Plugin for XpbdPlugin { PhysicsSet::Prepare, PhysicsSet::BroadPhase, PhysicsSet::Substeps, + PhysicsSet::Sleeping, PhysicsSet::Sync, ) .chain(), @@ -180,6 +189,7 @@ impl Plugin for XpbdPlugin { .add_plugin(BroadPhasePlugin) .add_plugin(IntegratorPlugin) .add_plugin(SolverPlugin) + .add_plugin(SleepingPlugin) .add_plugin(SyncPlugin); #[cfg(feature = "debug-render-aabbs")] diff --git a/src/resources.rs b/src/resources.rs index 477ed84e..924477d2 100644 --- a/src/resources.rs +++ b/src/resources.rs @@ -57,6 +57,43 @@ impl Default for NumPosIters { } } +/// A threshold that indicates the maximum linear and angular velocity allowed for a body to be deactivated. +/// +/// Setting a negative sleeping threshold disables sleeping entirely. +/// +/// See [`Sleeping`] for further information about sleeping. +#[derive(Reflect, Resource, Clone, Copy, PartialEq, PartialOrd, Debug)] +#[reflect(Resource)] +pub struct SleepingThreshold { + /// The maximum linear velocity allowed for a body to be marked as sleeping. + pub linear: Scalar, + /// The maximum angular velocity allowed for a body to be marked as sleeping. + pub angular: Scalar, +} + +impl Default for SleepingThreshold { + fn default() -> Self { + Self { + linear: 0.1, + angular: 0.2, + } + } +} + +/// How long in seconds the linear and angular velocity of a body need to be below +/// the [`SleepingThreshold`] before the body is deactivated. Defaults to 1 second. +/// +/// See [`Sleeping`] for further information about sleeping. +#[derive(Reflect, Resource, Clone, Copy, PartialEq, PartialOrd, Debug)] +#[reflect(Resource)] +pub struct DeactivationTime(pub Scalar); + +impl Default for DeactivationTime { + fn default() -> Self { + Self(1.0) + } +} + /// The global gravitational acceleration. This is applied to dynamic bodies in the integration step. /// /// The default is an acceleration of 9.81 m/s^2 pointing down, which is approximate to the gravitational acceleration near Earth's surface. diff --git a/src/steps/integrator.rs b/src/steps/integrator.rs index a6c8e01c..bd365bb5 100644 --- a/src/steps/integrator.rs +++ b/src/steps/integrator.rs @@ -14,16 +14,18 @@ impl Plugin for IntegratorPlugin { } } +type PosIntegrationComponents = ( + &'static RigidBody, + &'static mut Pos, + &'static mut PrevPos, + &'static mut LinVel, + &'static ExternalForce, + &'static Mass, +); + /// Explicitly integrates the positions and linear velocities of bodies taking only external forces like gravity into account. This acts as a prediction for the next positions of the bodies. fn integrate_pos( - mut bodies: Query<( - &RigidBody, - &mut Pos, - &mut PrevPos, - &mut LinVel, - &ExternalForce, - &Mass, - )>, + mut bodies: Query>, gravity: Res, sub_dt: Res, ) { @@ -45,20 +47,25 @@ fn integrate_pos( } } +type RotIntegrationComponents = ( + &'static RigidBody, + &'static mut Rot, + &'static mut PrevRot, + &'static mut AngVel, + &'static ExternalTorque, + &'static Inertia, + &'static InvInertia, +); + /// Explicitly integrates the rotations and angular velocities of bodies taking only external torque into account. This acts as a prediction for the next rotations of the bodies. #[cfg(feature = "2d")] fn integrate_rot( - mut bodies: Query<( - &RigidBody, - &mut Rot, - &mut PrevRot, - &mut AngVel, - &ExternalTorque, - &InvInertia, - )>, + mut bodies: Query>, sub_dt: Res, ) { - for (rb, mut rot, mut prev_rot, mut ang_vel, external_torque, inv_inertia) in &mut bodies { + for (rb, mut rot, mut prev_rot, mut ang_vel, external_torque, _inertia, inv_inertia) in + &mut bodies + { prev_rot.0 = *rot; if rb.is_static() { @@ -77,15 +84,7 @@ fn integrate_rot( /// Explicitly integrates the rotations and angular velocities of bodies taking only external torque into account. This acts as a prediction for the next rotations of the bodies. #[cfg(feature = "3d")] fn integrate_rot( - mut bodies: Query<( - &RigidBody, - &mut Rot, - &mut PrevRot, - &mut AngVel, - &ExternalTorque, - &Inertia, - &InvInertia, - )>, + mut bodies: Query>, sub_dt: Res, ) { for (rb, mut rot, mut prev_rot, mut ang_vel, external_torque, inertia, inv_inertia) in diff --git a/src/steps/mod.rs b/src/steps/mod.rs index b8ef7d44..4c2b77bd 100644 --- a/src/steps/mod.rs +++ b/src/steps/mod.rs @@ -3,12 +3,14 @@ pub mod broad_phase; pub mod integrator; pub mod prepare; +pub mod sleeping; pub mod solver; pub mod sync; pub use broad_phase::BroadPhasePlugin; pub use integrator::IntegratorPlugin; pub use prepare::PreparePlugin; +pub use sleeping::SleepingPlugin; pub use solver::SolverPlugin; pub use sync::SyncPlugin; @@ -25,8 +27,10 @@ pub enum PhysicsSet { /// /// The broad phase speeds up collision detection, as the number of accurate collision checks is greatly reduced. BroadPhase, - /// Substepping is an inner loop inside a physics step, see [`SubsteppingSet`] and [`XpbdSubstepSchedule`] + /// Substepping is an inner loop inside a physics step. See [`SubsteppingSet`] and [`XpbdSubstepSchedule`]. Substeps, + /// The sleeping step controls when bodies are active. This improves performance and helps prevent jitter. See [`Sleeping`]. + Sleeping, /// In the sync step, Bevy [`Transform`]s are synchronized with the physics world. Sync, } diff --git a/src/steps/sleeping.rs b/src/steps/sleeping.rs new file mode 100644 index 00000000..b900dc5d --- /dev/null +++ b/src/steps/sleeping.rs @@ -0,0 +1,109 @@ +//! Controls when bodies are active. This improves performance and helps prevent jitter. See [`Sleeping`]. + +use crate::prelude::*; +use bevy::prelude::*; + +/// Controls when bodies are active. This improves performance and helps prevent jitter. +/// +/// Bodies are marked as [`Sleeping`] when their linear and angular velocities are below the [`SleepingThreshold`] for a duration indicated by [`DeactivationTime`]. +/// +/// Bodies are woken up when an active body or constraint interacts with them, or when gravity changes, or when the body's position, rotation, velocity, or external forces are changed. +pub struct SleepingPlugin; + +impl Plugin for SleepingPlugin { + fn build(&self, app: &mut bevy::prelude::App) { + app.get_schedule_mut(XpbdSchedule) + .expect("add xpbd schedule first") + .add_systems( + (mark_sleeping_bodies, wake_up_bodies, gravity_wake_up_bodies) + .chain() + .in_set(PhysicsSet::Sleeping), + ); + } +} + +type SleepingQueryComponents = ( + Entity, + &'static RigidBody, + &'static mut LinVel, + &'static mut AngVel, + &'static mut TimeSleeping, +); + +/// Adds the [`Sleeping`] component to bodies whose linear and anigular velocities have been +/// under the [`SleepingThreshold`] for a duration indicated by [`DeactivationTime`]. +fn mark_sleeping_bodies( + mut commands: Commands, + mut bodies: Query, Without)>, + deactivation_time: Res, + sleep_threshold: Res, + dt: Res, +) { + for (entity, rb, mut lin_vel, mut ang_vel, mut time_sleeping) in &mut bodies { + // Only dynamic bodies can sleep. + if !rb.is_dynamic() { + continue; + } + + let lin_vel_sq = lin_vel.length_squared(); + + #[cfg(feature = "2d")] + let ang_vel_sq = ang_vel.powi(2); + #[cfg(feature = "3d")] + let ang_vel_sq = ang_vel.dot(ang_vel.0); + + // Negative thresholds indicate that sleeping is disabled. + let lin_sleeping_threshold_sq = sleep_threshold.linear * sleep_threshold.linear.abs(); + let ang_sleeping_threshold_sq = sleep_threshold.angular * sleep_threshold.angular.abs(); + + // If linear and angular velocity are below the sleeping threshold, + // add delta time to the time sleeping, i.e. the time that the body has remained still. + if lin_vel_sq < lin_sleeping_threshold_sq && ang_vel_sq < ang_sleeping_threshold_sq { + time_sleeping.0 += dt.0; + } else { + time_sleeping.0 = 0.0; + } + + // If the body has been still for long enough, set it to sleep and reset velocities. + if time_sleeping.0 > deactivation_time.0 { + commands.entity(entity).insert(Sleeping); + *lin_vel = LinVel::ZERO; + *ang_vel = AngVel::ZERO; + } + } +} + +type BodyWokeUpFilter = Or<( + Changed, + Changed, + Changed, + Changed, + Changed, + Changed, +)>; + +/// Removes the [`Sleeping`] component from sleeping bodies when properties like +/// position, rotation, velocity and external forces are changed. +fn wake_up_bodies( + mut commands: Commands, + mut bodies: Query<(Entity, &mut TimeSleeping), (With, BodyWokeUpFilter)>, +) { + for (entity, mut time_sleeping) in &mut bodies { + commands.entity(entity).remove::(); + time_sleeping.0 = 0.0; + } +} + +/// Removes the [`Sleeping`] component from sleeping bodies when [`Gravity`] is changed. +fn gravity_wake_up_bodies( + mut commands: Commands, + mut bodies: Query<(Entity, &mut TimeSleeping), With>, + gravity: Res, +) { + if gravity.is_changed() { + for (entity, mut time_sleeping) in &mut bodies { + commands.entity(entity).remove::(); + time_sleeping.0 = 0.0; + } + } +} diff --git a/src/steps/solver.rs b/src/steps/solver.rs index 4abe8d57..1db32224 100644 --- a/src/steps/solver.rs +++ b/src/steps/solver.rs @@ -1,4 +1,4 @@ -//! The XPBD solver is reponsible for constraint projection, velocity updates, and velocity corrections. See [`SolverPlugin`]. +//! The XPBD solver is responsible for constraint projection, velocity updates, and velocity corrections. See [`SolverPlugin`]. use crate::{ collision::*, @@ -101,7 +101,8 @@ fn clear_joint_lagrange(mut joints: Query<&mut T>) { /// Iterates through broad phase collision pairs, checks which ones are actually colliding, and uses [`PenetrationConstraint`]s to resolve the collisions. fn penetration_constraints( - mut bodies: Query<(RigidBodyQuery, &ColliderShape)>, + mut commands: Commands, + mut bodies: Query<(RigidBodyQuery, &ColliderShape, Option<&Sleeping>)>, broad_collision_pairs: Res, mut penetration_constraints: ResMut, sub_dt: Res, @@ -109,9 +110,18 @@ fn penetration_constraints( penetration_constraints.0.clear(); for (ent1, ent2) in broad_collision_pairs.0.iter() { - if let Ok([(mut body1, collider_shape1), (mut body2, collider_shape2)]) = - bodies.get_many_mut([*ent1, *ent2]) + if let Ok( + [(mut body1, collider_shape1, sleeping1), (mut body2, collider_shape2, sleeping2)], + ) = bodies.get_many_mut([*ent1, *ent2]) { + let inactive1 = body1.rb.is_static() || sleeping1.is_some(); + let inactive2 = body2.rb.is_static() || sleeping2.is_some(); + + // No collision if one of the bodies is static and the other one is sleeping. + if inactive1 && inactive2 { + continue; + } + if let Some(collision) = get_collision( *ent1, *ent2, @@ -124,6 +134,13 @@ fn penetration_constraints( &collider_shape1.0, &collider_shape2.0, ) { + // When an active body collides with a sleeping body, wake up the sleeping body. + if sleeping1.is_some() { + commands.entity(*ent1).remove::(); + } else if sleeping2.is_some() { + commands.entity(*ent2).remove::(); + } + let mut constraint = PenetrationConstraint::new(*ent1, *ent2, collision); constraint.constrain(&mut body1, &mut body2, sub_dt.0); penetration_constraints.0.push(constraint); @@ -134,7 +151,8 @@ fn penetration_constraints( /// Iterates through all joints and solves the constraints. fn joint_constraints( - mut bodies: Query, + mut commands: Commands, + mut bodies: Query<(RigidBodyQuery, Option<&Sleeping>)>, mut joints: Query<&mut T, Without>, num_pos_iters: Res, sub_dt: Res, @@ -142,12 +160,28 @@ fn joint_constraints( for _j in 0..num_pos_iters.0 { for mut joint in &mut joints { // Get components for entity a and b - if let Ok([mut body1, mut body2]) = bodies.get_many_mut(joint.entities()) { - // No need to solve constraints if neither of the bodies is dynamic - if !body1.rb.is_dynamic() && !body2.rb.is_dynamic() { + if let Ok([(mut body1, sleeping1), (mut body2, sleeping2)]) = + bodies.get_many_mut(joint.entities()) + { + let neither_dynamic = !body1.rb.is_dynamic() && !body2.rb.is_dynamic(); + let inactive1 = body1.rb.is_static() || sleeping1.is_some(); + let inactive2 = body2.rb.is_static() || sleeping2.is_some(); + + // No joint constraint solving if neither of the bodies is dynamic, + // or if one of the bodies is static and the other one is sleeping. + if neither_dynamic || (inactive1 && inactive2) { continue; } + let [ent1, ent2] = joint.entities(); + + // When an active body interacts with a sleeping body, wake up the sleeping body. + if sleeping1.is_some() { + commands.entity(ent1).remove::(); + } else if sleeping2.is_some() { + commands.entity(ent2).remove::(); + } + joint.constrain(&mut body1, &mut body2, sub_dt.0); } } @@ -156,7 +190,10 @@ fn joint_constraints( /// Updates the linear velocity of all dynamic bodies based on the change in position from the previous step. fn update_lin_vel( - mut bodies: Query<(&RigidBody, &Pos, &PrevPos, &mut LinVel, &mut PreSolveLinVel)>, + mut bodies: Query< + (&RigidBody, &Pos, &PrevPos, &mut LinVel, &mut PreSolveLinVel), + Without, + >, sub_dt: Res, ) { for (rb, pos, prev_pos, mut lin_vel, mut pre_solve_lin_vel) in &mut bodies { @@ -176,7 +213,10 @@ fn update_lin_vel( /// Updates the angular velocity of all dynamic bodies based on the change in rotation from the previous step. #[cfg(feature = "2d")] fn update_ang_vel( - mut bodies: Query<(&RigidBody, &Rot, &PrevRot, &mut AngVel, &mut PreSolveAngVel)>, + mut bodies: Query< + (&RigidBody, &Rot, &PrevRot, &mut AngVel, &mut PreSolveAngVel), + Without, + >, sub_dt: Res, ) { for (rb, rot, prev_rot, mut ang_vel, mut pre_solve_ang_vel) in &mut bodies { @@ -195,7 +235,10 @@ fn update_ang_vel( /// Updates the angular velocity of all dynamic bodies based on the change in rotation from the previous step. #[cfg(feature = "3d")] fn update_ang_vel( - mut bodies: Query<(&RigidBody, &Rot, &PrevRot, &mut AngVel, &mut PreSolveAngVel)>, + mut bodies: Query< + (&RigidBody, &Rot, &PrevRot, &mut AngVel, &mut PreSolveAngVel), + Without, + >, sub_dt: Res, ) { for (rb, rot, prev_rot, mut ang_vel, mut pre_solve_ang_vel) in &mut bodies { @@ -220,7 +263,7 @@ fn update_ang_vel( /// Applies velocity corrections caused by dynamic friction and restitution. #[allow(clippy::type_complexity)] fn solve_vel( - mut bodies: Query, + mut bodies: Query>, penetration_constraints: Res, gravity: Res, sub_dt: Res, @@ -310,7 +353,7 @@ fn solve_vel( /// Applies velocity corrections caused by joint damping. fn joint_damping( - mut bodies: Query<(&RigidBody, &mut LinVel, &mut AngVel, &InvMass)>, + mut bodies: Query<(&RigidBody, &mut LinVel, &mut AngVel, &InvMass), Without>, joints: Query<&T, Without>, sub_dt: Res, ) {