using FarseerPhysics; using FarseerPhysics.Collision; using FarseerPhysics.Common; using FarseerPhysics.Common.Decomposition; using FarseerPhysics.Dynamics; using FarseerPhysics.Dynamics.Contacts; using FarseerPhysics.Factories; using Microsoft.Xna.Framework; using System; using System.Collections.Generic; using System.Linq; using System.Text; using Voronoi2; namespace Subsurface { class SubmarineBody { public List HullVertices { get; private set; } private Submarine sub; private Body body; private Vector2 speed; private Vector2 targetPosition; public Rectangle Borders { get; private set; } public Vector2 Speed { get { return speed; } set { if (!MathUtils.IsValid(value)) return; speed = value; } } public Vector2 TargetPosition { get { return targetPosition; } set { if (!MathUtils.IsValid(value)) return; targetPosition = value; } } public Vector2 Center { get { return new Vector2(Borders.X + Borders.Width / 2, Borders.Y - Borders.Height / 2); } } public SubmarineBody(Submarine sub) { this.sub = sub; List convexHull = GenerateConvexHull(); HullVertices = convexHull; for (int i = 0; i < convexHull.Count; i++) { convexHull[i] = ConvertUnits.ToSimUnits(convexHull[i]); } convexHull.Reverse(); //get farseer 'vertices' from vectors Vertices shapevertices = new Vertices(convexHull); AABB hullAABB = shapevertices.GetAABB(); Borders = new Rectangle( (int)ConvertUnits.ToDisplayUnits(hullAABB.LowerBound.X), (int)ConvertUnits.ToDisplayUnits(hullAABB.UpperBound.Y), (int)ConvertUnits.ToDisplayUnits(hullAABB.Extents.X * 2.0f), (int)ConvertUnits.ToDisplayUnits(hullAABB.Extents.Y * 2.0f)); var triangulatedVertices = Triangulate.ConvexPartition(shapevertices, TriangulationAlgorithm.Bayazit); body = BodyFactory.CreateCompoundPolygon(GameMain.World, triangulatedVertices, 5.0f); body.BodyType = BodyType.Dynamic; body.CollisionCategories = Physics.CollisionMisc; body.CollidesWith = Physics.CollisionLevel; body.FixedRotation = true; body.Awake = true; body.SleepingAllowed = false; body.GravityScale = 0.0f; body.OnCollision += OnCollision; body.OnSeparation += OnSeparation; } private List GenerateConvexHull() { List points = new List(); Vector2 leftMost = Vector2.Zero; foreach (Structure wall in Structure.wallList) { for (int x = -1; x <= 1; x += 2) { for (int y = -1; y <= 1; y += 2) { Vector2 corner = new Vector2(wall.Rect.X + wall.Rect.Width / 2.0f, wall.Rect.Y - wall.Rect.Height / 2.0f); corner.X += x * wall.Rect.Width / 2.0f; corner.Y += y * wall.Rect.Height / 2.0f; if (points.Contains(corner)) continue; points.Add(corner); if (leftMost == Vector2.Zero || corner.X < leftMost.X) leftMost = corner; } } } List hullPoints = new List(); Vector2 currPoint = leftMost; Vector2 endPoint; do { hullPoints.Add(currPoint); endPoint = points[0]; for (int i = 1; i < points.Count; i++) { if ((currPoint == endPoint) || (MathUtils.VectorOrientation(currPoint, endPoint, points[i]) == -1)) { endPoint = points[i]; } } currPoint = endPoint; } while (endPoint != hullPoints[0]); return hullPoints; } float collisionRigidness = 1.0f; public void Update(float deltaTime) { Vector2 translateAmount = speed * deltaTime; translateAmount += ConvertUnits.ToDisplayUnits(body.Position) * collisionRigidness; if (targetPosition != Vector2.Zero && Vector2.Distance(targetPosition, sub.Position) > 50.0f) { translateAmount += (targetPosition - sub.Position) * 0.01f; } else { targetPosition = Vector2.Zero; } sub.Translate(translateAmount); //------------------------- Vector2 totalForce = CalculateBuoyancy(); float dragCoefficient = 0.00001f; float speedLength = (speed == Vector2.Zero) ? 0.0f : speed.Length(); float drag = speedLength * speedLength * dragCoefficient * mass; if (speed != Vector2.Zero) { totalForce += -Vector2.Normalize(speed) * drag; } ApplyForce(totalForce); //hullBodies[0].body.LinearVelocity = -hullBodies[0].body.Position; //hullBody.SetTransform(Vector2.Zero , 0.0f); body.LinearVelocity = -body.Position / (float)Physics.step; if (collidingCell == null) { collisionRigidness = MathHelper.Lerp(collisionRigidness, 1.0f, 0.1f); return; } foreach (GraphEdge ge in collidingCell.edges) { Body wallBody = Submarine.PickBody( ConvertUnits.ToSimUnits(ge.point1 + GameMain.GameSession.Level.Position), ConvertUnits.ToSimUnits(ge.point2 + GameMain.GameSession.Level.Position), new List() { collidingCell.body }); if (wallBody == null || wallBody.UserData == null) continue; Structure structure = wallBody.UserData as Structure; if (structure == null) continue; structure.AddDamage( structure.FindSectionIndex(ConvertUnits.ToDisplayUnits(Submarine.LastPickedPosition)), 50.0f); } collidingCell = null; } private Vector2 CalculateBuoyancy() { float waterVolume = 0.0f; float volume = 0.0f; foreach (Hull hull in Hull.hullList) { waterVolume += hull.Volume; volume += hull.FullVolume; } float waterPercentage = waterVolume / volume; float neutralPercentage = 0.07f; float buoyancy = neutralPercentage - waterPercentage; buoyancy *= mass * 30.0f; return new Vector2(0.0f, buoyancy); } float mass = 10000.0f; public void ApplyForce(Vector2 force) { speed += force / mass; } VoronoiCell collidingCell; public bool OnCollision(Fixture f1, Fixture f2, Contact contact) { VoronoiCell cell = f2.Body.UserData as VoronoiCell; if (cell == null) { speed = new Vector2(speed.X * 0.9f, speed.Y * 0.2f); return true; } Vector2 normal = contact.Manifold.LocalNormal; Vector2 simSpeed = ConvertUnits.ToSimUnits(speed); float impact = Vector2.Dot(simSpeed, normal); Vector2 u = Vector2.Dot(simSpeed, -normal) * -normal; Vector2 w = simSpeed - u; Vector2 limbForce = normal * impact; float length = limbForce.Length(); if (length > 10.0f) limbForce = (limbForce / length) * 10.0f; foreach (Character c in Character.CharacterList) { if (c.AnimController.CurrentHull == null) continue; if (impact > 2.0f) c.AnimController.StunTimer = (impact - 2.0f) * 0.1f; foreach (Limb limb in c.AnimController.Limbs) { if (c.AnimController.LowestLimb == limb) continue; limb.body.ApplyLinearImpulse(limb.Mass * limbForce); } } if (impact >= 1.0f) { AmbientSoundManager.PlayDamageSound(DamageSoundType.StructureBlunt, impact * 10.0f, cell.body); FarseerPhysics.Common.FixedArray2 worldPoints; contact.GetWorldManifold(out normal, out worldPoints); AmbientSoundManager.PlayDamageSound(DamageSoundType.StructureBlunt, impact * 10.0f, ConvertUnits.ToDisplayUnits(worldPoints[0])); GameMain.GameScreen.Cam.Shake = impact * 2.0f; } System.Diagnostics.Debug.WriteLine("IMPACT: " + impact + " normal: " + normal + " simspeed: " + simSpeed + " u: " + u + " w: " + w); if (impact < 4.0f) { speed = ConvertUnits.ToDisplayUnits(w * 0.9f - u * 0.2f); return true; } else { speed = ConvertUnits.ToDisplayUnits(w * 0.9f + u * 0.5f); } collisionRigidness = 0.8f; collidingCell = cell; return true; } public void OnSeparation(Fixture f1, Fixture f2) { collidingCell = null; } } }