Agregada funciones de Fluidos
This commit is contained in:
parent
20bdad509b
commit
d1ec333243
|
@ -77,13 +77,13 @@
|
|||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Aether.Physics2D" Version="2.1.0" />
|
||||
<PackageReference Include="ClosedXML" Version="0.104.2" />
|
||||
<PackageReference Include="Aether.Physics2D" Version="2.2.0" />
|
||||
<PackageReference Include="ClosedXML" Version="0.105.0-rc" />
|
||||
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0" />
|
||||
<PackageReference Include="Emgu.CV" Version="4.9.0.5494" />
|
||||
<PackageReference Include="Emgu.CV.runtime.windows" Version="4.9.0.5494" />
|
||||
<PackageReference Include="Emgu.CV.UI" Version="4.9.0.5494" />
|
||||
<PackageReference Include="Extended.Wpf.Toolkit" Version="4.6.1" />
|
||||
<PackageReference Include="Emgu.CV" Version="4.10.0.5680" />
|
||||
<PackageReference Include="Emgu.CV.runtime.windows" Version="4.10.0.5680" />
|
||||
<PackageReference Include="Emgu.CV.UI" Version="4.10.0.5680" />
|
||||
<PackageReference Include="Extended.Wpf.Toolkit" Version="4.7.25104.5739" />
|
||||
<PackageReference Include="LanguageDetection" Version="1.2.0" />
|
||||
<PackageReference Include="LiveChartsCore.SkiaSharpView.WPF" Version="2.0.0-rc4.5" />
|
||||
<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.Tag = this; // Importante para la identificación durante la colisión
|
||||
// Configurar la fricción
|
||||
Body.SetFriction(0.2f);
|
||||
//Body.SetFriction(0.2f);
|
||||
// Configurar amortiguamiento
|
||||
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.SetRestitution(0f); // Baja restitución para menos rebote
|
||||
//Body.SetRestitution(0f); // Baja restitución para menos rebote
|
||||
Body.SleepingAllowed = false;
|
||||
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