123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135 |
- 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
- {
- /// <summary>
- /// 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.
- /// </summary>
- public float AngularDragCoefficient;
- /// <summary>
- /// Density of the fluid. Higher values will make things more buoyant, lower values will cause things to sink.
- /// </summary>
- public float Density;
- /// <summary>
- /// 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.
- /// </summary>
- public float LinearDragCoefficient;
- /// <summary>
- /// Acts like waterflow. Defaults to 0,0.
- /// </summary>
- public Vector2 Velocity;
- private AABB _container;
- private Vector2 _gravity;
- private Vector2 _normal;
- private float _offset;
- private Dictionary<int, Body> _uniqueBodies = new Dictionary<int, Body>();
- /// <summary>
- /// Initializes a new instance of the <see cref="BuoyancyController"/> class.
- /// </summary>
- /// <param name="container">Only bodies inside this AABB will be influenced by the controller</param>
- /// <param name="density">Density of the fluid</param>
- /// <param name="linearDragCoefficient">Linear drag coefficient of the fluid</param>
- /// <param name="rotationalDragCoefficient">Rotational drag coefficient of the fluid</param>
- /// <param name="gravity">The direction gravity acts. Buoyancy force will act in opposite direction of gravity.</param>
- 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<int, Body> 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);
- }
- }
- }
- }
|