using System.Collections.Generic; using FarseerPhysics.Collision; using FarseerPhysics.Collision.Shapes; using FarseerPhysics.Dynamics; using Microsoft.Xna.Framework; namespace FarseerPhysics.Controllers { public sealed class BuoyancyController : Controller { /// /// 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 /// simulate water-like fluids. /// public float AngularDragCoefficient; /// /// Density of the fluid. Higher values will make things more buoyant, lower values will cause things to sink. /// public float Density; /// /// 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 /// simulate water-like fluids. /// public float LinearDragCoefficient; /// /// Acts like waterflow. Defaults to 0,0. /// public Vector2 Velocity; private AABB _container; private Vector2 _gravity; private Vector2 _normal; private float _offset; private Dictionary _uniqueBodies = new Dictionary(); /// /// Initializes a new instance of the class. /// /// Only bodies inside this AABB will be influenced by the controller /// Density of the fluid /// Linear drag coefficient of the fluid /// Rotational drag coefficient of the fluid /// The direction gravity acts. Buoyancy force will act in opposite direction of gravity. public BuoyancyController(AABB container, float density, float linearDragCoefficient, float rotationalDragCoefficient, Vector2 gravity) : base(ControllerType.BuoyancyController) { Container = container; _normal = new Vector2(0, 1); Density = density; LinearDragCoefficient = linearDragCoefficient; AngularDragCoefficient = rotationalDragCoefficient; _gravity = gravity; } public AABB Container { get { return _container; } set { _container = value; _offset = _container.UpperBound.Y; } } public override void Update(float dt) { _uniqueBodies.Clear(); World.QueryAABB(fixture => { if (fixture.Body.IsStatic || !fixture.Body.Awake) return true; if (!_uniqueBodies.ContainsKey(fixture.Body.BodyId)) _uniqueBodies.Add(fixture.Body.BodyId, fixture.Body); return true; }, ref _container); foreach (KeyValuePair kv in _uniqueBodies) { Body body = kv.Value; Vector2 areac = Vector2.Zero; Vector2 massc = Vector2.Zero; float area = 0; float mass = 0; for (int j = 0; j < body.FixtureList.Count; j++) { Fixture fixture = body.FixtureList[j]; if (fixture.Shape.ShapeType != ShapeType.Polygon && fixture.Shape.ShapeType != ShapeType.Circle) continue; Shape shape = fixture.Shape; Vector2 sc; float sarea = shape.ComputeSubmergedArea(_normal, _offset, body.Xf, out sc); area += sarea; areac.X += sarea * sc.X; areac.Y += sarea * sc.Y; mass += sarea * shape.Density; massc.X += sarea * sc.X * shape.Density; massc.Y += sarea * sc.Y * shape.Density; } areac.X /= area; areac.Y /= area; massc.X /= mass; massc.Y /= mass; if (area < Settings.Epsilon) continue; //Buoyancy Vector2 buoyancyForce = -Density * area * _gravity; body.ApplyForce(buoyancyForce, massc); //Linear drag Vector2 dragForce = body.GetLinearVelocityFromWorldPoint(areac) - Velocity; dragForce *= -LinearDragCoefficient * area; body.ApplyForce(dragForce, areac); //Angular drag body.ApplyTorque(-body.Inertia / body.Mass * area * body.AngularVelocity * AngularDragCoefficient); } } } }