2
0

BuoyancyController.cs 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. using System.Collections.Generic;
  2. using FarseerPhysics.Collision;
  3. using FarseerPhysics.Collision.Shapes;
  4. using FarseerPhysics.Dynamics;
  5. using Microsoft.Xna.Framework;
  6. namespace FarseerPhysics.Controllers
  7. {
  8. public sealed class BuoyancyController : Controller
  9. {
  10. /// <summary>
  11. /// Controls the rotational drag that the fluid exerts on the bodies within it. Use higher values will simulate thick fluid, like honey, lower values to
  12. /// simulate water-like fluids.
  13. /// </summary>
  14. public float AngularDragCoefficient;
  15. /// <summary>
  16. /// Density of the fluid. Higher values will make things more buoyant, lower values will cause things to sink.
  17. /// </summary>
  18. public float Density;
  19. /// <summary>
  20. /// Controls the linear drag that the fluid exerts on the bodies within it. Use higher values will simulate thick fluid, like honey, lower values to
  21. /// simulate water-like fluids.
  22. /// </summary>
  23. public float LinearDragCoefficient;
  24. /// <summary>
  25. /// Acts like waterflow. Defaults to 0,0.
  26. /// </summary>
  27. public Vector2 Velocity;
  28. private AABB _container;
  29. private Vector2 _gravity;
  30. private Vector2 _normal;
  31. private float _offset;
  32. private Dictionary<int, Body> _uniqueBodies = new Dictionary<int, Body>();
  33. /// <summary>
  34. /// Initializes a new instance of the <see cref="BuoyancyController"/> class.
  35. /// </summary>
  36. /// <param name="container">Only bodies inside this AABB will be influenced by the controller</param>
  37. /// <param name="density">Density of the fluid</param>
  38. /// <param name="linearDragCoefficient">Linear drag coefficient of the fluid</param>
  39. /// <param name="rotationalDragCoefficient">Rotational drag coefficient of the fluid</param>
  40. /// <param name="gravity">The direction gravity acts. Buoyancy force will act in opposite direction of gravity.</param>
  41. public BuoyancyController(AABB container, float density, float linearDragCoefficient,
  42. float rotationalDragCoefficient, Vector2 gravity)
  43. : base(ControllerType.BuoyancyController)
  44. {
  45. Container = container;
  46. _normal = new Vector2(0, 1);
  47. Density = density;
  48. LinearDragCoefficient = linearDragCoefficient;
  49. AngularDragCoefficient = rotationalDragCoefficient;
  50. _gravity = gravity;
  51. }
  52. public AABB Container
  53. {
  54. get { return _container; }
  55. set
  56. {
  57. _container = value;
  58. _offset = _container.UpperBound.Y;
  59. }
  60. }
  61. public override void Update(float dt)
  62. {
  63. _uniqueBodies.Clear();
  64. World.QueryAABB(fixture =>
  65. {
  66. if (fixture.Body.IsStatic || !fixture.Body.Awake)
  67. return true;
  68. if (!_uniqueBodies.ContainsKey(fixture.Body.BodyId))
  69. _uniqueBodies.Add(fixture.Body.BodyId, fixture.Body);
  70. return true;
  71. }, ref _container);
  72. foreach (KeyValuePair<int, Body> kv in _uniqueBodies)
  73. {
  74. Body body = kv.Value;
  75. Vector2 areac = Vector2.Zero;
  76. Vector2 massc = Vector2.Zero;
  77. float area = 0;
  78. float mass = 0;
  79. for (int j = 0; j < body.FixtureList.Count; j++)
  80. {
  81. Fixture fixture = body.FixtureList[j];
  82. if (fixture.Shape.ShapeType != ShapeType.Polygon && fixture.Shape.ShapeType != ShapeType.Circle)
  83. continue;
  84. Shape shape = fixture.Shape;
  85. Vector2 sc;
  86. float sarea = shape.ComputeSubmergedArea(_normal, _offset, body.Xf, out sc);
  87. area += sarea;
  88. areac.X += sarea * sc.X;
  89. areac.Y += sarea * sc.Y;
  90. mass += sarea * shape.Density;
  91. massc.X += sarea * sc.X * shape.Density;
  92. massc.Y += sarea * sc.Y * shape.Density;
  93. }
  94. areac.X /= area;
  95. areac.Y /= area;
  96. massc.X /= mass;
  97. massc.Y /= mass;
  98. if (area < Settings.Epsilon)
  99. continue;
  100. //Buoyancy
  101. Vector2 buoyancyForce = -Density * area * _gravity;
  102. body.ApplyForce(buoyancyForce, massc);
  103. //Linear drag
  104. Vector2 dragForce = body.GetLinearVelocityFromWorldPoint(areac) - Velocity;
  105. dragForce *= -LinearDragCoefficient * area;
  106. body.ApplyForce(dragForce, areac);
  107. //Angular drag
  108. body.ApplyTorque(-body.Inertia / body.Mass * area * body.AngularVelocity * AngularDragCoefficient);
  109. }
  110. }
  111. }
  112. }