diff options
author | Robert Adams | 2013-01-20 22:35:42 -0800 |
---|---|---|
committer | Robert Adams | 2013-01-20 23:09:54 -0800 |
commit | 52b341e2e24384395fddc7d32fd66358f5062468 (patch) | |
tree | a0b28ec5b79004b1a556db78d835e486b8040632 /OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs | |
parent | BulletSim: fix problem of avatar sliding very slowly occasionally after stopp... (diff) | |
download | opensim-SC-52b341e2e24384395fddc7d32fd66358f5062468.zip opensim-SC-52b341e2e24384395fddc7d32fd66358f5062468.tar.gz opensim-SC-52b341e2e24384395fddc7d32fd66358f5062468.tar.bz2 opensim-SC-52b341e2e24384395fddc7d32fd66358f5062468.tar.xz |
BulletSim: More aggressive as setting character velocity to zero
when should be standing.
Modify angular force routines to be the same pattern as linear force routines.
BulletSim vehicle turning is scaled like SL and is DIFFERENT THAN ODE!!
Fix some bugs in BSMotor dealing with the motor going to zero.
Add a bunch of parameters: MaxLinearVelocity, MaxAngularVelocity,
MaxAddForceMagnitude, VehicleMaxLinearVelocity, VehicleMaxAngularVelocity,
and most of the values are defaulted to values that are larger
than in SL.
Use the new parameters in BSPrim, BSCharacter and BSDynamic.
Diffstat (limited to '')
-rw-r--r-- | OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs | 143 |
1 files changed, 71 insertions, 72 deletions
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs b/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs index 388d4f9..f8fc3de 100644 --- a/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs | |||
@@ -231,6 +231,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin | |||
231 | break; | 231 | break; |
232 | case Vehicle.ANGULAR_MOTOR_DIRECTION: | 232 | case Vehicle.ANGULAR_MOTOR_DIRECTION: |
233 | m_angularMotorDirection = new Vector3(pValue, pValue, pValue); | 233 | m_angularMotorDirection = new Vector3(pValue, pValue, pValue); |
234 | m_angularMotor.Zero(); | ||
234 | m_angularMotor.SetTarget(m_angularMotorDirection); | 235 | m_angularMotor.SetTarget(m_angularMotorDirection); |
235 | break; | 236 | break; |
236 | case Vehicle.LINEAR_FRICTION_TIMESCALE: | 237 | case Vehicle.LINEAR_FRICTION_TIMESCALE: |
@@ -264,6 +265,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin | |||
264 | pValue.Y = ClampInRange(-12.56f, pValue.Y, 12.56f); | 265 | pValue.Y = ClampInRange(-12.56f, pValue.Y, 12.56f); |
265 | pValue.Z = ClampInRange(-12.56f, pValue.Z, 12.56f); | 266 | pValue.Z = ClampInRange(-12.56f, pValue.Z, 12.56f); |
266 | m_angularMotorDirection = new Vector3(pValue.X, pValue.Y, pValue.Z); | 267 | m_angularMotorDirection = new Vector3(pValue.X, pValue.Y, pValue.Z); |
268 | m_angularMotor.Zero(); | ||
267 | m_angularMotor.SetTarget(m_angularMotorDirection); | 269 | m_angularMotor.SetTarget(m_angularMotorDirection); |
268 | break; | 270 | break; |
269 | case Vehicle.LINEAR_FRICTION_TIMESCALE: | 271 | case Vehicle.LINEAR_FRICTION_TIMESCALE: |
@@ -945,10 +947,10 @@ namespace OpenSim.Region.Physics.BulletSPlugin | |||
945 | // ================================================================== | 947 | // ================================================================== |
946 | // Clamp high or low velocities | 948 | // Clamp high or low velocities |
947 | float newVelocityLengthSq = VehicleVelocity.LengthSquared(); | 949 | float newVelocityLengthSq = VehicleVelocity.LengthSquared(); |
948 | if (newVelocityLengthSq > 1000f) | 950 | if (newVelocityLengthSq > BSParam.VehicleMaxLinearVelocity) |
949 | { | 951 | { |
950 | VehicleVelocity /= VehicleVelocity.Length(); | 952 | VehicleVelocity /= VehicleVelocity.Length(); |
951 | VehicleVelocity *= 1000f; | 953 | VehicleVelocity *= BSParam.VehicleMaxLinearVelocity; |
952 | } | 954 | } |
953 | else if (newVelocityLengthSq < 0.001f) | 955 | else if (newVelocityLengthSq < 0.001f) |
954 | VehicleVelocity = Vector3.Zero; | 956 | VehicleVelocity = Vector3.Zero; |
@@ -1190,63 +1192,33 @@ namespace OpenSim.Region.Physics.BulletSPlugin | |||
1190 | // set directly on the vehicle. | 1192 | // set directly on the vehicle. |
1191 | private void MoveAngular(float pTimestep) | 1193 | private void MoveAngular(float pTimestep) |
1192 | { | 1194 | { |
1193 | // The user wants this many radians per second angular change? | 1195 | VehicleRotationalVelocity = Vector3.Zero; |
1194 | Vector3 angularMotorContribution = m_angularMotor.Step(pTimestep); | ||
1195 | angularMotorContribution = m_angularMotor.CurrentValue; | ||
1196 | 1196 | ||
1197 | // ================================================================== | 1197 | ComputeAngularTurning(pTimestep); |
1198 | // From http://wiki.secondlife.com/wiki/LlSetVehicleFlags : | ||
1199 | // This flag prevents linear deflection parallel to world z-axis. This is useful | ||
1200 | // for preventing ground vehicles with large linear deflection, like bumper cars, | ||
1201 | // from climbing their linear deflection into the sky. | ||
1202 | // That is, NO_DEFLECTION_UP says angular motion should not add any pitch or roll movement | ||
1203 | // TODO: This is here because this is where ODE put it but documentation says it | ||
1204 | // is a linear effect. Where should this check go? | ||
1205 | if ((m_flags & (VehicleFlag.NO_DEFLECTION_UP)) != 0) | ||
1206 | { | ||
1207 | angularMotorContribution.X = 0f; | ||
1208 | angularMotorContribution.Y = 0f; | ||
1209 | VDetailLog("{0}, MoveAngular,noDeflectionUp,angularMotorContrib={1}", Prim.LocalID, angularMotorContribution); | ||
1210 | } | ||
1211 | 1198 | ||
1212 | Vector3 verticalAttractionContribution = ComputeAngularVerticalAttraction(); | 1199 | ComputeAngularVerticalAttraction(); |
1213 | 1200 | ||
1214 | Vector3 deflectionContribution = ComputeAngularDeflection(); | 1201 | ComputeAngularDeflection(); |
1215 | 1202 | ||
1216 | Vector3 bankingContribution = ComputeAngularBanking(); | 1203 | ComputeAngularBanking(); |
1217 | 1204 | ||
1218 | // ================================================================== | 1205 | // ================================================================== |
1219 | m_lastVertAttractor = verticalAttractionContribution; | ||
1220 | |||
1221 | m_lastAngularVelocity = angularMotorContribution | ||
1222 | + verticalAttractionContribution | ||
1223 | + deflectionContribution | ||
1224 | + bankingContribution; | ||
1225 | |||
1226 | // All of the above computation are made relative to vehicle coordinates. | 1206 | // All of the above computation are made relative to vehicle coordinates. |
1227 | // Convert to world coordinates. | 1207 | // Convert to world coordinates. |
1228 | m_lastAngularVelocity *= VehicleOrientation; | 1208 | // TODO: Should this be applied as an angular force (torque)? |
1209 | VehicleRotationalVelocity *= VehicleOrientation; | ||
1229 | 1210 | ||
1230 | // ================================================================== | 1211 | // ================================================================== |
1231 | // Apply the correction velocity. | 1212 | if (VehicleRotationalVelocity.ApproxEquals(Vector3.Zero, 0.01f)) |
1232 | // TODO: Should this be applied as an angular force (torque)? | ||
1233 | if (!m_lastAngularVelocity.ApproxEquals(Vector3.Zero, 0.01f)) | ||
1234 | { | ||
1235 | VehicleRotationalVelocity = m_lastAngularVelocity; | ||
1236 | |||
1237 | VDetailLog("{0}, MoveAngular,done,nonZero,angMotorContrib={1},vertAttrContrib={2},bankContrib={3},deflectContrib={4},totalContrib={5}", | ||
1238 | Prim.LocalID, | ||
1239 | angularMotorContribution, verticalAttractionContribution, | ||
1240 | bankingContribution, deflectionContribution, | ||
1241 | m_lastAngularVelocity | ||
1242 | ); | ||
1243 | } | ||
1244 | else | ||
1245 | { | 1213 | { |
1246 | // The vehicle is not adding anything angular wise. | 1214 | // The vehicle is not adding anything angular wise. |
1247 | VehicleRotationalVelocity = Vector3.Zero; | 1215 | VehicleRotationalVelocity = Vector3.Zero; |
1248 | VDetailLog("{0}, MoveAngular,done,zero", Prim.LocalID); | 1216 | VDetailLog("{0}, MoveAngular,done,zero", Prim.LocalID); |
1249 | } | 1217 | } |
1218 | else | ||
1219 | { | ||
1220 | VDetailLog("{0}, MoveAngular,done,nonZero,angVel={1}", Prim.LocalID, VehicleRotationalVelocity); | ||
1221 | } | ||
1250 | 1222 | ||
1251 | // ================================================================== | 1223 | // ================================================================== |
1252 | //Offset section | 1224 | //Offset section |
@@ -1280,6 +1252,30 @@ namespace OpenSim.Region.Physics.BulletSPlugin | |||
1280 | } | 1252 | } |
1281 | 1253 | ||
1282 | } | 1254 | } |
1255 | |||
1256 | private void ComputeAngularTurning(float pTimestep) | ||
1257 | { | ||
1258 | // The user wants this many radians per second angular change? | ||
1259 | Vector3 angularMotorContribution = m_angularMotor.Step(pTimestep); | ||
1260 | |||
1261 | // ================================================================== | ||
1262 | // From http://wiki.secondlife.com/wiki/LlSetVehicleFlags : | ||
1263 | // This flag prevents linear deflection parallel to world z-axis. This is useful | ||
1264 | // for preventing ground vehicles with large linear deflection, like bumper cars, | ||
1265 | // from climbing their linear deflection into the sky. | ||
1266 | // That is, NO_DEFLECTION_UP says angular motion should not add any pitch or roll movement | ||
1267 | // TODO: This is here because this is where ODE put it but documentation says it | ||
1268 | // is a linear effect. Where should this check go? | ||
1269 | if ((m_flags & (VehicleFlag.NO_DEFLECTION_UP)) != 0) | ||
1270 | { | ||
1271 | angularMotorContribution.X = 0f; | ||
1272 | angularMotorContribution.Y = 0f; | ||
1273 | } | ||
1274 | |||
1275 | VehicleRotationalVelocity += angularMotorContribution; | ||
1276 | VDetailLog("{0}, MoveAngular,angularTurning,angularMotorContrib={1}", Prim.LocalID, angularMotorContribution); | ||
1277 | } | ||
1278 | |||
1283 | // From http://wiki.secondlife.com/wiki/Linden_Vehicle_Tutorial: | 1279 | // From http://wiki.secondlife.com/wiki/Linden_Vehicle_Tutorial: |
1284 | // Some vehicles, like boats, should always keep their up-side up. This can be done by | 1280 | // Some vehicles, like boats, should always keep their up-side up. This can be done by |
1285 | // enabling the "vertical attractor" behavior that springs the vehicle's local z-axis to | 1281 | // enabling the "vertical attractor" behavior that springs the vehicle's local z-axis to |
@@ -1288,13 +1284,13 @@ namespace OpenSim.Region.Physics.BulletSPlugin | |||
1288 | // and then set the VEHICLE_VERTICAL_ATTRACTION_EFFICIENCY to control the damping. An | 1284 | // and then set the VEHICLE_VERTICAL_ATTRACTION_EFFICIENCY to control the damping. An |
1289 | // efficiency of 0.0 will cause the spring to wobble around its equilibrium, while an | 1285 | // efficiency of 0.0 will cause the spring to wobble around its equilibrium, while an |
1290 | // efficiency of 1.0 will cause the spring to reach its equilibrium with exponential decay. | 1286 | // efficiency of 1.0 will cause the spring to reach its equilibrium with exponential decay. |
1291 | public Vector3 ComputeAngularVerticalAttraction() | 1287 | public void ComputeAngularVerticalAttraction() |
1292 | { | 1288 | { |
1293 | Vector3 ret = Vector3.Zero; | ||
1294 | |||
1295 | // If vertical attaction timescale is reasonable | 1289 | // If vertical attaction timescale is reasonable |
1296 | if (enableAngularVerticalAttraction && m_verticalAttractionTimescale < m_verticalAttractionCutoff) | 1290 | if (enableAngularVerticalAttraction && m_verticalAttractionTimescale < m_verticalAttractionCutoff) |
1297 | { | 1291 | { |
1292 | Vector3 vertContribution = Vector3.Zero; | ||
1293 | |||
1298 | // Take a vector pointing up and convert it from world to vehicle relative coords. | 1294 | // Take a vector pointing up and convert it from world to vehicle relative coords. |
1299 | Vector3 verticalError = Vector3.UnitZ * VehicleOrientation; | 1295 | Vector3 verticalError = Vector3.UnitZ * VehicleOrientation; |
1300 | 1296 | ||
@@ -1308,37 +1304,36 @@ namespace OpenSim.Region.Physics.BulletSPlugin | |||
1308 | 1304 | ||
1309 | // Y error means needed rotation around X axis and visa versa. | 1305 | // Y error means needed rotation around X axis and visa versa. |
1310 | // Since the error goes from zero to one, the asin is the corresponding angle. | 1306 | // Since the error goes from zero to one, the asin is the corresponding angle. |
1311 | ret.X = (float)Math.Asin(verticalError.Y); | 1307 | vertContribution.X = (float)Math.Asin(verticalError.Y); |
1312 | // (Tilt forward (positive X) needs to tilt back (rotate negative) around Y axis.) | 1308 | // (Tilt forward (positive X) needs to tilt back (rotate negative) around Y axis.) |
1313 | ret.Y = -(float)Math.Asin(verticalError.X); | 1309 | vertContribution.Y = -(float)Math.Asin(verticalError.X); |
1314 | 1310 | ||
1315 | // If verticalError.Z is negative, the vehicle is upside down. Add additional push. | 1311 | // If verticalError.Z is negative, the vehicle is upside down. Add additional push. |
1316 | if (verticalError.Z < 0f) | 1312 | if (verticalError.Z < 0f) |
1317 | { | 1313 | { |
1318 | ret.X += PIOverFour; | 1314 | vertContribution.X += PIOverFour; |
1319 | ret.Y += PIOverFour; | 1315 | vertContribution.Y += PIOverFour; |
1320 | } | 1316 | } |
1321 | 1317 | ||
1322 | // 'ret' is now the necessary velocity to correct tilt in one second. | 1318 | // 'vertContrbution' is now the necessary angular correction to correct tilt in one second. |
1323 | // Correction happens over a number of seconds. | 1319 | // Correction happens over a number of seconds. |
1324 | Vector3 unscaledContrib = ret; | 1320 | Vector3 unscaledContrib = vertContribution; // DEBUG DEBUG |
1325 | ret /= m_verticalAttractionTimescale; | 1321 | vertContribution /= m_verticalAttractionTimescale; |
1322 | |||
1323 | VehicleRotationalVelocity += vertContribution; | ||
1326 | 1324 | ||
1327 | VDetailLog("{0}, MoveAngular,verticalAttraction,,verticalError={1},unscaled={2},eff={3},ts={4},vertAttr={5}", | 1325 | VDetailLog("{0}, MoveAngular,verticalAttraction,,verticalError={1},unscaled={2},eff={3},ts={4},vertAttr={5}", |
1328 | Prim.LocalID, verticalError, unscaledContrib, m_verticalAttractionEfficiency, m_verticalAttractionTimescale, ret); | 1326 | Prim.LocalID, verticalError, unscaledContrib, m_verticalAttractionEfficiency, m_verticalAttractionTimescale, vertContribution); |
1329 | } | 1327 | } |
1330 | return ret; | ||
1331 | } | 1328 | } |
1332 | 1329 | ||
1333 | // Return the angular correction to correct the direction the vehicle is pointing to be | 1330 | // Angular correction to correct the direction the vehicle is pointing to be |
1334 | // the direction is should want to be pointing. | 1331 | // the direction is should want to be pointing. |
1335 | // The vehicle is moving in some direction and correct its orientation to it is pointing | 1332 | // The vehicle is moving in some direction and correct its orientation to it is pointing |
1336 | // in that direction. | 1333 | // in that direction. |
1337 | // TODO: implement reference frame. | 1334 | // TODO: implement reference frame. |
1338 | public Vector3 ComputeAngularDeflection() | 1335 | public void ComputeAngularDeflection() |
1339 | { | 1336 | { |
1340 | Vector3 ret = Vector3.Zero; | ||
1341 | |||
1342 | // Since angularMotorUp and angularDeflection are computed independently, they will calculate | 1337 | // Since angularMotorUp and angularDeflection are computed independently, they will calculate |
1343 | // approximately the same X or Y correction. When added together (when contributions are combined) | 1338 | // approximately the same X or Y correction. When added together (when contributions are combined) |
1344 | // this creates an over-correction and then wabbling as the target is overshot. | 1339 | // this creates an over-correction and then wabbling as the target is overshot. |
@@ -1346,6 +1341,8 @@ namespace OpenSim.Region.Physics.BulletSPlugin | |||
1346 | 1341 | ||
1347 | if (enableAngularDeflection && m_angularDeflectionEfficiency != 0 && VehicleForwardSpeed > 0.2) | 1342 | if (enableAngularDeflection && m_angularDeflectionEfficiency != 0 && VehicleForwardSpeed > 0.2) |
1348 | { | 1343 | { |
1344 | Vector3 deflectContribution = Vector3.Zero; | ||
1345 | |||
1349 | // The direction the vehicle is moving | 1346 | // The direction the vehicle is moving |
1350 | Vector3 movingDirection = VehicleVelocity; | 1347 | Vector3 movingDirection = VehicleVelocity; |
1351 | movingDirection.Normalize(); | 1348 | movingDirection.Normalize(); |
@@ -1371,18 +1368,19 @@ namespace OpenSim.Region.Physics.BulletSPlugin | |||
1371 | // ret = m_angularDeflectionCorrectionMotor(1f, deflectionError); | 1368 | // ret = m_angularDeflectionCorrectionMotor(1f, deflectionError); |
1372 | 1369 | ||
1373 | // Scale the correction by recovery timescale and efficiency | 1370 | // Scale the correction by recovery timescale and efficiency |
1374 | ret = (-deflectionError) * m_angularDeflectionEfficiency; | 1371 | deflectContribution = (-deflectionError) * m_angularDeflectionEfficiency; |
1375 | ret /= m_angularDeflectionTimescale; | 1372 | deflectContribution /= m_angularDeflectionTimescale; |
1373 | |||
1374 | VehicleRotationalVelocity += deflectContribution; | ||
1376 | 1375 | ||
1377 | VDetailLog("{0}, MoveAngular,Deflection,movingDir={1},pointingDir={2},deflectError={3},ret={4}", | 1376 | VDetailLog("{0}, MoveAngular,Deflection,movingDir={1},pointingDir={2},deflectError={3},ret={4}", |
1378 | Prim.LocalID, movingDirection, pointingDirection, deflectionError, ret); | 1377 | Prim.LocalID, movingDirection, pointingDirection, deflectionError, deflectContribution); |
1379 | VDetailLog("{0}, MoveAngular,Deflection,fwdSpd={1},defEff={2},defTS={3}", | 1378 | VDetailLog("{0}, MoveAngular,Deflection,fwdSpd={1},defEff={2},defTS={3}", |
1380 | Prim.LocalID, VehicleForwardSpeed, m_angularDeflectionEfficiency, m_angularDeflectionTimescale); | 1379 | Prim.LocalID, VehicleForwardSpeed, m_angularDeflectionEfficiency, m_angularDeflectionTimescale); |
1381 | } | 1380 | } |
1382 | return ret; | ||
1383 | } | 1381 | } |
1384 | 1382 | ||
1385 | // Return an angular change to rotate the vehicle around the Z axis when the vehicle | 1383 | // Angular change to rotate the vehicle around the Z axis when the vehicle |
1386 | // is tipped around the X axis. | 1384 | // is tipped around the X axis. |
1387 | // From http://wiki.secondlife.com/wiki/Linden_Vehicle_Tutorial: | 1385 | // From http://wiki.secondlife.com/wiki/Linden_Vehicle_Tutorial: |
1388 | // The vertical attractor feature must be enabled in order for the banking behavior to | 1386 | // The vertical attractor feature must be enabled in order for the banking behavior to |
@@ -1413,12 +1411,12 @@ namespace OpenSim.Region.Physics.BulletSPlugin | |||
1413 | // world z-axis is determined by the VEHICLE_BANKING_TIMESCALE. So if you want the vehicle to | 1411 | // world z-axis is determined by the VEHICLE_BANKING_TIMESCALE. So if you want the vehicle to |
1414 | // bank quickly then give it a banking timescale of about a second or less, otherwise you can | 1412 | // bank quickly then give it a banking timescale of about a second or less, otherwise you can |
1415 | // make a sluggish vehicle by giving it a timescale of several seconds. | 1413 | // make a sluggish vehicle by giving it a timescale of several seconds. |
1416 | public Vector3 ComputeAngularBanking() | 1414 | public void ComputeAngularBanking() |
1417 | { | 1415 | { |
1418 | Vector3 ret = Vector3.Zero; | ||
1419 | |||
1420 | if (enableAngularBanking && m_bankingEfficiency != 0 && m_verticalAttractionTimescale < m_verticalAttractionCutoff) | 1416 | if (enableAngularBanking && m_bankingEfficiency != 0 && m_verticalAttractionTimescale < m_verticalAttractionCutoff) |
1421 | { | 1417 | { |
1418 | Vector3 bankingContribution = Vector3.Zero; | ||
1419 | |||
1422 | // Rotate a UnitZ vector (pointing up) to how the vehicle is oriented. | 1420 | // Rotate a UnitZ vector (pointing up) to how the vehicle is oriented. |
1423 | // As the vehicle rolls to the right or left, the Y value will increase from | 1421 | // As the vehicle rolls to the right or left, the Y value will increase from |
1424 | // zero (straight up) to 1 or -1 (full tilt right or left) | 1422 | // zero (straight up) to 1 or -1 (full tilt right or left) |
@@ -1435,15 +1433,16 @@ namespace OpenSim.Region.Physics.BulletSPlugin | |||
1435 | mixedYawAngle = ClampInRange(-20f, mixedYawAngle, 20f); | 1433 | mixedYawAngle = ClampInRange(-20f, mixedYawAngle, 20f); |
1436 | 1434 | ||
1437 | // Build the force vector to change rotation from what it is to what it should be | 1435 | // Build the force vector to change rotation from what it is to what it should be |
1438 | ret.Z = -mixedYawAngle; | 1436 | bankingContribution.Z = -mixedYawAngle; |
1439 | 1437 | ||
1440 | // Don't do it all at once. | 1438 | // Don't do it all at once. |
1441 | ret /= m_bankingTimescale; | 1439 | bankingContribution /= m_bankingTimescale; |
1440 | |||
1441 | VehicleRotationalVelocity += bankingContribution; | ||
1442 | 1442 | ||
1443 | VDetailLog("{0}, MoveAngular,Banking,rollComp={1},speed={2},rollComp={3},yAng={4},mYAng={5},ret={6}", | 1443 | VDetailLog("{0}, MoveAngular,Banking,rollComp={1},speed={2},rollComp={3},yAng={4},mYAng={5},ret={6}", |
1444 | Prim.LocalID, rollComponents, VehicleForwardSpeed, rollComponents, yawAngle, mixedYawAngle, ret); | 1444 | Prim.LocalID, rollComponents, VehicleForwardSpeed, rollComponents, yawAngle, mixedYawAngle, bankingContribution); |
1445 | } | 1445 | } |
1446 | return ret; | ||
1447 | } | 1446 | } |
1448 | 1447 | ||
1449 | // This is from previous instantiations of XXXDynamics.cs. | 1448 | // This is from previous instantiations of XXXDynamics.cs. |