/// <summary>
/// Returns the vector force that a Magnet should exert on a Particle
/// </summary>
/// <param name="cMagnet">The Magnet affecting the Particle</param>
/// <param name="cParticle">The Particle being affected by the Magnet</param>
/// <returns>Returns the vector force that a Magnet should exert on a Particle</returns>
protected Vector3 CalculateForceMagnetShouldExertOnParticle(DefaultParticleSystemMagnet cMagnet, DPSFDefaultBaseParticle cParticle)
{
// Variable to store the Force to Exert on the Particle
Vector3 sForceToExertOnParticle = Vector3.Zero;
// Calculate which Direction to push the Particle
Vector3 sDirectionToPushParticle;
// If this is a Point Magnet
if (cMagnet.MagnetType == DefaultParticleSystemMagnet.MagnetTypes.PointMagnet)
{
// Cast the Magnet to the proper type
MagnetPoint cPointMagnet = (MagnetPoint)cMagnet;
// Calculate the direction to attract the Particle to the point in space where the Magnet is
sDirectionToPushParticle = cPointMagnet.PositionData.Position - cParticle.Position;
}
// Else If this is a Line Magnet
else if (cMagnet.MagnetType == DefaultParticleSystemMagnet.MagnetTypes.LineMagnet)
{
// Cast the Magnet to the proper type
MagnetLine cLineMagnet = (MagnetLine)cMagnet;
// Calculate the closest point on the Line to the Particle.
// Equation taken from http://ozviz.wasp.uwa.edu.au/~pbourke/geometry/pointline/
// Also explained at http://www.allegro.cc/forums/thread/589720
// Calculate 2 points on the Line
Vector3 sPosition1 = cLineMagnet.PositionOnLine;
Vector3 sPosition2 = cLineMagnet.PositionOnLine + cLineMagnet.Direction;
// Put calculations into temp variables for speed and easy readability
float fA = cParticle.Position.X - sPosition1.X;
float fB = cParticle.Position.Y - sPosition1.Y;
float fC = cParticle.Position.Z - sPosition1.Z;
float fD = sPosition2.X - sPosition1.X;
float fE = sPosition2.Y - sPosition1.Y;
float fF = sPosition2.Z - sPosition1.Z;
// Next calculate the value of U.
// NOTE: The Direction is normalized, so the distance between Position1 and Position2 is one, so we
// don't need to bother squaring and dividing by the length here.
float fU = (fA * fD) + (fB * fE) + (fC * fF);
// Calculate the closest point on the Line to the Particle
Vector3 sClosestPointOnLine = new Vector3();
sClosestPointOnLine.X = sPosition1.X + (fU * fD);
sClosestPointOnLine.Y = sPosition1.Y + (fU * fE);
sClosestPointOnLine.Z = sPosition1.Z + (fU * fF);
// Calculate the direction to attract the Particle to the closest point on the Line
sDirectionToPushParticle = sClosestPointOnLine - cParticle.Position;
}
// Else if the is a Line Segment Magnet
else if (cMagnet.MagnetType == DefaultParticleSystemMagnet.MagnetTypes.LineSegmentMagnet)
{
// Cast the Magnet to the proper type
MagnetLineSegment cLineSegmentMagnet = (MagnetLineSegment)cMagnet;
// Calculate the closest point on the Line to the Particle.
// Equation taken from http://ozviz.wasp.uwa.edu.au/~pbourke/geometry/pointline/
// Also explained at http://www.allegro.cc/forums/thread/589720
// Calculate 2 points on the Line
Vector3 sPosition1 = cLineSegmentMagnet.EndPoint1;
Vector3 sPosition2 = cLineSegmentMagnet.EndPoint2;
// Put calculations into temp variables for speed and easy readability
float fA = cParticle.Position.X - sPosition1.X;
float fB = cParticle.Position.Y - sPosition1.Y;
float fC = cParticle.Position.Z - sPosition1.Z;
float fD = sPosition2.X - sPosition1.X;
float fE = sPosition2.Y - sPosition1.Y;
float fF = sPosition2.Z - sPosition1.Z;
// Next calculate the value of U
float fDot = (fA * fD) + (fB * fE) + (fC * fF);
float fLengthSquared = (fD * fD) + (fE * fE) + (fF * fF);
float fU = fDot / fLengthSquared;
// Calculate the closest point on the Line to the Particle
Vector3 sClosestPointOnLine = new Vector3();
// If the Particle is closest to the first End Point
if (fU < 0.0f)
{
sClosestPointOnLine = sPosition1;
}
// Else If the Particle is closest to the second End Point
else if (fU > 1.0f)
{
sClosestPointOnLine = sPosition2;
}
// Else the Particle is closest to the Line Segment somewhere between the End Points
else
{
// Calculate where in between the End Points the Particle is closest to
sClosestPointOnLine.X = sPosition1.X + (fU * (sPosition2.X - sPosition1.X));
sClosestPointOnLine.Y = sPosition1.Y + (fU * (sPosition2.Y - sPosition1.Y));
sClosestPointOnLine.Z = sPosition1.Z + (fU * (sPosition2.Z - sPosition1.Z));
}
// Calculate the direction to attract the Particle to the closest point on the Line
sDirectionToPushParticle = sClosestPointOnLine - cParticle.Position;
}
// Else If this is a Plane Magnet
else if (cMagnet.MagnetType == DefaultParticleSystemMagnet.MagnetTypes.PlaneMagnet)
{
// Cast the Magnet to the proper type
MagnetPlane cPlaneMagnet = (MagnetPlane)cMagnet;
// Calculate the closest point on the Plane to the Particle.
// Equation taken from http://ozviz.wasp.uwa.edu.au/~pbourke/geometry/pointline/
// Calculate how far from the Plane the Particle is
float fDistanceFromPlane = Vector3.Dot(cParticle.Position - cPlaneMagnet.PositionOnPlane, cPlaneMagnet.Normal);
// Calculate the closest point on the Plane to the Particle
Vector3 sClosestPointOnPlane = cParticle.Position + (-cPlaneMagnet.Normal * fDistanceFromPlane);
// Calculate the direction to attract the Particle to the closest point on the Plane
sDirectionToPushParticle = sClosestPointOnPlane - cParticle.Position;
}
// Else we don't know what kind of Magnet this is
else
{
// So exit returning no force
return(Vector3.Zero);
}
// If the Particle should be Repelled away from the Magnet (instead of attracted to it)
if (cMagnet.Mode == DefaultParticleSystemMagnet.MagnetModes.Repel)
{
// Reverse the direction we are going to push the Particle
sDirectionToPushParticle *= -1;
}
// If the Direction To Push the Particle is not valid and we should be Repelling the Particle
if (sDirectionToPushParticle == Vector3.Zero && cMagnet.Mode == DefaultParticleSystemMagnet.MagnetModes.Repel)
{
// Pick a random Direction vector with a very short length to repel the Particle with
sDirectionToPushParticle = DPSFHelper.RandomNormalizedVector() * 0.00001f;
}
// Get how far away the Particle is from the Magnet
float fDistanceFromMagnet = sDirectionToPushParticle.Length();
// If the Particle is within range to be affected by the Magnet
if (fDistanceFromMagnet >= cMagnet.MinDistance && fDistanceFromMagnet <= cMagnet.MaxDistance)
{
// If the Direction To Push the Particle is valid
if (sDirectionToPushParticle != Vector3.Zero)
{
// Normalize the Direction To Push the Particle
sDirectionToPushParticle.Normalize();
}
// Calculate the normalized distance from the Magnet that the Particle is
float fLerpAmount = 0.0f;
if (cMagnet.MaxDistance != cMagnet.MinDistance)
{
fLerpAmount = (fDistanceFromMagnet - cMagnet.MinDistance) / (cMagnet.MaxDistance - cMagnet.MinDistance);
}
// Else the Max Distance equals the Min Distance
else
{
// So to avoid a divide by zero we just assume a full Lerp amount
fLerpAmount = 1.0f;
}
// Calculate how much of the Max Force to apply to the Particle
float fNormalizedForce = 0.0f;
switch (cMagnet.DistanceFunction)
{
default:
case DefaultParticleSystemMagnet.DistanceFunctions.Constant:
fNormalizedForce = cMagnet.MaxForce;
break;
case DefaultParticleSystemMagnet.DistanceFunctions.Linear:
fNormalizedForce = MathHelper.Lerp(0, cMagnet.MaxForce, fLerpAmount);
break;
case DefaultParticleSystemMagnet.DistanceFunctions.Squared:
fNormalizedForce = MathHelper.Lerp(0, cMagnet.MaxForce, fLerpAmount * fLerpAmount);
break;
case DefaultParticleSystemMagnet.DistanceFunctions.Cubed:
fNormalizedForce = MathHelper.Lerp(0, cMagnet.MaxForce, fLerpAmount * fLerpAmount * fLerpAmount);
break;
case DefaultParticleSystemMagnet.DistanceFunctions.LinearInverse:
fNormalizedForce = MathHelper.Lerp(cMagnet.MaxForce, 0, fLerpAmount);
break;
case DefaultParticleSystemMagnet.DistanceFunctions.SquaredInverse:
fNormalizedForce = MathHelper.Lerp(cMagnet.MaxForce, 0, fLerpAmount * fLerpAmount);
break;
case DefaultParticleSystemMagnet.DistanceFunctions.CubedInverse:
fNormalizedForce = MathHelper.Lerp(cMagnet.MaxForce, 0, fLerpAmount * fLerpAmount * fLerpAmount);
break;
}
// Calculate how much Force should be Exerted on the Particle
sForceToExertOnParticle = sDirectionToPushParticle * (fNormalizedForce * cMagnet.MaxForce);
}
// Return how much Force to Exert on the Particle
return(sForceToExertOnParticle);
}