(61d00a474) v0.9.7.1

This commit is contained in:
Regalis
2020-03-04 13:04:10 +01:00
parent 3c50efa5c9
commit 3c09ebe02f
5086 changed files with 786063 additions and 295871 deletions
@@ -0,0 +1,121 @@
/* Original source Farseer Physics Engine:
* Copyright (c) 2014 Ian Qvist, http://farseerphysics.codeplex.com
* Microsoft Permissive License (Ms-PL) v1.1
*/
using Microsoft.Xna.Framework;
namespace FarseerPhysics.Fluids
{
/// <summary>
/// Fluid parameters, see pvfs.pdf for a detailed explanation
/// </summary>
public struct FluidDefinition
{
/// <summary>
/// Distance of influence between the particles
/// </summary>
public float InfluenceRadius;
/// <summary>
/// Density of the fluid
/// </summary>
public float DensityRest;
/// <summary>
/// Stiffness of the fluid (when particles are far)
/// </summary>
public float Stiffness;
/// <summary>
/// Stiffness of the fluid (when particles are near)
/// Set by Check()
/// </summary>
public float StiffnessNear;
/// <summary>
/// Toggles viscosity forces
/// </summary>
public bool UseViscosity;
/// <summary>
/// See pvfs.pdf for more information
/// </summary>
public float ViscositySigma;
/// <summary>
/// See pvfs.pdf for more information
/// </summary>
public float ViscosityBeta;
/// <summary>
/// Toggles plasticity computation (springs etc.)
/// </summary>
public bool UsePlasticity;
/// <summary>
/// Plasticity, amount of memory of the shape
/// See pvfs.pdf for more information
/// </summary>
public float Plasticity;
/// <summary>
/// K of the springs used for plasticity
/// </summary>
public float KSpring;
/// <summary>
/// Amount of change of the rest length of the springs (when compressed)
/// </summary>
public float YieldRatioCompress;
/// <summary>
/// Amount of change of the rest length of the springs (when stretched)
/// </summary>
public float YieldRatioStretch;
public static FluidDefinition Default
{
get
{
FluidDefinition def = new FluidDefinition
{
InfluenceRadius = 1.0f,
DensityRest = 10.0f,
Stiffness = 10.0f,
StiffnessNear = 0.0f, // Set by Check()
UseViscosity = false,
ViscositySigma = 10.0f,
ViscosityBeta = 0.0f,
UsePlasticity = false,
Plasticity = 0.3f,
KSpring = 2.0f,
YieldRatioCompress = 0.1f,
YieldRatioStretch = 0.1f
};
def.Check();
return def;
}
}
public void Check()
{
InfluenceRadius = MathHelper.Clamp(InfluenceRadius, 0.1f, 10.0f);
DensityRest = MathHelper.Clamp(DensityRest, 1.0f, 100.0f);
Stiffness = MathHelper.Clamp(Stiffness, 0.1f, 10.0f);
StiffnessNear = Stiffness * 100.0f; // See pvfs.pdf
ViscositySigma = MathHelper.Max(ViscositySigma, 0.0f);
ViscosityBeta = MathHelper.Max(ViscosityBeta, 0.0f);
Plasticity = MathHelper.Max(Plasticity, 0.0f);
KSpring = MathHelper.Max(KSpring, 0.0f);
YieldRatioCompress = MathHelper.Max(YieldRatioCompress, 0.0f);
YieldRatioStretch = MathHelper.Max(YieldRatioStretch, 0.0f);
}
}
}
@@ -0,0 +1,80 @@
/* Original source Farseer Physics Engine:
* Copyright (c) 2014 Ian Qvist, http://farseerphysics.codeplex.com
* Microsoft Permissive License (Ms-PL) v1.1
*/
using System.Collections.Generic;
using Microsoft.Xna.Framework;
namespace FarseerPhysics.Fluids
{
public class FluidParticle
{
public Vector2 Position;
public Vector2 PreviousPosition;
public Vector2 Velocity;
public Vector2 Acceleration;
internal FluidParticle(Vector2 position)
{
Neighbours = new List<FluidParticle>();
IsActive = true;
MoveTo(position);
Damping = 0.0f;
Mass = 1.0f;
}
public bool IsActive { get; set; }
public List<FluidParticle> Neighbours { get; private set; }
// For gameplay purposes
public float Density { get; internal set; }
public float Pressure { get; internal set; }
// Other properties
public int Index { get; internal set; }
// Physics properties
public float Damping { get; set; }
public float Mass { get; set; }
public void MoveTo(Vector2 p)
{
Position = p;
PreviousPosition = p;
Velocity = Vector2.Zero;
Acceleration = Vector2.Zero;
}
public void ApplyForce(ref Vector2 force)
{
Acceleration += force * Mass;
}
public void ApplyImpulse(ref Vector2 impulse)
{
Velocity += impulse;
}
public void Update(float deltaTime)
{
Velocity += Acceleration * deltaTime;
Vector2 delta = (1.0f - Damping) * Velocity * deltaTime;
PreviousPosition = Position;
Position += delta;
Acceleration = Vector2.Zero;
}
public void UpdateVelocity(float deltaTime)
{
Velocity = (Position - PreviousPosition) / deltaTime;
}
}
}
@@ -0,0 +1,413 @@
/* Original source Farseer Physics Engine:
* Copyright (c) 2014 Ian Qvist, http://farseerphysics.codeplex.com
* Microsoft Permissive License (Ms-PL) v1.1
*/
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
namespace FarseerPhysics.Fluids
{
public class FluidSystem1
{
private float _influenceRadiusSquared;
private HashGrid _hashGrid = new HashGrid();
private Dictionary<SpringHash, Spring> _springs = new Dictionary<SpringHash, Spring>();
private List<SpringHash> _springsToRemove = new List<SpringHash>();
private Vector2 _totalForce;
public FluidSystem1(Vector2 gravity)
{
Gravity = gravity;
Particles = new List<FluidParticle>();
DefaultDefinition();
}
public FluidDefinition Definition { get; private set; }
public List<FluidParticle> Particles { get; private set; }
public int ParticlesCount { get { return Particles.Count; } }
public Vector2 Gravity { get; set; }
public void DefaultDefinition()
{
SetDefinition(FluidDefinition.Default);
}
public void SetDefinition(FluidDefinition def)
{
Definition = def;
Definition.Check();
_influenceRadiusSquared = Definition.InfluenceRadius * Definition.InfluenceRadius;
}
public FluidParticle AddParticle(Vector2 position)
{
FluidParticle particle = new FluidParticle(position) { Index = Particles.Count };
Particles.Add(particle);
return particle;
}
public void Clear()
{
//TODO
}
public void ApplyForce(Vector2 f)
{
_totalForce += f;
}
private void ApplyForces()
{
Vector2 f = Gravity + _totalForce;
for (int i = 0; i < Particles.Count; ++i)
{
Particles[i].ApplyForce(ref f);
}
_totalForce = Vector2.Zero;
}
private void ApplyViscosity(FluidParticle p, float timeStep)
{
for (int i = 0; i < p.Neighbours.Count; ++i)
{
FluidParticle neighbour = p.Neighbours[i];
if (p.Index >= neighbour.Index)
{
continue;
}
float q;
Vector2.DistanceSquared(ref p.Position, ref neighbour.Position, out q);
if (q > _influenceRadiusSquared)
{
continue;
}
Vector2 direction;
Vector2.Subtract(ref neighbour.Position, ref p.Position, out direction);
if (direction.LengthSquared() < float.Epsilon)
{
continue;
}
direction.Normalize();
Vector2 deltaVelocity;
Vector2.Subtract(ref p.Velocity, ref neighbour.Velocity, out deltaVelocity);
float u;
Vector2.Dot(ref deltaVelocity, ref direction, out u);
if (u > 0.0f)
{
q = 1.0f - (float)Math.Sqrt(q) / Definition.InfluenceRadius;
float impulseFactor = 0.5f * timeStep * q * (u * (Definition.ViscositySigma + Definition.ViscosityBeta * u));
Vector2 impulse;
Vector2.Multiply(ref direction, -impulseFactor, out impulse);
p.ApplyImpulse(ref impulse);
Vector2.Multiply(ref direction, impulseFactor, out impulse);
neighbour.ApplyImpulse(ref impulse);
}
}
}
private const int MaxNeighbors = 25;
//private int _len2;
//private int _j;
//private float _q;
//private float _qq;
//private Vector2 _rij;
//private float _d;
//private Vector2 _dx;
private float _density;
private float _densityNear;
private float _pressure;
private float _pressureNear;
private float[] _distanceCache = new float[MaxNeighbors];
//private void DoubleDensityRelaxation1(FluidParticle p, float timeStep)
//{
// _density = 0;
// _densityNear = 0;
// _len2 = p.Neighbours.Count;
// if (_len2 > MaxNeighbors)
// _len2 = MaxNeighbors;
// for (_j = 0; _j < _len2; _j++)
// {
// _q = Vector2.DistanceSquared(p.Position, p.Neighbours[_j].Position);
// _distanceCache[_j] = _q;
// if (_q < _influenceRadiusSquared && _q != 0)
// {
// _q = (float)Math.Sqrt(_q);
// _q /= Definition.InfluenceRadius;
// _qq = ((1 - _q) * (1 - _q));
// _density += _qq;
// _densityNear += _qq * (1 - _q);
// }
// }
// _pressure = Definition.Stiffness * (_density - Definition.DensityRest);
// _pressureNear = Definition.StiffnessNear * _densityNear;
// _dx = Vector2.Zero;
// for (_j = 0; _j < _len2; _j++)
// {
// _q = _distanceCache[_j];
// if (_q < _influenceRadiusSquared && _q != 0)
// {
// _q = (float)Math.Sqrt(_q);
// _rij = p.Neighbours[_j].Position;
// _rij -= p.Position;
// _rij *= 1 / _q;
// _q /= _influenceRadiusSquared;
// _d = ((timeStep * timeStep) * (_pressure * (1 - _q) + _pressureNear * (1 - _q) * (1 - _q)));
// _rij *= _d * 0.5f;
// p.Neighbours[_j].Position += _rij;
// _dx -= _rij;
// }
// }
// p.Position += _dx;
//}
private void DoubleDensityRelaxation(FluidParticle particle, float deltaTime2)
{
_density = 0.0f;
_densityNear = 0.0f;
int neightborCount = particle.Neighbours.Count;
if (neightborCount > MaxNeighbors)
neightborCount = MaxNeighbors;
for (int i = 0; i < neightborCount; ++i)
{
FluidParticle neighbour = particle.Neighbours[i];
if (particle.Index == neighbour.Index)
continue;
float q;
Vector2.DistanceSquared(ref particle.Position, ref neighbour.Position, out q);
_distanceCache[i] = q;
if (q > _influenceRadiusSquared)
continue;
q = 1.0f - (float)Math.Sqrt(q) / Definition.InfluenceRadius;
float densityDelta = q * q;
_density += densityDelta;
_densityNear += densityDelta * q;
}
_pressure = Definition.Stiffness * (_density - Definition.DensityRest);
_pressureNear = Definition.StiffnessNear * _densityNear;
// For gameplay purposes
particle.Density = _density + _densityNear;
particle.Pressure = _pressure + _pressureNear;
Vector2 delta = Vector2.Zero;
for (int i = 0; i < neightborCount; ++i)
{
FluidParticle neighbour = particle.Neighbours[i];
if (particle.Index == neighbour.Index)
continue;
float q = _distanceCache[i];
if (q > _influenceRadiusSquared)
continue;
q = 1.0f - (float)Math.Sqrt(q) / Definition.InfluenceRadius;
float dispFactor = deltaTime2 * (q * (_pressure + _pressureNear * q));
Vector2 direction;
Vector2.Subtract(ref neighbour.Position, ref particle.Position, out direction);
if (direction.LengthSquared() < float.Epsilon)
continue;
direction.Normalize();
Vector2 disp;
Vector2.Multiply(ref direction, dispFactor, out disp);
Vector2.Add(ref neighbour.Position, ref disp, out neighbour.Position);
Vector2.Multiply(ref direction, -dispFactor, out disp);
Vector2.Add(ref delta, ref disp, out delta);
}
Vector2.Add(ref particle.Position, ref delta, out particle.Position);
}
private void CreateSprings(FluidParticle p)
{
for (int i = 0; i < p.Neighbours.Count; ++i)
{
FluidParticle neighbour = p.Neighbours[i];
if (p.Index >= neighbour.Index)
continue;
float q;
Vector2.DistanceSquared(ref p.Position, ref neighbour.Position, out q);
if (q > _influenceRadiusSquared)
continue;
SpringHash hash = new SpringHash { P0 = p, P1 = neighbour };
if (!_springs.ContainsKey(hash))
{
//TODO: Use pool?
Spring spring = new Spring(p, neighbour) { RestLength = (float)Math.Sqrt(q) };
_springs.Add(hash, spring);
}
}
}
private void AdjustSprings(float timeStep)
{
foreach (var pair in _springs)
{
Spring spring = pair.Value;
spring.Update(timeStep, Definition.KSpring, Definition.InfluenceRadius);
if (spring.Active)
{
float L = spring.RestLength;
float distance;
Vector2.Distance(ref spring.P0.Position, ref spring.P1.Position, out distance);
if (distance > (L + (Definition.YieldRatioStretch * L)))
{
spring.RestLength += timeStep * Definition.Plasticity * (distance - L - (Definition.YieldRatioStretch * L));
}
else if (distance < (L - (Definition.YieldRatioCompress * L)))
{
spring.RestLength -= timeStep * Definition.Plasticity * (L - (Definition.YieldRatioCompress * L) - distance);
}
}
else
{
_springsToRemove.Add(pair.Key);
}
}
for (int i = 0; i < _springsToRemove.Count; ++i)
{
_springs.Remove(_springsToRemove[i]);
}
}
private void ComputeNeighbours()
{
_hashGrid.GridSize = Definition.InfluenceRadius;
_hashGrid.Clear();
for (int i = 0; i < Particles.Count; ++i)
{
FluidParticle p = Particles[i];
if (p.IsActive)
{
_hashGrid.Add(p);
}
}
for (int i = 0; i < Particles.Count; ++i)
{
FluidParticle p = Particles[i];
p.Neighbours.Clear();
_hashGrid.Find(ref p.Position, p.Neighbours);
}
}
public void Update(float deltaTime)
{
if (deltaTime == 0)
return;
float deltaTime2 = 0.5f * deltaTime * deltaTime;
ComputeNeighbours();
ApplyForces();
if (Definition.UseViscosity)
{
for (int i = 0; i < Particles.Count; ++i)
{
FluidParticle p = Particles[i];
if (p.IsActive)
{
ApplyViscosity(p, deltaTime);
}
}
}
for (int i = 0; i < Particles.Count; ++i)
{
FluidParticle p = Particles[i];
if (p.IsActive)
{
p.Update(deltaTime);
}
}
for (int i = 0; i < Particles.Count; ++i)
{
FluidParticle p = Particles[i];
if (p.IsActive)
{
DoubleDensityRelaxation(p, deltaTime2);
}
}
if (Definition.UsePlasticity)
{
for (int i = 0; i < Particles.Count; ++i)
{
FluidParticle p = Particles[i];
if (p.IsActive)
{
CreateSprings(p);
}
}
}
AdjustSprings(deltaTime);
UpdateVelocities(deltaTime);
}
internal void UpdateVelocities(float timeStep)
{
for (int i = 0; i < Particles.Count; ++i)
{
Particles[i].UpdateVelocity(timeStep);
}
}
}
}
@@ -0,0 +1,95 @@
/* Original source Farseer Physics Engine:
* Copyright (c) 2014 Ian Qvist, http://farseerphysics.codeplex.com
* Microsoft Permissive License (Ms-PL) v1.1
*/
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
namespace FarseerPhysics.Fluids
{
/// <summary>
/// Grid used by particle system to keep track of neightbor particles.
/// </summary>
public class HashGrid
{
private Dictionary<ulong, List<FluidParticle>> _hash = new Dictionary<ulong, List<FluidParticle>>();
private Stack<List<FluidParticle>> _bucketPool = new Stack<List<FluidParticle>>();
public HashGrid()
{
GridSize = 1.0f;
}
public float GridSize { get; set; }
private static ulong HashKey(int x, int y)
{
return ((ulong)x * 2185031351ul) ^ ((ulong)y * 4232417593ul);
}
private ulong HashKey(Vector2 position)
{
return HashKey(
(int)Math.Floor(position.X / GridSize),
(int)Math.Floor(position.Y / GridSize)
);
}
public void Clear()
{
foreach (KeyValuePair<ulong, List<FluidParticle>> pair in _hash)
{
pair.Value.Clear();
_bucketPool.Push(pair.Value);
}
_hash.Clear();
}
public void Add(FluidParticle particle)
{
ulong key = HashKey(particle.Position);
List<FluidParticle> bucket;
if (!_hash.TryGetValue(key, out bucket))
{
if (_bucketPool.Count > 0)
{
bucket = _bucketPool.Pop();
}
else
{
bucket = new List<FluidParticle>();
}
_hash.Add(key, bucket);
}
bucket.Add(particle);
}
public void Find(ref Vector2 position, List<FluidParticle> neighbours)
{
int ix = (int)Math.Floor(position.X / GridSize);
int iy = (int)Math.Floor(position.Y / GridSize);
// Check all 9 neighbouring cells
for (int x = ix - 1; x <= ix + 1; ++x)
{
for (int y = iy - 1; y <= iy + 1; ++y)
{
ulong key = HashKey(x, y);
List<FluidParticle> bucket;
if (_hash.TryGetValue(key, out bucket))
{
for (int i = 0; i < bucket.Count; ++i)
{
if (bucket[i] != null)
{
neighbours.Add(bucket[i]);
}
}
}
}
}
}
}
}
@@ -0,0 +1,57 @@
/* Original source Farseer Physics Engine:
* Copyright (c) 2014 Ian Qvist, http://farseerphysics.codeplex.com
* Microsoft Permissive License (Ms-PL) v1.1
*/
using Microsoft.Xna.Framework;
namespace FarseerPhysics.Fluids
{
//TODO: Could be struct?
public class Spring
{
public FluidParticle P0;
public FluidParticle P1;
public Spring(FluidParticle p0, FluidParticle p1)
{
Active = true;
P0 = p0;
P1 = p1;
}
public bool Active { get; set; }
public float RestLength { get; set; }
public void Update(float timeStep, float kSpring, float influenceRadius)
{
if (!Active)
return;
Vector2 dir = P1.Position - P0.Position;
float distance = dir.Length();
dir.Normalize();
// This is to avoid imploding simulation with really springy fluids
if (distance < 0.5f * influenceRadius)
{
Active = false;
return;
}
if (RestLength > influenceRadius)
{
Active = false;
return;
}
//Algorithm 3
float displacement = timeStep * timeStep * kSpring * (1.0f - RestLength / influenceRadius) * (RestLength - distance) * 0.5f;
dir *= displacement;
P0.Position -= dir;
P1.Position += dir;
}
}
}
@@ -0,0 +1,26 @@
/* Original source Farseer Physics Engine:
* Copyright (c) 2014 Ian Qvist, http://farseerphysics.codeplex.com
* Microsoft Permissive License (Ms-PL) v1.1
*/
using System.Collections.Generic;
namespace FarseerPhysics.Fluids
{
public class SpringHash : IEqualityComparer<SpringHash>
{
public FluidParticle P0;
public FluidParticle P1;
public bool Equals(SpringHash lhs, SpringHash rhs)
{
return (lhs.P0.Index == rhs.P0.Index && lhs.P1.Index == rhs.P1.Index)
|| (lhs.P0.Index == rhs.P1.Index && lhs.P1.Index == rhs.P0.Index);
}
public int GetHashCode(SpringHash s)
{
return (s.P0.Index * 73856093) ^ (s.P1.Index * 19349663) ^ (s.P0.Index * 19349663) ^ (s.P1.Index * 73856093);
}
}
}