From 63580d38a50322a1f80ba55925094db47749d68e Mon Sep 17 00:00:00 2001 From: Periwink Date: Fri, 23 Aug 2024 12:08:57 -0400 Subject: [PATCH 01/36] Make contacts deterministic across Worlds (#480) # Objective - Fixes https://github.com/Jondolf/avian/issues/406 We noticed an issue where the Collisions were using the order of `Entity` directly to sort the pair of entities in a contact. The issue is that for networking, there is no guarantee that the pair of Entities that are replicated between a Client and a Server world are in the same direction. ## Solution - A solution is to keep using the `aabb.min.x` to order the two entities. - There are a couple concerns: - we want to make sure that when we match with previous entity pairs, this is not an issue. - it's not a problem for `previous_contacts` here: https://github.com/cBournhonesque/avian/blob/ac1fd0269e04f19f6892c62019cbb9ad9c19444e/src/collision/narrow_phase.rs#L549 because we check both directions - it is a problem for `match_manifolds` here: https://github.com/cBournhonesque/avian/blob/ac1fd0269e04f19f6892c62019cbb9ad9c19444e/src/collision/narrow_phase.rs#L571 which is why we modify the function - we want to make sure that this doesn't change how `CollisionStarted` and `CollisionEnded` events are sent, even if the entities change their order during a contact. **we should probably add a test for this** - it should be fined because those events are based on the `Contacts` struct that is generated during the narrow phase, and we made sure that contact matching still works independently of past entity order ## Test - tested in lightyear that the simulations are now completely deterministic - probably need to test that contact matching works correctly, even if the `aabb.min.x` of the two entities gets swapped during the contact --- ...tion_is_deterministic_across_machines.snap | 896 +++++++++--------- src/collision/broad_phase.rs | 6 +- src/collision/mod.rs | 53 +- src/collision/narrow_phase.rs | 15 +- 4 files changed, 488 insertions(+), 482 deletions(-) diff --git a/crates/avian3d/snapshots/avian3d__tests__cubes_simulation_is_deterministic_across_machines.snap b/crates/avian3d/snapshots/avian3d__tests__cubes_simulation_is_deterministic_across_machines.snap index 53b531f5..3ae4208d 100644 --- a/crates/avian3d/snapshots/avian3d__tests__cubes_simulation_is_deterministic_across_machines.snap +++ b/crates/avian3d/snapshots/avian3d__tests__cubes_simulation_is_deterministic_across_machines.snap @@ -9,15 +9,15 @@ expression: bodies ), Transform { translation: Vec3( - -4.3243527, - 0.49991986, - -4.3539424, + -4.6235313, + 0.4999551, + -4.3102026, ), rotation: Quat( - 2.5392612e-5, - 0.04461888, - 5.6499757e-6, - 0.99900407, + 6.792209e-6, + 0.028391084, + 9.04388e-6, + 0.9995969, ), scale: Vec3( 1.0, @@ -32,15 +32,15 @@ expression: bodies ), Transform { translation: Vec3( - -4.2786093, - 0.49992478, - -2.132244, + -4.395441, + 0.4999247, + -2.1216874, ), rotation: Quat( - 4.8880383e-7, - 0.0029120322, - 1.1011286e-5, - 0.99999577, + 8.929338e-6, + 0.04565544, + 3.2982214e-5, + 0.9989572, ), scale: Vec3( 1.0, @@ -55,15 +55,15 @@ expression: bodies ), Transform { translation: Vec3( - -4.369801, - 0.49982738, - -0.002351249, + -4.274698, + 0.49992138, + -0.05236319, ), rotation: Quat( - 6.927235e-5, - 0.024584759, - 3.78621e-5, - 0.99969774, + -2.374302e-6, + 0.008430953, + 2.023666e-5, + 0.9999645, ), scale: Vec3( 1.0, @@ -78,15 +78,15 @@ expression: bodies ), Transform { translation: Vec3( - -4.344536, - 0.4998861, - 2.0625727, + -4.5010552, + 0.49988276, + 2.0600283, ), rotation: Quat( - 1.3142657e-5, - 0.033230767, - -4.559078e-5, - 0.9994477, + -1.549203e-5, + 0.060082633, + 3.9520124e-5, + 0.9981934, ), scale: Vec3( 1.0, @@ -101,15 +101,15 @@ expression: bodies ), Transform { translation: Vec3( - -2.1951137, - 0.49992466, - -4.2405095, + -2.3512757, + 0.4999154, + -4.3770533, ), rotation: Quat( - -2.8148599e-5, - 0.03811511, - -2.8113238e-5, - 0.99927336, + -1.436397e-5, + -0.0029353325, + 2.4329216e-5, + 0.9999957, ), scale: Vec3( 1.0, @@ -124,15 +124,15 @@ expression: bodies ), Transform { translation: Vec3( - -2.1763153, - 0.4999553, - -2.1939993, + -2.1879144, + 0.49990264, + -2.1195703, ), rotation: Quat( - 1.470748e-5, - 0.029806782, - 2.8462907e-7, - 0.9995557, + 1.3067328e-5, + 0.042666864, + 3.453079e-6, + 0.99908936, ), scale: Vec3( 1.0, @@ -147,15 +147,15 @@ expression: bodies ), Transform { translation: Vec3( - -2.3350973, - 0.49995607, - -0.17375073, + -2.2019854, + 0.49993193, + -0.08177335, ), rotation: Quat( - 1.010217e-5, - 0.028624436, - -4.758877e-6, - 0.9995902, + 3.632382e-5, + 0.03833177, + -6.8493405e-6, + 0.9992651, ), scale: Vec3( 1.0, @@ -170,15 +170,15 @@ expression: bodies ), Transform { translation: Vec3( - -2.1607132, - 0.49988654, - 1.889722, + -2.2972653, + 0.4998191, + 2.0399506, ), rotation: Quat( - 2.1787431e-5, - 0.0150535945, - -4.8457427e-5, - 0.9998867, + -2.1943695e-5, + 0.044874776, + -1.23802765e-5, + 0.9989926, ), scale: Vec3( 1.0, @@ -193,15 +193,15 @@ expression: bodies ), Transform { translation: Vec3( - 0.07129576, - 0.49997407, - -4.4746776, + 0.107554115, + 0.49993876, + -4.327578, ), rotation: Quat( - 2.3356838e-6, - 0.0022213592, - 1.778282e-7, - 0.99999756, + 6.8130457e-6, + 0.033044733, + 7.462414e-7, + 0.9994539, ), scale: Vec3( 1.0, @@ -216,15 +216,15 @@ expression: bodies ), Transform { translation: Vec3( - -0.026171334, - 0.4999397, - -2.2034783, + -0.16756493, + 0.49985483, + -2.235181, ), rotation: Quat( - 6.363814e-6, - 0.014872479, - -1.3326761e-5, - 0.9998894, + 6.80968e-5, + 0.049224917, + 1.751861e-5, + 0.9987877, ), scale: Vec3( 1.0, @@ -239,15 +239,15 @@ expression: bodies ), Transform { translation: Vec3( - -0.19169083, - 0.49992487, - -0.18196382, + -0.0541185, + 0.49994805, + -0.10040754, ), rotation: Quat( - 1.0830025e-5, - 0.021014728, - 2.1036401e-5, - 0.99977916, + 3.8723425e-5, + 0.036581654, + 2.2724045e-9, + 0.99933064, ), scale: Vec3( 1.0, @@ -262,15 +262,15 @@ expression: bodies ), Transform { translation: Vec3( - -0.055619642, - 0.49991018, - 1.8385749, + -0.1380171, + 0.4999437, + 2.041131, ), rotation: Quat( - 1.2763383e-5, - 0.01656498, - 2.7240958e-5, - 0.9998628, + 4.469642e-6, + 0.063405216, + -8.009917e-6, + 0.99798787, ), scale: Vec3( 1.0, @@ -285,15 +285,15 @@ expression: bodies ), Transform { translation: Vec3( - 2.2245302, - 0.4999203, - -4.5461516, + 2.3947806, + 0.49992827, + -4.5461936, ), rotation: Quat( - 1.709381e-5, - 0.009905274, - 1.890729e-5, - 0.99995095, + -1.0799266e-5, + 0.027643675, + -1.9875699e-5, + 0.9996178, ), scale: Vec3( 1.0, @@ -308,15 +308,15 @@ expression: bodies ), Transform { translation: Vec3( - 1.9937165, - 0.49996367, - -2.0315197, + 1.9873046, + 0.49990204, + -2.1898642, ), rotation: Quat( - -4.879413e-6, - 0.006292736, - 7.124769e-6, - 0.9999802, + -7.0308447e-6, + 0.013860726, + -9.374108e-7, + 0.9999039, ), scale: Vec3( 1.0, @@ -331,15 +331,15 @@ expression: bodies ), Transform { translation: Vec3( - 2.0661972, - 0.49995384, - -0.02310217, + 1.9924507, + 0.49990392, + 0.028409133, ), rotation: Quat( - 7.933694e-6, - 0.0018710081, - 1.0839999e-5, - 0.9999983, + -2.0307734e-5, + 0.038908634, + -2.5417212e-5, + 0.9992428, ), scale: Vec3( 1.0, @@ -354,15 +354,15 @@ expression: bodies ), Transform { translation: Vec3( - 2.5236223, - 0.49996725, - 1.9759675, + 2.0213652, + 0.49992904, + 2.07552, ), rotation: Quat( - -1.0665481e-5, - 0.0015681527, - -6.8327086e-6, - 0.99999875, + -8.9036865e-7, + 0.038583953, + -4.484539e-6, + 0.99925536, ), scale: Vec3( 1.0, @@ -377,15 +377,15 @@ expression: bodies ), Transform { translation: Vec3( - -4.339363, - 2.4994073, - -4.2629104, + -4.6172295, + 2.4997044, + -4.4200807, ), rotation: Quat( - 6.1576924e-5, - 0.033588145, - 9.425271e-5, - 0.9994358, + -3.9479753e-5, + 0.008044018, + 0.000119170385, + 0.99996763, ), scale: Vec3( 1.0, @@ -400,15 +400,15 @@ expression: bodies ), Transform { translation: Vec3( - -4.810213, - 2.4993768, - -2.0232263, + -4.209664, + 2.4996552, + -1.8515848, ), rotation: Quat( - -2.8831807e-6, - 0.053750236, - 0.0001223818, - 0.9985544, + -2.3919894e-5, + -0.09500672, + 2.4027535e-5, + 0.9954766, ), scale: Vec3( 1.0, @@ -423,15 +423,15 @@ expression: bodies ), Transform { translation: Vec3( - -4.4904027, - 2.4992795, - 0.20261757, + -4.2944584, + 2.4997544, + 0.41124114, ), rotation: Quat( - -0.0001897744, - 0.020543976, - -2.1458027e-6, - 0.99978894, + -5.633291e-6, + -0.049498014, + 4.2487663e-6, + 0.99877423, ), scale: Vec3( 1.0, @@ -446,15 +446,15 @@ expression: bodies ), Transform { translation: Vec3( - -4.534022, - 2.4995549, - 2.3980114, + -4.963533, + 2.4995315, + 2.9559677, ), rotation: Quat( - 1.1574514e-5, - -0.076673515, - 6.9183196e-5, - 0.99705625, + 3.0370862e-5, + 0.22723709, + 0.000112364956, + 0.97383946, ), scale: Vec3( 1.0, @@ -469,15 +469,15 @@ expression: bodies ), Transform { translation: Vec3( - -2.047455, - 0.4999647, - -7.2127266, + -2.5552368, + 2.4993527, + -4.500378, ), rotation: Quat( - -0.70702183, - 0.0110129155, - 0.0110116, - 0.7070202, + -0.00011087178, + 0.04056032, + 0.00016879673, + 0.9991771, ), scale: Vec3( 1.0, @@ -492,15 +492,15 @@ expression: bodies ), Transform { translation: Vec3( - -2.2553782, - 2.499893, - -1.5258576, + -2.0473864, + 2.4997523, + -2.1444805, ), rotation: Quat( - 7.879404e-6, - -0.050584134, - 2.0222738e-5, - 0.9987198, + 2.3767692e-5, + -0.06617973, + 4.5312634e-5, + 0.99780774, ), scale: Vec3( 1.0, @@ -515,15 +515,15 @@ expression: bodies ), Transform { translation: Vec3( - -2.1133072, - 2.4998116, - 0.6407957, + -2.0019884, + 2.4996307, + 0.07245258, ), rotation: Quat( - 1.4860717e-5, - -0.06389087, - 8.017414e-6, - 0.9979569, + -1.8408135e-5, + -0.033830024, + -3.4742905e-5, + 0.9994276, ), scale: Vec3( 1.0, @@ -538,15 +538,15 @@ expression: bodies ), Transform { translation: Vec3( - -2.3780742, - 2.499642, - 2.9385276, + -2.3242505, + 2.4993322, + 2.2606218, ), rotation: Quat( - 5.148289e-5, - -0.07521257, - -5.60672e-5, - 0.9971675, + 0.00024682377, + -0.038670935, + -1.3273278e-5, + 0.99925196, ), scale: Vec3( 1.0, @@ -561,15 +561,15 @@ expression: bodies ), Transform { translation: Vec3( - 0.3398966, - 0.49997208, - -7.488397, + -0.017980704, + 2.4997847, + -4.5714655, ), rotation: Quat( - -0.7059476, - -0.04047617, - -0.040471934, - 0.7059474, + 7.516822e-5, + -0.1037768, + -6.3543735e-6, + 0.9946006, ), scale: Vec3( 1.0, @@ -584,15 +584,15 @@ expression: bodies ), Transform { translation: Vec3( - -0.05249534, - 2.4997911, - -1.965032, + 0.035522643, + 2.499665, + -1.887645, ), rotation: Quat( - 5.872177e-6, - -0.026311453, - -1.0617185e-5, - 0.9996538, + -5.4151067e-5, + -0.07759328, + 5.7816993e-5, + 0.9969851, ), scale: Vec3( 1.0, @@ -607,15 +607,15 @@ expression: bodies ), Transform { translation: Vec3( - 0.21838848, - 2.499774, - 0.3454408, + 0.13470578, + 2.4998295, + 0.23872411, ), rotation: Quat( - 1.00489415e-5, - 0.0010831612, - -2.1967493e-5, - 0.9999994, + -2.729211e-5, + -0.036848005, + 3.6757367e-5, + 0.99932086, ), scale: Vec3( 1.0, @@ -630,15 +630,15 @@ expression: bodies ), Transform { translation: Vec3( - -0.089054555, - 2.4995275, - 2.504886, + 1.2370723, + 2.4997654, + 3.0245857, ), rotation: Quat( - 4.7427882e-5, - -0.080646336, - -0.00010408245, - 0.9967428, + 5.3966458e-5, + -0.08192157, + -2.5600575e-5, + 0.9966388, ), scale: Vec3( 1.0, @@ -653,15 +653,15 @@ expression: bodies ), Transform { translation: Vec3( - 2.527748, - 2.4991374, - -4.4892116, + 3.0207999, + 2.49938, + -4.804246, ), rotation: Quat( - -9.4314746e-5, - -0.02328474, - -0.000272487, - 0.99972886, + -0.00025090633, + -0.22644581, + -3.296936e-5, + 0.97402376, ), scale: Vec3( 1.0, @@ -676,15 +676,15 @@ expression: bodies ), Transform { translation: Vec3( - 4.6978083, - 0.49997446, - -2.529034, + 2.2870855, + 2.4995823, + -2.0314507, ), rotation: Quat( - -0.12651552, - 0.12651613, - -0.695696, - 0.6956972, + -4.212562e-5, + -0.0795341, + -8.496051e-5, + 0.99683213, ), scale: Vec3( 1.0, @@ -699,15 +699,15 @@ expression: bodies ), Transform { translation: Vec3( - 5.0067825, - 0.49995244, - -0.23179539, + 2.401399, + 2.4997003, + 0.40044406, ), rotation: Quat( - -0.01690404, - 0.016887175, - -0.7068965, - 0.7069133, + -4.2936877e-5, + -0.02853228, + -6.93234e-5, + 0.99959284, ), scale: Vec3( 1.0, @@ -722,15 +722,15 @@ expression: bodies ), Transform { translation: Vec3( - 2.21022, - 0.49997383, - 4.6588492, + 4.8708215, + 0.49994954, + 2.716985, ), rotation: Quat( - 0.70695096, - -0.014782014, - 0.014780647, - 0.7069536, + -0.22389062, + 0.22390395, + -0.67072326, + 0.67072374, ), scale: Vec3( 1.0, @@ -745,15 +745,15 @@ expression: bodies ), Transform { translation: Vec3( - -4.924656, - 4.498837, - -4.395266, + -5.743751, + 0.49997622, + -7.7576003, ), rotation: Quat( - -5.0297967e-5, - 0.012984138, - 0.00015310242, - 0.9999157, + -0.6831842, + -0.1823796, + -0.18237586, + 0.68318087, ), scale: Vec3( 1.0, @@ -768,15 +768,15 @@ expression: bodies ), Transform { translation: Vec3( - -4.5116987, - 4.4991884, - -2.0919309, + -4.4394183, + 4.499168, + -2.2780342, ), rotation: Quat( - -6.07906e-5, - 0.21205448, - 0.00025381567, - 0.9772578, + -0.00010241466, + -0.029148506, + 0.00014624714, + 0.9995751, ), scale: Vec3( 1.0, @@ -791,15 +791,15 @@ expression: bodies ), Transform { translation: Vec3( - -4.708402, - 4.499147, - 0.4031223, + -4.742195, + 4.4995513, + -0.10190557, ), rotation: Quat( - -0.00022171022, - -0.009159838, - 6.280141e-5, - 0.99995804, + 2.112891e-5, + -0.07290185, + 5.221194e-5, + 0.9973391, ), scale: Vec3( 1.0, @@ -814,15 +814,15 @@ expression: bodies ), Transform { translation: Vec3( - -5.7102857, - 0.49997422, - 5.8349714, + -5.3016768, + 4.4993925, + 2.579552, ), rotation: Quat( - 0.70070577, - -0.094907604, - 0.09490725, - 0.7007115, + 3.9158287e-5, + -0.033970475, + 0.00010121866, + 0.99942285, ), scale: Vec3( 1.0, @@ -837,15 +837,15 @@ expression: bodies ), Transform { translation: Vec3( - -2.7235184, - 0.49997434, - -10.980807, + -2.5227122, + 4.499137, + -4.907738, ), rotation: Quat( - -0.6748626, - -0.21109083, - -0.21109423, - 0.6748632, + -0.00016248651, + -0.04171383, + 0.00018044861, + 0.9991296, ), scale: Vec3( 1.0, @@ -860,15 +860,15 @@ expression: bodies ), Transform { translation: Vec3( - -1.9942989, - 2.4997768, - -4.127104, + -2.2256882, + 4.499635, + -2.15674, ), rotation: Quat( - -0.7070685, - -0.011345126, - -0.011363063, - 0.70696276, + 1.6705788e-5, + -0.026514797, + 7.257547e-6, + 0.9996484, ), scale: Vec3( 1.0, @@ -883,15 +883,15 @@ expression: bodies ), Transform { translation: Vec3( - -2.4975805, - 4.4997125, - 0.0650188, + -2.3874211, + 4.499609, + 0.07365528, ), rotation: Quat( - 5.400572e-5, - -0.035251696, - -1.1071046e-5, - 0.99937844, + -1.194691e-5, + 0.0037788856, + -3.14291e-5, + 0.99999285, ), scale: Vec3( 1.0, @@ -906,15 +906,15 @@ expression: bodies ), Transform { translation: Vec3( - -2.734841, - 4.4995475, - 2.596534, + -2.5631702, + 4.4991107, + 2.296562, ), rotation: Quat( - 1.6605532e-5, - -0.07100289, - -3.328125e-5, - 0.9974761, + 0.0002573887, + 0.0011914646, + -3.442954e-5, + 0.9999993, ), scale: Vec3( 1.0, @@ -929,15 +929,15 @@ expression: bodies ), Transform { translation: Vec3( - -0.13911, - 0.49997413, - -11.233632, + 2.410794, + 4.4989686, + -5.4617033, ), rotation: Quat( - -0.7045634, - 0.05990648, - 0.059910644, - 0.70456535, + -0.00032732144, + 0.1383523, + -4.109261e-5, + 0.990383, ), scale: Vec3( 1.0, @@ -952,15 +952,15 @@ expression: bodies ), Transform { translation: Vec3( - 0.80650884, - 4.49954, - -2.1179333, + 0.26225993, + 4.4995036, + -2.110422, ), rotation: Quat( - -3.152784e-5, - 0.088109314, - -7.021435e-5, - 0.9961108, + -9.350747e-5, + 0.033721026, + 6.520454e-5, + 0.99943125, ), scale: Vec3( 1.0, @@ -975,15 +975,15 @@ expression: bodies ), Transform { translation: Vec3( - -0.021189805, - 4.4996448, - 0.0980292, + -0.3186675, + 4.499629, + 0.0963318, ), rotation: Quat( - 3.8537484e-5, - 0.035240203, - 3.141011e-5, - 0.99937886, + -1.4555179e-5, + 0.022730097, + 4.3712032e-5, + 0.9997416, ), scale: Vec3( 1.0, @@ -998,15 +998,15 @@ expression: bodies ), Transform { translation: Vec3( - 0.39334828, - 4.499295, - 2.5914605, + 1.660552, + 4.499688, + 2.6138442, ), rotation: Quat( - 3.1565494e-5, - -0.03311778, - -1.3205492e-5, - 0.99945146, + 5.7584428e-5, + 0.177509, + -2.7266731e-5, + 0.9841192, ), scale: Vec3( 1.0, @@ -1021,15 +1021,15 @@ expression: bodies ), Transform { translation: Vec3( - 2.9196174, - 4.498402, - -5.0181313, + 6.5616693, + 0.49995765, + -4.681283, ), rotation: Quat( - -0.00010783016, - -0.09249844, - -0.00039367206, - 0.99571276, + 0.11759628, + -0.117593996, + -0.6972648, + 0.6972551, ), scale: Vec3( 1.0, @@ -1044,15 +1044,15 @@ expression: bodies ), Transform { translation: Vec3( - 8.9486065, - 0.49997792, - -4.5367913, + 2.6076624, + 4.499192, + -2.3946123, ), rotation: Quat( - 0.37209338, - -0.37209192, - -0.6012873, - 0.6012883, + -8.149893e-5, + -0.13977607, + -0.00016805792, + 0.9901831, ), scale: Vec3( 1.0, @@ -1067,15 +1067,15 @@ expression: bodies ), Transform { translation: Vec3( - 10.860054, - 0.49997455, - 0.33711982, + 2.7510502, + 4.4994597, + -0.11339602, ), rotation: Quat( - 0.539376, - 0.45724833, - -0.45724505, - 0.5393741, + 1.7198529e-6, + -0.025809804, + -0.00010880344, + 0.99966687, ), scale: Vec3( 1.0, @@ -1090,15 +1090,15 @@ expression: bodies ), Transform { translation: Vec3( - 1.7973658, - 0.49997544, - 7.6060634, + 10.469548, + 0.4999776, + 1.3612407, ), rotation: Quat( - 0.6361565, - -0.30871332, - 0.3087156, - 0.6361569, + -0.5211823, + 0.5211832, + -0.4778806, + 0.4778779, ), scale: Vec3( 1.0, @@ -1113,15 +1113,15 @@ expression: bodies ), Transform { translation: Vec3( - -4.769561, - 6.4986734, - -4.464631, + -4.9279284, + 0.49997404, + -11.524672, ), rotation: Quat( - -5.1275645e-5, - 0.04602603, - 0.00013058743, - 0.9989402, + -0.7058846, + 0.041562825, + 0.041558053, + 0.70588416, ), scale: Vec3( 1.0, @@ -1136,15 +1136,15 @@ expression: bodies ), Transform { translation: Vec3( - -5.208853, - 6.4987497, - -2.2389634, + -4.447393, + 6.498999, + -2.2909615, ), rotation: Quat( - -8.055547e-6, - 0.021011695, - 0.00027942174, - 0.99977916, + -8.9638736e-5, + -0.0734362, + 0.00016005662, + 0.9972999, ), scale: Vec3( 1.0, @@ -1159,15 +1159,15 @@ expression: bodies ), Transform { translation: Vec3( - -4.677629, - 6.4989843, - 0.36477792, + -4.9871564, + 6.4994545, + 0.7354106, ), rotation: Quat( - -0.00023052825, - -0.07554992, - 0.00013078307, - 0.99714196, + 2.413892e-5, + -0.34084716, + 5.7699748e-5, + 0.94011873, ), scale: Vec3( 1.0, @@ -1182,15 +1182,15 @@ expression: bodies ), Transform { translation: Vec3( - -6.2685947, - 0.4999749, - 9.447185, + -11.189314, + 0.49997368, + 2.969288, ), rotation: Quat( - 0.7002883, - -0.097953685, - 0.09795478, - 0.70029014, + 0.40561, + -2.8614409e-6, + 0.9140462, + 2.6912608e-6, ), scale: Vec3( 1.0, @@ -1205,15 +1205,15 @@ expression: bodies ), Transform { translation: Vec3( - -3.2613604, - 0.49997345, - -14.447802, + -3.1166217, + 6.498797, + -4.673969, ), rotation: Quat( - -0.7053601, - 0.049655087, - 0.049652148, - 0.70536244, + -0.00015008492, + 0.022614641, + 0.00021529867, + 0.99974424, ), scale: Vec3( 1.0, @@ -1228,15 +1228,15 @@ expression: bodies ), Transform { translation: Vec3( - -1.4933374, - 2.4998949, - -7.4620485, + -2.2219517, + 6.4995317, + -1.6324775, ), rotation: Quat( - -0.69231987, - -0.1438652, - -0.14384842, - 0.69231755, + -9.831658e-6, + -0.012949602, + -1.5674532e-5, + 0.99991614, ), scale: Vec3( 1.0, @@ -1251,15 +1251,15 @@ expression: bodies ), Transform { translation: Vec3( - -2.598814, - 6.4995704, - 1.0894675, + -2.3359718, + 6.4995947, + 0.6213731, ), rotation: Quat( - 2.7027687e-5, - -0.11454783, - -1.9854979e-5, - 0.99341774, + -1.4475261e-5, + -0.14079188, + -3.3769644e-5, + 0.9900392, ), scale: Vec3( 1.0, @@ -1274,15 +1274,15 @@ expression: bodies ), Transform { translation: Vec3( - -1.027358, - 0.4999708, - 13.038362, + -3.2923048, + 6.498702, + 2.6605368, ), rotation: Quat( - 0.61228055, - 0.35370472, - -0.35370508, - 0.6122893, + 0.00031247907, + -0.032806676, + 7.217066e-5, + 0.99946165, ), scale: Vec3( 1.0, @@ -1297,15 +1297,15 @@ expression: bodies ), Transform { translation: Vec3( - -0.7097831, - 0.49997377, - -14.894028, + -0.036978077, + 5.15025, + -4.5430636, ), rotation: Quat( - -0.67818254, - 0.20018506, - 0.2001762, - 0.6781769, + 0.14573015, + -0.050092924, + 0.3867345, + 0.90922487, ), scale: Vec3( 1.0, @@ -1320,15 +1320,15 @@ expression: bodies ), Transform { translation: Vec3( - 0.07846992, - 6.4994574, - -2.276928, + 0.09961634, + 6.4994335, + -1.8124114, ), rotation: Quat( - -4.896268e-5, - -0.018394945, - -1.5834506e-5, - 0.9998308, + -9.371393e-5, + -0.028939188, + 5.6513025e-5, + 0.99958116, ), scale: Vec3( 1.0, @@ -1343,15 +1343,15 @@ expression: bodies ), Transform { translation: Vec3( - -0.34891623, - 6.4994407, - 1.0149225, + 0.40858662, + 6.4996233, + 0.5594393, ), rotation: Quat( - 4.3123695e-5, - -0.06607756, - 1.23120435e-5, - 0.9978145, + -2.1253027e-5, + -0.08784573, + 4.792721e-5, + 0.9961341, ), scale: Vec3( 1.0, @@ -1366,15 +1366,15 @@ expression: bodies ), Transform { translation: Vec3( - -0.4301395, - 0.4999747, - 6.790393, + -1.844904, + 0.4999791, + 4.966705, ), rotation: Quat( - 0.63092864, - 0.31926596, - -0.3192641, - 0.63092685, + -0.58803606, + -0.58803576, + 0.3927007, + 0.39270052, ), scale: Vec3( 1.0, @@ -1389,15 +1389,15 @@ expression: bodies ), Transform { translation: Vec3( - 2.3003325, - 6.4987993, - -4.480083, + 7.232593, + 2.4998035, + -4.444068, ), rotation: Quat( - -3.0372501e-5, - 0.079241425, - -0.00031923188, - 0.9968554, + 1.3062127e-5, + -0.16993561, + -7.430008e-5, + 0.98545516, ), scale: Vec3( 1.0, @@ -1412,15 +1412,15 @@ expression: bodies ), Transform { translation: Vec3( - 3.959462, - 4.4994497, - -0.8488095, + 3.2513006, + 6.4988227, + -2.2973287, ), rotation: Quat( - 0.18084154, - -0.1810612, - -0.68348867, - 0.6836347, + -6.8110654e-5, + -0.09295494, + -0.00023127707, + 0.99567026, ), scale: Vec3( 1.0, @@ -1435,15 +1435,15 @@ expression: bodies ), Transform { translation: Vec3( - 4.0047736, - 2.4997685, - 0.0033400285, + 2.899124, + 6.499335, + -0.016431106, ), rotation: Quat( - 0.0011647202, - 0.0013140906, - 0.7071359, - 0.7070754, + 1.5358692e-5, + 0.05692746, + -9.1249414e-5, + 0.99837834, ), scale: Vec3( 1.0, @@ -1458,15 +1458,15 @@ expression: bodies ), Transform { translation: Vec3( - 1.8501573, - 0.49997666, - 11.836599, + 5.00174, + 2.49973, + 2.6626215, ), rotation: Quat( - 0.7022902, - -0.08240583, - 0.08240115, - 0.70228755, + -0.16455474, + 0.16442548, + -0.68768793, + 0.68772906, ), scale: Vec3( 1.0, diff --git a/src/collision/broad_phase.rs b/src/collision/broad_phase.rs index c034d2bf..4949435d 100644 --- a/src/collision/broad_phase.rs +++ b/src/collision/broad_phase.rs @@ -237,11 +237,7 @@ fn sweep_and_prune( continue; } - if *ent1 < *ent2 { - broad_collision_pairs.push((*ent1, *ent2)); - } else { - broad_collision_pairs.push((*ent2, *ent1)); - } + broad_collision_pairs.push((*ent1, *ent2)); if *store_intersections1 { if let Ok(mut intersections) = aabb_intersection_query.get_mut(*ent1) { diff --git a/src/collision/mod.rs b/src/collision/mod.rs index 0726f7d4..d26b1081 100644 --- a/src/collision/mod.rs +++ b/src/collision/mod.rs @@ -117,13 +117,13 @@ impl Collisions { /// /// The order of the entities does not matter. pub fn get(&self, entity1: Entity, entity2: Entity) -> Option<&Contacts> { + // the keys are always sorted in order of `Entity` + if entity2 < entity1 { + return self.get(entity2, entity1); + } self.0 .get(&(entity1, entity2)) .filter(|contacts| contacts.during_current_frame) - .or(self - .0 - .get(&(entity2, entity1)) - .filter(|contacts| contacts.during_current_frame)) } /// Returns a mutable reference to the [contacts](Contacts) stored for the given entity pair if they are colliding, @@ -131,17 +131,13 @@ impl Collisions { /// /// The order of the entities does not matter. pub fn get_mut(&mut self, entity1: Entity, entity2: Entity) -> Option<&mut Contacts> { - // For lifetime reasons, the mutable borrows can't be in the same scope, - // so we check if the key exists first (there's probably a better way though) - if self.0.contains_key(&(entity1, entity2)) { - self.0 - .get_mut(&(entity1, entity2)) - .filter(|contacts| contacts.during_current_frame) - } else { - self.0 - .get_mut(&(entity2, entity1)) - .filter(|contacts| contacts.during_current_frame) + // the keys are always sorted in entity order + if entity2 < entity1 { + return self.get_mut(entity2, entity1); } + self.0 + .get_mut(&(entity1, entity2)) + .filter(|contacts| contacts.during_current_frame) } /// Returns `true` if the given entities have been in contact during this frame. @@ -205,10 +201,15 @@ impl Collisions { /// If you simply want to modify existing collisions, consider using methods like [`get_mut`](Self::get_mut) /// or [`iter_mut`](Self::iter_mut). pub fn insert_collision_pair(&mut self, contacts: Contacts) -> Option { - // TODO: We might want to order the data by Entity ID so that entity1, point1 etc. are for the "smaller" - // entity ID. This requires changes elsewhere as well though. - self.0 - .insert((contacts.entity1, contacts.entity2), contacts) + // order the keys by entity ID so that we don't get duplicate contacts + // between two entities + if contacts.entity1 < contacts.entity2 { + self.0 + .insert((contacts.entity1, contacts.entity2), contacts) + } else { + self.0 + .insert((contacts.entity2, contacts.entity1), contacts) + } } /// Extends [`Collisions`] with all collision pairs in the given iterable. @@ -375,8 +376,12 @@ impl ContactManifold { for contact in self.contacts.iter_mut() { for previous_contact in previous_contacts.iter() { // If the feature IDs match, copy the contact impulses over for warm starting. - if contact.feature_id1 == previous_contact.feature_id1 - && contact.feature_id2 == previous_contact.feature_id2 + if (contact.feature_id1 == previous_contact.feature_id1 + && contact.feature_id2 == previous_contact.feature_id2) || + // we have to check both directions because the entities are sorted in order + // of aabb.min.x, which could have changed even the two objects in contact are the same + (contact.feature_id2 == previous_contact.feature_id1 + && contact.feature_id1 == previous_contact.feature_id2) { contact.normal_impulse = previous_contact.normal_impulse; contact.tangent_impulse = previous_contact.tangent_impulse; @@ -389,10 +394,14 @@ impl ContactManifold { // If the feature IDs are unknown and the contact positions match closely enough, // copy the contact impulses over for warm starting. if unknown_features - && contact.point1.distance_squared(previous_contact.point1) + && (contact.point1.distance_squared(previous_contact.point1) < distance_threshold_squared - && contact.point2.distance_squared(previous_contact.point2) + && contact.point2.distance_squared(previous_contact.point2) + < distance_threshold_squared) + || (contact.point1.distance_squared(previous_contact.point2) < distance_threshold_squared + && contact.point2.distance_squared(previous_contact.point1) + < distance_threshold_squared) { contact.normal_impulse = previous_contact.normal_impulse; contact.tangent_impulse = previous_contact.tangent_impulse; diff --git a/src/collision/narrow_phase.rs b/src/collision/narrow_phase.rs index 1b11e278..2e4b4c5d 100644 --- a/src/collision/narrow_phase.rs +++ b/src/collision/narrow_phase.rs @@ -546,14 +546,15 @@ impl<'w, 's, C: AnyCollider> NarrowPhase<'w, 's, C> { ); // Get the previous contacts if there are any. - let previous_contacts = self - .collisions - .get_internal() - .get(&(collider1.entity, collider2.entity)) - .or(self - .collisions + let previous_contacts = if collider1.entity < collider2.entity { + self.collisions .get_internal() - .get(&(collider2.entity, collider1.entity))); + .get(&(collider1.entity, collider2.entity)) + } else { + self.collisions + .get_internal() + .get(&(collider2.entity, collider1.entity)) + }; let mut total_normal_impulse = 0.0; let mut total_tangent_impulse = default(); From f1a98d96d240220d1345a785c563b3d84ad653a6 Mon Sep 17 00:00:00 2001 From: Jan Hohenheim Date: Wed, 28 Aug 2024 11:13:01 +0200 Subject: [PATCH 02/36] Fix missing feature flag (#502) # Objective While depending on Avian's `main` branch, running `cargo doc --all-features` fails on my crate when including a serialize feature ## Solution Fix bitflags not being serializable when serializing. Ideally, the CI should catch this. --- crates/avian2d/Cargo.toml | 1 + crates/avian3d/Cargo.toml | 1 + 2 files changed, 2 insertions(+) diff --git a/crates/avian2d/Cargo.toml b/crates/avian2d/Cargo.toml index e6e97a1a..0d21cb30 100644 --- a/crates/avian2d/Cargo.toml +++ b/crates/avian2d/Cargo.toml @@ -39,6 +39,7 @@ serialize = [ "bevy/serialize", "parry2d?/serde-serialize", "parry2d-f64?/serde-serialize", + "bitflags/serde", ] [lib] diff --git a/crates/avian3d/Cargo.toml b/crates/avian3d/Cargo.toml index 81f9d961..b451f408 100644 --- a/crates/avian3d/Cargo.toml +++ b/crates/avian3d/Cargo.toml @@ -48,6 +48,7 @@ serialize = [ "bevy/serialize", "parry3d?/serde-serialize", "parry3d-f64?/serde-serialize", + "bitflags/serde", ] [lib] From 2f83cc15bd804ad82ecd7e080e337d052d1c3472 Mon Sep 17 00:00:00 2001 From: Richard Jones Date: Wed, 4 Sep 2024 10:29:09 +0100 Subject: [PATCH 03/36] add some #[must_use] to LockedAxes builder fns (#506) # Objective - Helps avoid my mistake of grabbing `LockedAxes` from a query and calling `unlock_rotation()` without reassigning to the component, failing to notice it returns self ## Solution - Adds `#[must_use]` to builder-style fns that return Self, yielding this compiler warning: ``` warning: unused return value of `avian2d::prelude::LockedAxes::unlock_rotation` that must be used --> crates/avian2d/examples/uncommitted_example_fixed_joint_2d.rs:89:17 | 89 | locked_axes.unlock_rotation(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `#[warn(unused_must_use)]` on by default help: use `let _ = ...` to ignore the resulting value | 89 | let _ = locked_axes.unlock_rotation(); | +++++++ ``` --- src/dynamics/rigid_body/locked_axes.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/dynamics/rigid_body/locked_axes.rs b/src/dynamics/rigid_body/locked_axes.rs index 747a7213..c56bb49a 100644 --- a/src/dynamics/rigid_body/locked_axes.rs +++ b/src/dynamics/rigid_body/locked_axes.rs @@ -62,12 +62,14 @@ impl LockedAxes { } /// Locks translation along the `X` axis. + #[must_use] pub const fn lock_translation_x(mut self) -> Self { self.0 |= 0b100_000; self } /// Locks translation along the `Y` axis. + #[must_use] pub const fn lock_translation_y(mut self) -> Self { self.0 |= 0b010_000; self @@ -75,6 +77,7 @@ impl LockedAxes { /// Locks translation along the `Z` axis. #[cfg(feature = "3d")] + #[must_use] pub const fn lock_translation_z(mut self) -> Self { self.0 |= 0b001_000; self @@ -89,6 +92,7 @@ impl LockedAxes { /// Locks rotation around the `Y` axis. #[cfg(feature = "3d")] + #[must_use] pub const fn lock_rotation_y(mut self) -> Self { self.0 |= 0b000_010; self @@ -96,6 +100,7 @@ impl LockedAxes { /// Locks rotation around the `Z` axis. #[cfg(feature = "3d")] + #[must_use] pub const fn lock_rotation_z(mut self) -> Self { self.0 |= 0b000_001; self @@ -103,18 +108,21 @@ impl LockedAxes { /// Locks all rotation. #[cfg(feature = "2d")] + #[must_use] pub const fn lock_rotation(mut self) -> Self { self.0 |= 0b000_001; self } /// Unlocks translation along the `X` axis. + #[must_use] pub const fn unlock_translation_x(mut self) -> Self { self.0 &= !0b100_000; self } /// Unlocks translation along the `Y` axis. + #[must_use] pub const fn unlock_translation_y(mut self) -> Self { self.0 &= !0b010_000; self @@ -122,6 +130,7 @@ impl LockedAxes { /// Unlocks translation along the `Z` axis. #[cfg(feature = "3d")] + #[must_use] pub const fn unlock_translation_z(mut self) -> Self { self.0 &= !0b001_000; self @@ -129,6 +138,7 @@ impl LockedAxes { /// Unlocks rotation around the `X` axis. #[cfg(feature = "3d")] + #[must_use] pub const fn unlock_rotation_x(mut self) -> Self { self.0 &= !0b000_100; self @@ -136,6 +146,7 @@ impl LockedAxes { /// Unlocks rotation around the `Y` axis. #[cfg(feature = "3d")] + #[must_use] pub const fn unlock_rotation_y(mut self) -> Self { self.0 &= !0b000_010; self @@ -143,6 +154,7 @@ impl LockedAxes { /// Unlocks rotation around the `Z` axis. #[cfg(feature = "3d")] + #[must_use] pub const fn unlock_rotation_z(mut self) -> Self { self.0 &= !0b000_001; self @@ -150,6 +162,7 @@ impl LockedAxes { /// Unlocks all rotation. #[cfg(feature = "2d")] + #[must_use] pub const fn unlock_rotation(mut self) -> Self { self.0 &= !0b000_001; self From a5616dd194aecfef294d70ad9294d20ac68a49d4 Mon Sep 17 00:00:00 2001 From: Darryl James Date: Sat, 7 Sep 2024 20:12:38 +0900 Subject: [PATCH 04/36] Optimize `wake_on_collision_ended` when large number of collisions are occurring (#508) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Objective - With a large number of colliding bodies, I was seeing `wake_on_collision_ended` use almost as much time as `run_substep_schedule` in some cases - I narrowed the slowness down to evaluation of [this if condition](https://github.com/Jondolf/avian/blob/2f83cc15bd804ad82ecd7e080e337d052d1c3472/src/dynamics/sleeping/mod.rs#L280-L285) - Evaluating this once takes around 3μs on my laptop which isn't a huge deal on its own, but when there's 2500 colliders it started to add up to multiple milliseconds ## Solution - In `wake_on_collision_ended`, skip the body of the first for loop (over `sleeping`) if the following both hold true: - The body does not already have a `Sleeping` component - TimeSleeping is 0.0 I believe this is ok because the side-effects of that loop is to remove the `Sleeping` component and to reset `TimeSleeping` to zero, which is exactly the condition it now tests for. Therefore, if the entity is already in that state, there's no point in executing the [relatively costly if statement here](https://github.com/Jondolf/avian/blob/2f83cc15bd804ad82ecd7e080e337d052d1c3472/src/dynamics/sleeping/mod.rs#L280-L285) (as mentioned above). - Since it now also already knows if the `Sleeping` component is present, I gated the `commands.entity(entity).remove::();` calls so that it only adds that command if the component is present - This should save a few lookups on Bevy's end, but isn't strictly necessary for this PR - It may also be worth gating the `sleeping` query on `Without` but I wasn't sure how correct that was - If we think it's worth it, we could add that too so that bodies with sleeping disabled aren't even considered here ## Results Large numbers of colliders (here, 2500) showed a near 1000x improvement in execution time of `wake_on_collision_ended`, going from multiple milliseconds to a few microseconds when none of the bodies are sleeping: 2500 ~~Performance regressed for small numbers of colliders (here, 100), however this is regression at the microseconds level (3.5μs to 15.5μs median), so I posit that this is a worthy tradeoff:~~ (removed; I had the traces backwards) --- ## Changelog - SleepingPlugin's `wake_on_collision_ended` system - `sleeping` query now includes `Has` - first loop in system body skips anything that isn't already sleeping or about to sleep - don't remove `Sleeping` component if `Has`resolved to false --- src/dynamics/sleeping/mod.rs | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/src/dynamics/sleeping/mod.rs b/src/dynamics/sleeping/mod.rs index 44e79a54..423f0823 100644 --- a/src/dynamics/sleeping/mod.rs +++ b/src/dynamics/sleeping/mod.rs @@ -265,10 +265,18 @@ fn wake_on_collision_ended( moved_bodies: Query, (Changed, Without)>, colliders: Query<(&ColliderParent, Ref)>, collisions: Res, - mut sleeping: Query<(Entity, &mut TimeSleeping)>, + mut sleeping: Query<(Entity, &mut TimeSleeping, Has)>, ) { // Wake up bodies when a body they're colliding with moves. - for (entity, mut time_sleeping) in &mut sleeping { + for (entity, mut time_sleeping, is_sleeping) in &mut sleeping { + // Skip anything that isn't currently sleeping and already has a time_sleeping of zero. + // We can't gate the sleeping query using With here because must also reset + // non-zero time_sleeping to 0 when a colliding body moves. + let must_check = is_sleeping || time_sleeping.0 > 0.0; + if !must_check { + continue; + } + // Here we could use CollidingEntities, but it'd be empty if the ContactReportingPlugin was disabled. let mut colliding_entities = collisions.collisions_with_entity(entity).map(|c| { if entity == c.entity1 { @@ -283,7 +291,9 @@ fn wake_on_collision_ended( || moved_bodies.get(p.get()).is_ok_and(|pos| pos.is_changed()) }) }) { - commands.entity(entity).remove::(); + if is_sleeping { + commands.entity(entity).remove::(); + } time_sleeping.0 = 0.0; } } @@ -293,12 +303,16 @@ fn wake_on_collision_ended( if contacts.during_current_frame || !contacts.during_previous_frame { continue; } - if let Ok((_, mut time_sleeping)) = sleeping.get_mut(contacts.entity1) { - commands.entity(contacts.entity1).remove::(); + if let Ok((_, mut time_sleeping, is_sleeping)) = sleeping.get_mut(contacts.entity1) { + if is_sleeping { + commands.entity(contacts.entity1).remove::(); + } time_sleeping.0 = 0.0; } - if let Ok((_, mut time_sleeping)) = sleeping.get_mut(contacts.entity2) { - commands.entity(contacts.entity2).remove::(); + if let Ok((_, mut time_sleeping, is_sleeping)) = sleeping.get_mut(contacts.entity2) { + if is_sleeping { + commands.entity(contacts.entity2).remove::(); + } time_sleeping.0 = 0.0; } } From 7d79ca1148e661d0fc57e8b5b8b381725ef0ad06 Mon Sep 17 00:00:00 2001 From: Thierry Berger Date: Fri, 20 Sep 2024 13:00:34 +0200 Subject: [PATCH 05/36] experimentation with debugdump (#383) # Objective - I appreciate graphs from [`bevy_mod_debugdump`](https://github.com/jakobhellermann/bevy_mod_debugdump), this PR merely starts a discussion on adding some "first party" interest to such graphs. - it can be useful to compare with bevy_rapier: https://github.com/dimforge/bevy_rapier/pull/576 ## Solution - Add an example to generate those Run with: `cargo run --example debugdump_3d > avian.dot && dot -Tsvg avian.dot > avian.svg` ## Output for 3d
PhysicsSchedule

![dump_physics](https://github.com/user-attachments/assets/0ac6d299-0cb2-4f1f-987a-763afcc2f57c)

SubstepSchedule

![dump_substeps](https://github.com/user-attachments/assets/189a1141-95d8-459f-8d28-a4807f38ed4e)

FixedPostUpdate

![dump_fixed_update](https://github.com/user-attachments/assets/12488f54-932a-494c-a90b-929320c363aa)

Update

![dump_update](https://github.com/user-attachments/assets/051a4d79-a626-4a3c-905d-084d16afd798)

--- ## Changelog - Add an example to generate systems ordering graphs. --------- Co-authored-by: Joona Aalto --- crates/avian2d/Cargo.toml | 5 +++++ crates/avian2d/examples/debugdump_2d.rs | 18 ++++++++++++++++++ crates/avian3d/Cargo.toml | 6 +++++- crates/avian3d/examples/debugdump_3d.rs | 19 +++++++++++++++++++ crates/examples_common_2d/Cargo.toml | 2 +- crates/examples_common_3d/Cargo.toml | 2 +- 6 files changed, 49 insertions(+), 3 deletions(-) create mode 100644 crates/avian2d/examples/debugdump_2d.rs create mode 100644 crates/avian3d/examples/debugdump_3d.rs diff --git a/crates/avian2d/Cargo.toml b/crates/avian2d/Cargo.toml index 0d21cb30..0b951a35 100644 --- a/crates/avian2d/Cargo.toml +++ b/crates/avian2d/Cargo.toml @@ -72,6 +72,7 @@ bevy_math = { version = "0.14", features = ["approx"] } approx = "0.5" criterion = { version = "0.5", features = ["html_reports"] } insta = "1.0" +bevy_mod_debugdump = "0.11" [[example]] name = "dynamic_character_2d" @@ -117,6 +118,10 @@ required-features = ["2d", "default-collider"] name = "revolute_joint_2d" required-features = ["2d", "default-collider"] +[[example]] +name = "debugdump_2d" +required-features = ["2d"] + [[bench]] name = "pyramid" required-features = ["2d", "default-collider"] diff --git a/crates/avian2d/examples/debugdump_2d.rs b/crates/avian2d/examples/debugdump_2d.rs new file mode 100644 index 00000000..d8abf497 --- /dev/null +++ b/crates/avian2d/examples/debugdump_2d.rs @@ -0,0 +1,18 @@ +//! Run with: +//! `cargo run --example debugdump_2d > dump.dot && dot -Tsvg dump.dot > dump.svg` + +use avian2d::prelude::*; +use bevy::prelude::*; + +fn main() { + let mut app = App::new(); + + app.add_plugins((PhysicsPlugins::default(), PhysicsDebugPlugin::default())); + + // Schedules of interest: + // - PhysicsSchedule + // - SubstepSchedule + // - FixedPostUpdate + // - Update + bevy_mod_debugdump::print_schedule_graph(&mut app, PhysicsSchedule); +} diff --git a/crates/avian3d/Cargo.toml b/crates/avian3d/Cargo.toml index b451f408..c7c53432 100644 --- a/crates/avian3d/Cargo.toml +++ b/crates/avian3d/Cargo.toml @@ -82,7 +82,7 @@ bevy_math = { version = "0.14", features = ["approx"] } approx = "0.5" criterion = { version = "0.5", features = ["html_reports"] } insta = "1.0" - +bevy_mod_debugdump = "0.11" [[example]] name = "dynamic_character_3d" @@ -136,6 +136,10 @@ required-features = ["3d", "default-collider", "bevy_scene"] name = "collider_constructors" required-features = ["3d", "default-collider", "bevy_scene"] +[[example]] +name = "debugdump_3d" +required-features = ["3d"] + [[bench]] name = "cubes" required-features = ["3d", "default-collider"] diff --git a/crates/avian3d/examples/debugdump_3d.rs b/crates/avian3d/examples/debugdump_3d.rs new file mode 100644 index 00000000..acdc652e --- /dev/null +++ b/crates/avian3d/examples/debugdump_3d.rs @@ -0,0 +1,19 @@ +//! Run with: +//! `cargo run --example debugdump_3d > dump.dot && dot -Tsvg dump.dot > dump.svg` + +use avian3d::debug_render::PhysicsDebugPlugin; +use avian3d::prelude::*; +use bevy::prelude::*; + +fn main() { + let mut app = App::new(); + + app.add_plugins((PhysicsPlugins::default(), PhysicsDebugPlugin::default())); + + // Schedules of interest: + // - PhysicsSchedule + // - SubstepSchedule + // - FixedPostUpdate + // - Update + bevy_mod_debugdump::print_schedule_graph(&mut app, PhysicsSchedule); +} diff --git a/crates/examples_common_2d/Cargo.toml b/crates/examples_common_2d/Cargo.toml index f905cd0c..f3abb2eb 100644 --- a/crates/examples_common_2d/Cargo.toml +++ b/crates/examples_common_2d/Cargo.toml @@ -22,6 +22,6 @@ bevy = { version = "0.14", default-features = false, features = [ "ktx2", "zstd", "bevy_winit", - "x11", # github actions runners don't have libxkbcommon installed, so can't use wayland + "x11", # github actions runners don't have libxkbcommon installed, so can't use wayland ] } avian2d = { path = "../avian2d", default-features = false } diff --git a/crates/examples_common_3d/Cargo.toml b/crates/examples_common_3d/Cargo.toml index 85e38efb..b7109d58 100644 --- a/crates/examples_common_3d/Cargo.toml +++ b/crates/examples_common_3d/Cargo.toml @@ -24,6 +24,6 @@ bevy = { version = "0.14", default-features = false, features = [ "png", "zstd", "bevy_winit", - "x11", # github actions runners don't have libxkbcommon installed, so can't use wayland + "x11", # github actions runners don't have libxkbcommon installed, so can't use wayland ] } avian3d = { path = "../avian3d", default-features = false } From d79bb13f4d5a92a26d2a798f706429f60f96a521 Mon Sep 17 00:00:00 2001 From: Joona Aalto Date: Mon, 23 Sep 2024 01:46:46 +0300 Subject: [PATCH 06/36] Fix 3D rotation update in `transform_to_position` (#520) # Objective Fixes #516. Rotation is currently updated incorrectly in 3D by `transform_to_position`. It uses addition and subtraction instead of multiplication for quaternions. This causes clear desync between `Transform` rotation and `Rotation` when `SyncConfig::position_to_transform` is `false` and the `Transform` is changed: https://github.com/user-attachments/assets/5b51480e-6f55-4317-9ed1-ec02f9c4d640 ## Solution Fix the rotation update. We can also remove the normalization, because individual quaternion multiplications should remain normalized, assuming the inputs are normalized (and there aren't too many successive rotations). Now, the desync is fixed: https://github.com/user-attachments/assets/8922832f-21b4-4ed9-947d-bd4b77786647 --- src/sync/mod.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/sync/mod.rs b/src/sync/mod.rs index ca5579e2..1bed6cc5 100644 --- a/src/sync/mod.rs +++ b/src/sync/mod.rs @@ -263,10 +263,9 @@ pub fn transform_to_position( #[cfg(feature = "3d")] { rotation.0 = (previous_transform.rotation - + (transform.rotation - previous_transform.rotation) - + (rotation.f32() - previous_transform.rotation)) - .normalize() - .adjust_precision(); + * (transform.rotation * previous_transform.rotation.inverse()) + * (rotation.f32() * previous_transform.rotation.inverse())) + .adjust_precision(); } } } From 68e2ba780cb50f4ddfbae1a097d381f3f47914ad Mon Sep 17 00:00:00 2001 From: Shane Celis Date: Thu, 26 Sep 2024 07:04:02 -0400 Subject: [PATCH 07/36] Make disabling joints possible. (#519) --- src/dynamics/solver/joints/mod.rs | 7 +++++++ src/dynamics/solver/xpbd/mod.rs | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/dynamics/solver/joints/mod.rs b/src/dynamics/solver/joints/mod.rs index 6fa37350..c6ae9e6c 100644 --- a/src/dynamics/solver/joints/mod.rs +++ b/src/dynamics/solver/joints/mod.rs @@ -338,3 +338,10 @@ impl AngleLimit { None } } + +/// Disables the joint of the entity it is placed on. +#[derive(Reflect, Clone, Copy, Component, Debug)] +#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serialize", reflect(Serialize, Deserialize))] +#[reflect(Debug, Component)] +pub struct JointDisabled; diff --git a/src/dynamics/solver/xpbd/mod.rs b/src/dynamics/solver/xpbd/mod.rs index be7539cf..37077865 100644 --- a/src/dynamics/solver/xpbd/mod.rs +++ b/src/dynamics/solver/xpbd/mod.rs @@ -351,7 +351,7 @@ pub trait XpbdConstraint: MapEntities { pub fn solve_constraint + Component, const ENTITY_COUNT: usize>( mut commands: Commands, mut bodies: Query, - mut constraints: Query<&mut C, Without>, + mut constraints: Query<&mut C, (Without, Without)>, time: Res