Skip to content

Commit

Permalink
introducing Nez Verlet physics!
Browse files Browse the repository at this point in the history
  • Loading branch information
prime31 committed Oct 9, 2016
1 parent b523aa8 commit 123efd2
Show file tree
Hide file tree
Showing 12 changed files with 1,088 additions and 2 deletions.
15 changes: 13 additions & 2 deletions Nez-PCL/Nez.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@
<Folder Include="Input\" />
<Folder Include="Math\" />
<Folder Include="Physics\" />
<Folder Include="Stuff\" />
<Folder Include="Utils\" />
<Folder Include="PipelineRuntime\" />
<Folder Include="Utils\DebugConsole\" />
Expand Down Expand Up @@ -91,6 +90,7 @@
<Folder Include="Graphics\Materials\" />
<Folder Include="AI\Pathfinding\Dijkstra\" />
<Folder Include="ECS\Components\Renderables\3D\" />
<Folder Include="Physics\Verlet\" />
<Folder Include="Physics\Shapes\ShapeCollisions\" />
</ItemGroup>
<ItemGroup>
Expand Down Expand Up @@ -123,7 +123,6 @@
<Compile Include="Physics\Physics.cs" />
<Compile Include="Physics\Ray2D.cs" />
<Compile Include="Physics\SpatialHash.cs" />
<Compile Include="Stuff\IMGUI\IMGUI.cs" />
<Compile Include="Utils\Emitter.cs" />
<Compile Include="Utils\Time.cs" />
<Compile Include="Core.cs" />
Expand Down Expand Up @@ -508,12 +507,24 @@
<Compile Include="ECS\Components\Renderables\LineRenderer.cs" />
<Compile Include="Utils\Extensions\Vector3Ext.cs" />
<Compile Include="ECS\Components\Renderables\PolygonMesh.cs" />
<Compile Include="Physics\Verlet\Composites\Ball.cs" />
<Compile Include="Physics\Verlet\Composites\Cloth.cs" />
<Compile Include="Physics\Verlet\Composites\Composite.cs" />
<Compile Include="Physics\Verlet\Composites\LineSegments.cs" />
<Compile Include="Physics\Verlet\Composites\Tire.cs" />
<Compile Include="Physics\Verlet\Composites\Tree.cs" />
<Compile Include="Physics\Verlet\Constraints\AngleConstraint.cs" />
<Compile Include="Physics\Verlet\Constraints\Constraint.cs" />
<Compile Include="Physics\Verlet\Constraints\DistanceConstraint.cs" />
<Compile Include="Physics\Verlet\Particle.cs" />
<Compile Include="Physics\Verlet\World.cs" />
<Compile Include="Physics\Shapes\ShapeCollisions\ShapeCollisionsPolygon.cs" />
<Compile Include="Physics\Shapes\ShapeCollisions\ShapeCollisionsBox.cs" />
<Compile Include="Physics\Shapes\ShapeCollisions\ShapeCollisionsCircle.cs" />
<Compile Include="Physics\Shapes\ShapeCollisions\ShapeCollisionsLine.cs" />
<Compile Include="Physics\Shapes\ShapeCollisions\ShapeCollisionsPoint.cs" />
<Compile Include="Debug\DefaultColors.cs" />
<Compile Include="UI\IMGUI.cs" />
</ItemGroup>
<ItemGroup>
<Reference Include="MonoGame.Framework">
Expand Down
16 changes: 16 additions & 0 deletions Nez-PCL/Physics/Verlet/Composites/Ball.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using Microsoft.Xna.Framework;


namespace Nez.Verlet
{
/// <summary>
/// single Particle composite
/// </summary>
public class Ball : Composite
{
public Ball( Vector2 position, float radius = 10 )
{
addParticle( new Particle( position ) ).radius = radius;
}
}
}
48 changes: 48 additions & 0 deletions Nez-PCL/Physics/Verlet/Composites/Cloth.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
using Microsoft.Xna.Framework;


namespace Nez.Verlet
{
public class Cloth : Composite
{
/// <summary>
/// creates a Cloth. If connectHorizontalParticles is false it will not link horizontal Particles and create a hair-like cloth
/// </summary>
/// <param name="topLeftPosition">Top left position.</param>
/// <param name="width">Width.</param>
/// <param name="height">Height.</param>
/// <param name="segments">Segments.</param>
/// <param name="stiffness">Stiffness.</param>
/// <param name="tearSensitivity">Tear sensitivity.</param>
/// <param name="connectHorizontalParticles">If set to <c>true</c> connect horizontal particles.</param>
public Cloth( Vector2 topLeftPosition, float width, float height, int segments = 20, float stiffness = 0.25f, float tearSensitivity = 5, bool connectHorizontalParticles = true )
{
var xStride = width / segments;
var yStride = height / segments;

for( var y = 0; y < segments; y++ )
{
for( var x = 0; x < segments; x++ )
{
var px = topLeftPosition.X + x * xStride;
var py = topLeftPosition.Y + y * yStride;
var particle = addParticle( new Particle( new Vector2( px, py ) ) );

// remove this constraint to make only vertical constaints for a hair-like cloth
if( connectHorizontalParticles && x > 0 )
addConstraint( new DistanceConstraint( particles[y * segments + x], particles[y * segments + x - 1], stiffness ) )
.setTearSensitvity( tearSensitivity )
.setCollidesWithColliders( false );

if( y > 0 )
addConstraint( new DistanceConstraint( particles[y * segments + x], particles[( y - 1 ) * segments + x], stiffness ) )
.setTearSensitvity( tearSensitivity )
.setCollidesWithColliders( false );

if( y == 0 )
particle.pin();
}
}
}
}
}
185 changes: 185 additions & 0 deletions Nez-PCL/Physics/Verlet/Composites/Composite.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
using System.Runtime.CompilerServices;
using Microsoft.Xna.Framework;


namespace Nez.Verlet
{
/// <summary>
/// represents an object in the Verlet world. Consists of Particles and Constraints and handles updating them
/// </summary>
public class Composite
{
/// <summary>
/// friction applied to all Particle movement to dampen it. Value should be very close to 1.
/// </summary>
public Vector2 friction = new Vector2( 0.98f, 1 );

/// <summary>
/// should Particles be rendered when doing a debugRender?
/// </summary>
public bool drawParticles = true;

/// <summary>
/// should Constraints be rendered when doing a debugRender?
/// </summary>
public bool drawConstraints = true;

/// <summary>
/// layer mask of all the layers this Collider should collide with when Entity.move methods are used. defaults to all layers.
/// </summary>
public int collidesWithLayers = Physics.allLayers;

public FastList<Particle> particles = new FastList<Particle>();
FastList<Constraint> _constraints = new FastList<Constraint>();


#region Particle/Constraint management

/// <summary>
/// adds a Particle to the Composite
/// </summary>
/// <returns>The particle.</returns>
/// <param name="particle">Particle.</param>
public Particle addParticle( Particle particle )
{
particles.add( particle );
return particle;
}


/// <summary>
/// removes the Particle from the Composite
/// </summary>
/// <param name="particle">Particle.</param>
public void removeParticle( Particle particle )
{
particles.remove( particle );
}


/// <summary>
/// removes all Particles and Constraints from the Composite
/// </summary>
public void removeAll()
{
particles.clear();
_constraints.clear();
}


/// <summary>
/// adds a Constraint to the Composite
/// </summary>
/// <returns>The constraint.</returns>
/// <param name="constraint">Constraint.</param>
/// <typeparam name="T">The 1st type parameter.</typeparam>
public T addConstraint<T>( T constraint ) where T : Constraint
{
_constraints.add( constraint );
constraint.composite = this;
return constraint;
}


/// <summary>
/// removes a Constraint from the Composite
/// </summary>
/// <param name="constraint">Constraint.</param>
public void removeConstraint( Constraint constraint )
{
_constraints.remove( constraint );
}

#endregion


/// <summary>
/// applies a force to all Particles in this Composite
/// </summary>
/// <param name="force">Force.</param>
public void applyForce( Vector2 force )
{
for( var j = 0; j < particles.length; j++ )
particles.buffer[j].applyForce( force );
}


/// <summary>
/// handles solving all Constraints
/// </summary>
[MethodImpl( MethodImplOptions.AggressiveInlining )]
public void solveConstraints()
{
// loop backwards in case any Constraints break and are removed
for( var i = _constraints.length - 1; i >= 0; i-- )
_constraints.buffer[i].solve();
}


/// <summary>
/// applies gravity to each Particle and does the verlet integration
/// </summary>
/// <param name="deltaTimeSquared">Delta time.</param>
/// <param name="gravity">Gravity.</param>
[MethodImpl( MethodImplOptions.AggressiveInlining )]
public void updateParticles( float deltaTimeSquared, Vector2 gravity )
{
for( var j = 0; j < particles.length; j++ )
{
var p = particles.buffer[j];
if( p.isPinned )
{
p.position = p.pinnedPosition;
continue;
}

p.applyForce( p.mass * gravity );

// calculate velocity and dampen it with friction
var vel = ( p.position - p.lastPosition ) * friction;

// calculate the next position using Verlet Integration
var nextPos = p.position + vel + 0.5f * p.acceleration * deltaTimeSquared;

// reset variables
p.lastPosition = p.position;
p.position = nextPos;
p.acceleration.X = p.acceleration.Y = 0;
}
}


[MethodImpl( MethodImplOptions.AggressiveInlining )]
public void handleConstraintCollisions()
{
// loop backwards in case any Constraints break and are removed
for( var i = _constraints.length - 1; i >= 0; i-- )
{
if( _constraints.buffer[i].collidesWithColliders )
_constraints.buffer[i].handleCollisions( collidesWithLayers );
}
}


public void debugRender( Batcher batcher )
{
if( drawConstraints )
{
for( var i = 0; i < _constraints.length; i++ )
_constraints.buffer[i].debugRender( batcher );
}

if( drawParticles )
{
for( var i = 0; i < particles.length; i++ )
{
if( particles.buffer[i].radius == 0 )
batcher.drawPixel( particles.buffer[i].position, DefaultColors.verletParticle, 4 );
else
batcher.drawCircle( particles.buffer[i].position, (int)particles.buffer[i].radius, DefaultColors.verletParticle, 1, 4 );
}
}
}

}
}
35 changes: 35 additions & 0 deletions Nez-PCL/Physics/Verlet/Composites/LineSegments.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
using Microsoft.Xna.Framework;


namespace Nez.Verlet
{
/// <summary>
/// a series of points connected with DistanceConstraints
/// </summary>
public class LineSegments : Composite
{
public LineSegments( Vector2[] vertices, float stiffness )
{
for( var i = 0; i < vertices.Length; i++ )
{
var p = new Particle( vertices[i] );
addParticle( p );

if( i > 0 )
addConstraint( new DistanceConstraint( particles.buffer[i], particles.buffer[i - 1], stiffness ) );
}
}


/// <summary>
/// pins the Particle at the given index
/// </summary>
/// <param name="index">Index.</param>
public LineSegments pinParticleAtIndex( int index )
{
particles.buffer[index].pin();
return this;
}

}
}
31 changes: 31 additions & 0 deletions Nez-PCL/Physics/Verlet/Composites/Tire.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using Microsoft.Xna.Framework;


namespace Nez.Verlet
{
public class Tire : Composite
{
public Tire( Vector2 origin, float radius, int segments, float spokeStiffness = 1, float treadStiffness = 1 )
{
var stride = 2 * MathHelper.Pi / segments;

// particles
for( var i = 0; i < segments; i++ )
{
var theta = i * stride;
addParticle( new Particle( new Vector2( origin.X + Mathf.cos( theta ) * radius, origin.Y + Mathf.sin( theta ) * radius ) ) );
}

var centerParticle = addParticle( new Particle( origin ) );

// constraints
for( var i = 0; i < segments; i++ )
{
addConstraint( new DistanceConstraint( particles[i], particles[( i + 1 ) % segments], treadStiffness ) );
addConstraint( new DistanceConstraint( particles[i], centerParticle, spokeStiffness ) )
.setCollidesWithColliders( false );
addConstraint( new DistanceConstraint( particles[i], particles[( i + 5 ) % segments], treadStiffness ) );
}
}
}
}
Loading

0 comments on commit 123efd2

Please sign in to comment.