-
Notifications
You must be signed in to change notification settings - Fork 363
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
12 changed files
with
1,088 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); | ||
} | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 ); | ||
} | ||
} | ||
} | ||
|
||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} | ||
|
||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 ) ); | ||
} | ||
} | ||
} | ||
} |
Oops, something went wrong.