Agregada funciones de Fluidos
This commit is contained in:
parent
20bdad509b
commit
d1ec333243
|
@ -77,13 +77,13 @@
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Aether.Physics2D" Version="2.1.0" />
|
<PackageReference Include="Aether.Physics2D" Version="2.2.0" />
|
||||||
<PackageReference Include="ClosedXML" Version="0.104.2" />
|
<PackageReference Include="ClosedXML" Version="0.105.0-rc" />
|
||||||
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0" />
|
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0" />
|
||||||
<PackageReference Include="Emgu.CV" Version="4.9.0.5494" />
|
<PackageReference Include="Emgu.CV" Version="4.10.0.5680" />
|
||||||
<PackageReference Include="Emgu.CV.runtime.windows" Version="4.9.0.5494" />
|
<PackageReference Include="Emgu.CV.runtime.windows" Version="4.10.0.5680" />
|
||||||
<PackageReference Include="Emgu.CV.UI" Version="4.9.0.5494" />
|
<PackageReference Include="Emgu.CV.UI" Version="4.10.0.5680" />
|
||||||
<PackageReference Include="Extended.Wpf.Toolkit" Version="4.6.1" />
|
<PackageReference Include="Extended.Wpf.Toolkit" Version="4.7.25104.5739" />
|
||||||
<PackageReference Include="LanguageDetection" Version="1.2.0" />
|
<PackageReference Include="LanguageDetection" Version="1.2.0" />
|
||||||
<PackageReference Include="LiveChartsCore.SkiaSharpView.WPF" Version="2.0.0-rc4.5" />
|
<PackageReference Include="LiveChartsCore.SkiaSharpView.WPF" Version="2.0.0-rc4.5" />
|
||||||
<PackageReference Include="Microsoft.Xaml.Behaviors.Wpf" Version="1.1.135" />
|
<PackageReference Include="Microsoft.Xaml.Behaviors.Wpf" Version="1.1.135" />
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -553,11 +553,11 @@ namespace CtrEditor.Simulacion
|
||||||
Body.OnSeparation += HandleOnSeparation;
|
Body.OnSeparation += HandleOnSeparation;
|
||||||
Body.Tag = this; // Importante para la identificación durante la colisión
|
Body.Tag = this; // Importante para la identificación durante la colisión
|
||||||
// Configurar la fricción
|
// Configurar la fricción
|
||||||
Body.SetFriction(0.2f);
|
//Body.SetFriction(0.2f);
|
||||||
// Configurar amortiguamiento
|
// Configurar amortiguamiento
|
||||||
Body.LinearDamping = 3f; // Ajustar para controlar la reducción de la velocidad lineal
|
Body.LinearDamping = 3f; // Ajustar para controlar la reducción de la velocidad lineal
|
||||||
Body.AngularDamping = 1f; // Ajustar para controlar la reducción de la velocidad angular
|
Body.AngularDamping = 1f; // Ajustar para controlar la reducción de la velocidad angular
|
||||||
Body.SetRestitution(0f); // Baja restitución para menos rebote
|
//Body.SetRestitution(0f); // Baja restitución para menos rebote
|
||||||
Body.SleepingAllowed = false;
|
Body.SleepingAllowed = false;
|
||||||
Body.IsBullet = true;
|
Body.IsBullet = true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 nkast.Aether.Physics2D.Common;
|
||||||
|
|
||||||
|
namespace tainicom.Aether.Physics2D.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 = MathUtils.Clamp(InfluenceRadius, 0.1f, 10.0f);
|
||||||
|
DensityRest = MathUtils.Clamp(DensityRest, 1.0f, 100.0f);
|
||||||
|
Stiffness = MathUtils.Clamp(Stiffness, 0.1f, 10.0f);
|
||||||
|
StiffnessNear = Stiffness * 100.0f; // See pvfs.pdf
|
||||||
|
|
||||||
|
ViscositySigma = Math.Max(ViscositySigma, 0.0f);
|
||||||
|
ViscosityBeta = Math.Max(ViscosityBeta, 0.0f);
|
||||||
|
|
||||||
|
Plasticity = Math.Max(Plasticity, 0.0f);
|
||||||
|
KSpring = Math.Max(KSpring, 0.0f);
|
||||||
|
YieldRatioCompress = Math.Max(YieldRatioCompress, 0.0f);
|
||||||
|
YieldRatioStretch = Math.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 nkast.Aether.Physics2D.Common;
|
||||||
|
|
||||||
|
namespace tainicom.Aether.Physics2D.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 nkast.Aether.Physics2D.Common;
|
||||||
|
|
||||||
|
namespace tainicom.Aether.Physics2D.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 nkast.Aether.Physics2D.Common;
|
||||||
|
|
||||||
|
namespace tainicom.Aether.Physics2D.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 nkast.Aether.Physics2D.Common;
|
||||||
|
|
||||||
|
namespace tainicom.Aether.Physics2D.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 tainicom.Aether.Physics2D.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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,445 @@
|
||||||
|
/* 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 nkast.Aether.Physics2D.Common;
|
||||||
|
|
||||||
|
namespace tainicom.Aether.Physics2D.Fluids
|
||||||
|
{
|
||||||
|
public class FluidSystem2
|
||||||
|
{
|
||||||
|
public const int MaxNeighbors = 25;
|
||||||
|
public const int CellSize = 1;
|
||||||
|
|
||||||
|
// Most of these can be tuned at runtime with F1-F9 and keys 1-9 (no numpad)
|
||||||
|
public const float InfluenceRadius = 20.0f;
|
||||||
|
public const float InfluenceRadiusSquared = InfluenceRadius * InfluenceRadius;
|
||||||
|
public const float Stiffness = 0.504f;
|
||||||
|
public const float StiffnessFarNearRatio = 10.0f;
|
||||||
|
public const float StiffnessNear = Stiffness * StiffnessFarNearRatio;
|
||||||
|
public const float ViscositySigma = 0.0f;
|
||||||
|
public const float ViscosityBeta = 0.3f;
|
||||||
|
public const float DensityRest = 10.0f;
|
||||||
|
public const float KSpring = 0.3f;
|
||||||
|
public const float RestLength = 5.0f;
|
||||||
|
public const float RestLengthSquared = RestLength * RestLength;
|
||||||
|
public const float YieldRatioStretch = 0.5f;
|
||||||
|
public const float YieldRatioCompress = 0.5f;
|
||||||
|
public const float Plasticity = 0.5f;
|
||||||
|
public const int VelocityCap = 150;
|
||||||
|
public const float DeformationFactor = 0f;
|
||||||
|
public const float CollisionForce = 0.3f;
|
||||||
|
|
||||||
|
private bool _isElasticityInitialized;
|
||||||
|
private bool _elasticityEnabled;
|
||||||
|
private bool _isPlasticityInitialized;
|
||||||
|
private bool _plasticityEnabled;
|
||||||
|
|
||||||
|
private float _deltaTime2;
|
||||||
|
private Vector2 _dx = new Vector2(0.0f, 0.0f);
|
||||||
|
private const int Wpadding = 20;
|
||||||
|
private const int Hpadding = 20;
|
||||||
|
|
||||||
|
public SpatialTable Particles;
|
||||||
|
|
||||||
|
// Temp variables
|
||||||
|
private Vector2 _rij = new Vector2(0.0f, 0.0f);
|
||||||
|
private Vector2 _tempVect = new Vector2(0.0f, 0.0f);
|
||||||
|
|
||||||
|
private Dictionary<int, List<int>> _springPresenceTable;
|
||||||
|
private List<Spring2> _springs;
|
||||||
|
private List<Particle> _tempParticles;
|
||||||
|
|
||||||
|
private int _worldWidth;
|
||||||
|
private int _worldHeight;
|
||||||
|
|
||||||
|
public int ParticlesCount { get { return Particles.Count; } }
|
||||||
|
|
||||||
|
public FluidSystem2(Vector2 gravity, int maxParticleLimit, int worldWidth, int worldHeight)
|
||||||
|
{
|
||||||
|
_worldHeight = worldHeight;
|
||||||
|
_worldWidth = worldWidth;
|
||||||
|
Particles = new SpatialTable(worldWidth, worldHeight, CellSize);
|
||||||
|
MaxParticleLimit = maxParticleLimit;
|
||||||
|
Gravity = gravity;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vector2 Gravity { get; set; }
|
||||||
|
public int MaxParticleLimit { get; private set; }
|
||||||
|
|
||||||
|
public bool ElasticityEnabled
|
||||||
|
{
|
||||||
|
get { return _elasticityEnabled; }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (!_isElasticityInitialized)
|
||||||
|
InitializeElasticity();
|
||||||
|
|
||||||
|
_elasticityEnabled = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool PlasticityEnabled
|
||||||
|
{
|
||||||
|
get { return _plasticityEnabled; }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (!_isPlasticityInitialized)
|
||||||
|
InitializePlasticity();
|
||||||
|
|
||||||
|
_plasticityEnabled = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateParticleVelocity(float deltaTime)
|
||||||
|
{
|
||||||
|
for(int i = 0; i < Particles.Count; i++)
|
||||||
|
{
|
||||||
|
Particle particle = Particles[i];
|
||||||
|
particle.PreviousPosition = particle.Position;
|
||||||
|
particle.Position = new Vector2(particle.Position.X + (deltaTime * particle.Velocity.X), particle.Position.Y + (deltaTime * particle.Velocity.Y));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void WallCollision(Particle pi)
|
||||||
|
{
|
||||||
|
float x = 0;
|
||||||
|
float y = 0;
|
||||||
|
|
||||||
|
if (pi.Position.X > (_worldWidth / 2 - Wpadding))
|
||||||
|
x -= (pi.Position.X - (_worldWidth / 2 - Wpadding)) / CollisionForce;
|
||||||
|
else if (pi.Position.X < (-_worldWidth / 2 + Wpadding))
|
||||||
|
x += ((-_worldWidth / 2 + Wpadding) - pi.Position.X) / CollisionForce;
|
||||||
|
|
||||||
|
if (pi.Position.Y > (_worldHeight - Hpadding))
|
||||||
|
y -= (pi.Position.Y - (_worldHeight - Hpadding)) / CollisionForce;
|
||||||
|
else if (pi.Position.Y < Hpadding)
|
||||||
|
y += (Hpadding - pi.Position.Y) / CollisionForce;
|
||||||
|
|
||||||
|
pi.Velocity.X += x;
|
||||||
|
pi.Velocity.Y += y;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CapVelocity(Vector2 v)
|
||||||
|
{
|
||||||
|
if (v.X > VelocityCap)
|
||||||
|
v.X = VelocityCap;
|
||||||
|
else if (v.X < -VelocityCap)
|
||||||
|
v.X = -VelocityCap;
|
||||||
|
|
||||||
|
if (v.Y > VelocityCap)
|
||||||
|
v.Y = VelocityCap;
|
||||||
|
else if (v.Y < -VelocityCap)
|
||||||
|
v.Y = -VelocityCap;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InitializePlasticity()
|
||||||
|
{
|
||||||
|
_isPlasticityInitialized = true;
|
||||||
|
|
||||||
|
_springs.Clear();
|
||||||
|
float q;
|
||||||
|
foreach (Particle pa in Particles)
|
||||||
|
{
|
||||||
|
foreach (Particle pb in Particles)
|
||||||
|
{
|
||||||
|
if (pa.GetHashCode() == pb.GetHashCode())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
Vector2.Distance(ref pa.Position, ref pb.Position, out q);
|
||||||
|
Vector2.Subtract(ref pb.Position, ref pa.Position, out _rij);
|
||||||
|
_rij /= q;
|
||||||
|
|
||||||
|
if (q < RestLength)
|
||||||
|
{
|
||||||
|
_springs.Add(new Spring2(pa, pb, q));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pa.Velocity = Vector2.Zero;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CalculatePlasticity(float deltaTime)
|
||||||
|
{
|
||||||
|
foreach (Spring2 spring in _springs)
|
||||||
|
{
|
||||||
|
spring.Update();
|
||||||
|
|
||||||
|
if (spring.CurrentDistance == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
Vector2.Subtract(ref spring.PB.Position, ref spring.PA.Position, out _rij);
|
||||||
|
_rij /= spring.CurrentDistance;
|
||||||
|
float D = deltaTime * KSpring * (spring.RestLength - spring.CurrentDistance);
|
||||||
|
_rij *= (D * 0.5f);
|
||||||
|
spring.PA.Position = new Vector2(spring.PA.Position.X - _rij.X, spring.PA.Position.Y - _rij.Y);
|
||||||
|
spring.PB.Position = new Vector2(spring.PB.Position.X + _rij.X, spring.PB.Position.Y + _rij.Y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InitializeElasticity()
|
||||||
|
{
|
||||||
|
_isElasticityInitialized = true;
|
||||||
|
|
||||||
|
foreach (Particle particle in Particles)
|
||||||
|
{
|
||||||
|
_springPresenceTable.Add(particle.GetHashCode(), new List<int>(MaxParticleLimit));
|
||||||
|
particle.Velocity = Vector2.Zero;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CalculateElasticity(float deltaTime)
|
||||||
|
{
|
||||||
|
float sqDist;
|
||||||
|
for (int i = 0; i < Particles.Count; i++)
|
||||||
|
{
|
||||||
|
Particle pa = Particles[i];
|
||||||
|
|
||||||
|
if (Particles.CountNearBy(pa) <= 1)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
_tempParticles = Particles.GetNearby(pa);
|
||||||
|
int len2 = _tempParticles.Count;
|
||||||
|
|
||||||
|
if (len2 > MaxNeighbors)
|
||||||
|
len2 = MaxNeighbors;
|
||||||
|
|
||||||
|
for (int j = 0; j < len2; j++)
|
||||||
|
{
|
||||||
|
Particle pb = Particles[j];
|
||||||
|
Vector2.DistanceSquared(ref pa.Position, ref pb.Position, out sqDist);
|
||||||
|
if (sqDist > RestLengthSquared)
|
||||||
|
continue;
|
||||||
|
if (pa.GetHashCode() == pb.GetHashCode())
|
||||||
|
continue;
|
||||||
|
if (!_springPresenceTable[pa.GetHashCode()].Contains(pb.GetHashCode()))
|
||||||
|
{
|
||||||
|
_springs.Add(new Spring2(pa, pb, RestLength));
|
||||||
|
_springPresenceTable[pa.GetHashCode()].Add(pb.GetHashCode());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = _springs.Count - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
Spring2 spring = _springs[i];
|
||||||
|
spring.Update();
|
||||||
|
|
||||||
|
// Stretch
|
||||||
|
if (spring.CurrentDistance > (spring.RestLength + DeformationFactor))
|
||||||
|
{
|
||||||
|
spring.RestLength += deltaTime * Plasticity * (spring.CurrentDistance - spring.RestLength - (YieldRatioStretch * spring.RestLength));
|
||||||
|
}
|
||||||
|
// Compress
|
||||||
|
else if (spring.CurrentDistance < (spring.RestLength - DeformationFactor))
|
||||||
|
{
|
||||||
|
spring.RestLength -= deltaTime * Plasticity * (spring.RestLength - (YieldRatioCompress * spring.RestLength) - spring.CurrentDistance);
|
||||||
|
}
|
||||||
|
// Remove springs with restLength longer than REST_LENGTH
|
||||||
|
if (spring.RestLength > RestLength)
|
||||||
|
{
|
||||||
|
_springs.RemoveAt(i);
|
||||||
|
_springPresenceTable[spring.PA.GetHashCode()].Remove(spring.PB.GetHashCode());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (spring.CurrentDistance == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
Vector2.Subtract(ref spring.PB.Position, ref spring.PA.Position, out _rij);
|
||||||
|
_rij /= spring.CurrentDistance;
|
||||||
|
float D = deltaTime * KSpring * (spring.RestLength - spring.CurrentDistance);
|
||||||
|
_rij *= (D * 0.5f);
|
||||||
|
spring.PA.Position = new Vector2(spring.PA.Position.X - _rij.X, spring.PA.Position.Y - _rij.Y);
|
||||||
|
spring.PB.Position = new Vector2(spring.PB.Position.X + _rij.X, spring.PB.Position.Y + _rij.Y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ApplyGravity(Particle particle)
|
||||||
|
{
|
||||||
|
particle.Velocity = new Vector2(particle.Velocity.X + Gravity.X, particle.Velocity.Y + Gravity.Y);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ApplyViscosity(float deltaTime)
|
||||||
|
{
|
||||||
|
float u, q;
|
||||||
|
for (int i = 0; i < Particles.Count; i++)
|
||||||
|
{
|
||||||
|
Particle particle = Particles[i];
|
||||||
|
|
||||||
|
_tempParticles = Particles.GetNearby(particle);
|
||||||
|
|
||||||
|
int len2 = _tempParticles.Count;
|
||||||
|
if (len2 > MaxNeighbors)
|
||||||
|
len2 = MaxNeighbors;
|
||||||
|
|
||||||
|
for (int j = 0; j < len2; j++)
|
||||||
|
{
|
||||||
|
Particle tempParticle = _tempParticles[j];
|
||||||
|
|
||||||
|
Vector2.DistanceSquared(ref particle.Position, ref tempParticle.Position, out q);
|
||||||
|
if ((q < InfluenceRadiusSquared) && (q != 0))
|
||||||
|
{
|
||||||
|
q = (float)Math.Sqrt(q);
|
||||||
|
Vector2.Subtract(ref tempParticle.Position, ref particle.Position, out _rij);
|
||||||
|
Vector2.Divide(ref _rij, q, out _rij);
|
||||||
|
|
||||||
|
Vector2.Subtract(ref particle.Velocity, ref tempParticle.Velocity, out _tempVect);
|
||||||
|
Vector2.Dot(ref _tempVect, ref _rij, out u);
|
||||||
|
if (u <= 0.0f)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
q /= InfluenceRadius;
|
||||||
|
|
||||||
|
float I = (deltaTime * (1 - q) * (ViscositySigma * u + ViscosityBeta * u * u));
|
||||||
|
Vector2.Multiply(ref _rij, (I * 0.5f), out _rij);
|
||||||
|
Vector2.Subtract(ref particle.Velocity, ref _rij, out _tempVect);
|
||||||
|
particle.Velocity = _tempVect;
|
||||||
|
_tempVect = tempParticle.Velocity;
|
||||||
|
_tempVect += _rij;
|
||||||
|
tempParticle.Velocity = _tempVect;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DoubleDensityRelaxation()
|
||||||
|
{
|
||||||
|
float q;
|
||||||
|
for (int i = 0; i < Particles.Count; i++)
|
||||||
|
{
|
||||||
|
Particle particle = Particles[i];
|
||||||
|
particle.Density = 0;
|
||||||
|
particle.NearDensity = 0;
|
||||||
|
|
||||||
|
_tempParticles = Particles.GetNearby(particle);
|
||||||
|
|
||||||
|
int len2 = _tempParticles.Count;
|
||||||
|
if (len2 > MaxNeighbors)
|
||||||
|
len2 = MaxNeighbors;
|
||||||
|
|
||||||
|
for (int j = 0; j < len2; j++)
|
||||||
|
{
|
||||||
|
Particle tempParticle = _tempParticles[j];
|
||||||
|
|
||||||
|
Vector2.DistanceSquared(ref particle.Position, ref tempParticle.Position, out q);
|
||||||
|
if (q < InfluenceRadiusSquared && q != 0)
|
||||||
|
{
|
||||||
|
q = (float)Math.Sqrt(q);
|
||||||
|
q /= InfluenceRadius;
|
||||||
|
float qq = ((1 - q) * (1 - q));
|
||||||
|
particle.Density += qq;
|
||||||
|
particle.NearDensity += qq * (1 - q);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
particle.Pressure = (Stiffness * (particle.Density - DensityRest));
|
||||||
|
particle.NearPressure = (StiffnessNear * particle.NearDensity);
|
||||||
|
_dx = Vector2.Zero;
|
||||||
|
|
||||||
|
for (int j = 0; j < len2; j++)
|
||||||
|
{
|
||||||
|
Particle tempParticle = _tempParticles[j];
|
||||||
|
|
||||||
|
Vector2.DistanceSquared(ref particle.Position, ref tempParticle.Position, out q);
|
||||||
|
if ((q < InfluenceRadiusSquared) && (q != 0))
|
||||||
|
{
|
||||||
|
q = (float)Math.Sqrt(q);
|
||||||
|
Vector2.Subtract(ref tempParticle.Position, ref particle.Position, out _rij);
|
||||||
|
Vector2.Divide(ref _rij, q, out _rij);
|
||||||
|
q /= InfluenceRadius;
|
||||||
|
|
||||||
|
float D = (_deltaTime2 * (particle.Pressure * (1 - q) + particle.NearPressure * (1 - q) * (1 - q)));
|
||||||
|
Vector2.Multiply(ref _rij, (D * 0.5f), out _rij);
|
||||||
|
tempParticle.Position = new Vector2(tempParticle.Position.X + _rij.X, tempParticle.Position.Y + _rij.Y);
|
||||||
|
Vector2.Subtract(ref _dx, ref _rij, out _dx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
particle.Position = particle.Position + _dx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Update(float deltaTime)
|
||||||
|
{
|
||||||
|
if (deltaTime == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_deltaTime2 = deltaTime * deltaTime;
|
||||||
|
|
||||||
|
ApplyViscosity(deltaTime);
|
||||||
|
|
||||||
|
//Update velocity
|
||||||
|
UpdateParticleVelocity(deltaTime);
|
||||||
|
|
||||||
|
Particles.Rehash();
|
||||||
|
|
||||||
|
if (_elasticityEnabled)
|
||||||
|
CalculateElasticity(deltaTime);
|
||||||
|
|
||||||
|
if (_plasticityEnabled)
|
||||||
|
CalculatePlasticity(deltaTime);
|
||||||
|
|
||||||
|
DoubleDensityRelaxation();
|
||||||
|
|
||||||
|
for(int i = 0; i < Particles.Count; i++)
|
||||||
|
{
|
||||||
|
Particle particle = Particles[i];
|
||||||
|
particle.Velocity = new Vector2((particle.Position.X - particle.PreviousPosition.X) / deltaTime, (particle.Position.Y - particle.PreviousPosition.Y) / deltaTime);
|
||||||
|
ApplyGravity(particle);
|
||||||
|
WallCollision(particle);
|
||||||
|
CapVelocity(particle.Velocity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddParticle(Vector2 position)
|
||||||
|
{
|
||||||
|
Particles.Add(new Particle(position.X, position.Y));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Particle
|
||||||
|
{
|
||||||
|
public float Density;
|
||||||
|
public float NearDensity;
|
||||||
|
public float NearPressure;
|
||||||
|
public Vector2 Position = new Vector2(0, 0);
|
||||||
|
public float Pressure;
|
||||||
|
public Vector2 PreviousPosition = new Vector2(0, 0);
|
||||||
|
public Vector2 Velocity = new Vector2(0, 0);
|
||||||
|
|
||||||
|
public Particle(float posX, float posY)
|
||||||
|
{
|
||||||
|
Position = new Vector2(posX, posY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Spring2
|
||||||
|
{
|
||||||
|
public float CurrentDistance;
|
||||||
|
public Particle PA;
|
||||||
|
public Particle PB;
|
||||||
|
public float RestLength;
|
||||||
|
|
||||||
|
public Spring2(Particle pa, Particle pb, float restLength)
|
||||||
|
{
|
||||||
|
PA = pa;
|
||||||
|
PB = pb;
|
||||||
|
RestLength = restLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Update()
|
||||||
|
{
|
||||||
|
Vector2.Distance(ref PA.Position, ref PB.Position, out CurrentDistance);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Contains(Particle p)
|
||||||
|
{
|
||||||
|
return (PA.GetHashCode() == p.GetHashCode() || PB.GetHashCode() == p.GetHashCode());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,179 @@
|
||||||
|
/* Original source Farseer Physics Engine:
|
||||||
|
* Copyright (c) 2014 Ian Qvist, http://farseerphysics.codeplex.com
|
||||||
|
* Microsoft Permissive License (Ms-PL) v1.1
|
||||||
|
*/
|
||||||
|
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace tainicom.Aether.Physics2D.Fluids
|
||||||
|
{
|
||||||
|
public class SpatialTable : IEnumerable<Particle>
|
||||||
|
{
|
||||||
|
// default nearby table size
|
||||||
|
private const int DefaultNearbySize = 50;
|
||||||
|
private List<Particle> _table;
|
||||||
|
private List<Particle> _voidList = new List<Particle>(1);
|
||||||
|
private List<Particle>[][] _nearby;
|
||||||
|
bool _initialized;
|
||||||
|
|
||||||
|
private int _row;
|
||||||
|
private int _column;
|
||||||
|
private int _cellSize;
|
||||||
|
|
||||||
|
public SpatialTable(int column, int row, int cellSize)
|
||||||
|
{
|
||||||
|
_row = row;
|
||||||
|
_cellSize = cellSize;
|
||||||
|
_column = column;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Initialize()
|
||||||
|
{
|
||||||
|
_table = new List<Particle>((_row * _column) / 2);
|
||||||
|
_nearby = new List<Particle>[_column][];
|
||||||
|
|
||||||
|
for (int i = 0; i < _column; ++i)
|
||||||
|
{
|
||||||
|
_nearby[i] = new List<Particle>[_row];
|
||||||
|
|
||||||
|
for (int j = 0; j < _row; ++j)
|
||||||
|
{
|
||||||
|
_nearby[i][j] = new List<Particle>(DefaultNearbySize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_initialized = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Append value to the table and identify its position in the space.
|
||||||
|
/// Don't need to rehash table after append operation.</summary>
|
||||||
|
/// <param name="value"></param>
|
||||||
|
public void Add(Particle value)
|
||||||
|
{
|
||||||
|
if (!_initialized)
|
||||||
|
Initialize();
|
||||||
|
|
||||||
|
AddInterRadius(value);
|
||||||
|
_table.Add(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Particle this[int i]
|
||||||
|
{
|
||||||
|
get { return _table[i]; }
|
||||||
|
set { _table[i] = value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Remove(Particle value)
|
||||||
|
{
|
||||||
|
_table.Remove(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Clear()
|
||||||
|
{
|
||||||
|
for (int i = 0; i < _column; ++i)
|
||||||
|
{
|
||||||
|
for (int j = 0; j < _row; ++j)
|
||||||
|
{
|
||||||
|
_nearby[i][j].Clear();
|
||||||
|
_nearby[i][j] = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_table.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Count
|
||||||
|
{
|
||||||
|
get { return (_table == null)? 0 : _table.Count; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Particle> GetNearby(Particle value)
|
||||||
|
{
|
||||||
|
int x = posX(value);
|
||||||
|
int y = posY(value);
|
||||||
|
|
||||||
|
if (!InRange(x, y))
|
||||||
|
return _voidList;
|
||||||
|
|
||||||
|
return _nearby[x][y];
|
||||||
|
}
|
||||||
|
|
||||||
|
private int posX(Particle value)
|
||||||
|
{
|
||||||
|
return (int)((value.Position.X + (_column / 2) + 0.3f) / _cellSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int posY(Particle value)
|
||||||
|
{
|
||||||
|
return (int)((value.Position.Y + 0.3f) / _cellSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int CountNearBy(Particle value)
|
||||||
|
{
|
||||||
|
return GetNearby(value).Count;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Updates the spatial relationships of objects. Rehash function
|
||||||
|
/// needed if elements change their position in the space.
|
||||||
|
/// </summary>
|
||||||
|
public void Rehash()
|
||||||
|
{
|
||||||
|
if (_table == null || _table.Count == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (int i = 0; i < _column; i++)
|
||||||
|
{
|
||||||
|
for (int j = 0; j < _row; j++)
|
||||||
|
{
|
||||||
|
if (_nearby[i][j] != null)
|
||||||
|
_nearby[i][j].Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (Particle particle in _table)
|
||||||
|
{
|
||||||
|
AddInterRadius(particle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Add element to its position and neighbor cells.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value"></param>
|
||||||
|
private void AddInterRadius(Particle value)
|
||||||
|
{
|
||||||
|
for (int i = -1; i < 2; ++i)
|
||||||
|
{
|
||||||
|
for (int j = -1; j < 2; ++j)
|
||||||
|
{
|
||||||
|
int x = posX(value) + i;
|
||||||
|
int y = posY(value) + j;
|
||||||
|
if (InRange(x, y))
|
||||||
|
_nearby[x][y].Add(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Check if a position is out of the spatial range
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="x"></param>
|
||||||
|
/// <param name="y"></param>
|
||||||
|
/// <returns>true if position is in range.</returns>
|
||||||
|
private bool InRange(float x, float y)
|
||||||
|
{
|
||||||
|
return (x > 0 && x < _column && y > 0 && y < _row);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerator<Particle> GetEnumerator()
|
||||||
|
{
|
||||||
|
return _table.GetEnumerator();
|
||||||
|
}
|
||||||
|
|
||||||
|
IEnumerator IEnumerable.GetEnumerator()
|
||||||
|
{
|
||||||
|
return GetEnumerator();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue