Example #1
0
		// update toppling, cant and spring
		internal static void UpdateTopplingCantAndSpring(Train Train, int CarIndex, double TimeElapsed)
		{
			if (TimeElapsed == 0.0 | TimeElapsed > 0.5)
			{
				return;
			}
			// get direction, up and side vectors
			double dx, dy, dz;
			double ux, uy, uz;
			double sx, sy, sz;
			{
				dx = Train.Cars[CarIndex].FrontAxle.Follower.WorldPosition.X - Train.Cars[CarIndex].RearAxle.Follower.WorldPosition.X;
				dy = Train.Cars[CarIndex].FrontAxle.Follower.WorldPosition.Y - Train.Cars[CarIndex].RearAxle.Follower.WorldPosition.Y;
				dz = Train.Cars[CarIndex].FrontAxle.Follower.WorldPosition.Z - Train.Cars[CarIndex].RearAxle.Follower.WorldPosition.Z;
				double t = 1.0 / Math.Sqrt(dx * dx + dy * dy + dz * dz);
				dx *= t; dy *= t; dz *= t;
				t = 1.0 / Math.Sqrt(dx * dx + dz * dz);
				double ex = dx * t;
				double ez = dz * t;
				sx = ez;
				sy = 0.0;
				sz = -ex;
				World.Cross(dx, dy, dz, sx, sy, sz, out ux, out uy, out uz);
			}
			// cant and radius
			double c;
			{
				double ca = Train.Cars[CarIndex].FrontAxle.Follower.CurveCant;
				double cb = Train.Cars[CarIndex].RearAxle.Follower.CurveCant;
				c = Math.Tan(0.5 * (Math.Atan(ca) + Math.Atan(cb)));
			}
			double r, rs;
			if (Train.Cars[CarIndex].FrontAxle.Follower.CurveRadius != 0.0 & Train.Cars[CarIndex].RearAxle.Follower.CurveRadius != 0.0)
			{
				r = Math.Sqrt(Math.Abs(Train.Cars[CarIndex].FrontAxle.Follower.CurveRadius * Train.Cars[CarIndex].RearAxle.Follower.CurveRadius));
				rs = (double)Math.Sign(Train.Cars[CarIndex].FrontAxle.Follower.CurveRadius + Train.Cars[CarIndex].RearAxle.Follower.CurveRadius);
			}
			else if (Train.Cars[CarIndex].FrontAxle.Follower.CurveRadius != 0.0)
			{
				r = Math.Abs(Train.Cars[CarIndex].FrontAxle.Follower.CurveRadius);
				rs = (double)Math.Sign(Train.Cars[CarIndex].FrontAxle.Follower.CurveRadius);
			}
			else if (Train.Cars[CarIndex].RearAxle.Follower.CurveRadius != 0.0)
			{
				r = Math.Abs(Train.Cars[CarIndex].RearAxle.Follower.CurveRadius);
				rs = (double)Math.Sign(Train.Cars[CarIndex].RearAxle.Follower.CurveRadius);
			}
			else
			{
				r = 0.0;
				rs = 0.0;
			}
			// roll due to shaking
			{

				double a0 = Train.Cars[CarIndex].Specs.CurrentRollDueToShakingAngle;
				double a1;
				if (Train.Cars[CarIndex].Specs.CurrentRollShakeDirection != 0.0)
				{
					const double c0 = 0.03;
					const double c1 = 0.15;
					a1 = c1 * Math.Atan(c0 * Train.Cars[CarIndex].Specs.CurrentRollShakeDirection);
					double d = 0.5 + Train.Cars[CarIndex].Specs.CurrentRollShakeDirection * Train.Cars[CarIndex].Specs.CurrentRollShakeDirection;
					if (Train.Cars[CarIndex].Specs.CurrentRollShakeDirection < 0.0)
					{
						Train.Cars[CarIndex].Specs.CurrentRollShakeDirection += d * TimeElapsed;
						if (Train.Cars[CarIndex].Specs.CurrentRollShakeDirection > 0.0) Train.Cars[CarIndex].Specs.CurrentRollShakeDirection = 0.0;
					}
					else
					{
						Train.Cars[CarIndex].Specs.CurrentRollShakeDirection -= d * TimeElapsed;
						if (Train.Cars[CarIndex].Specs.CurrentRollShakeDirection < 0.0) Train.Cars[CarIndex].Specs.CurrentRollShakeDirection = 0.0;
					}
				}
				else
				{
					a1 = 0.0;
				}
				double SpringAcceleration;
				if (!Train.Cars[CarIndex].Derailed)
				{
					SpringAcceleration = 15.0 * Math.Abs(a1 - a0);
				}
				else
				{
					SpringAcceleration = 1.5 * Math.Abs(a1 - a0);
				}
				double SpringDeceleration = 0.25 * SpringAcceleration;
				Train.Cars[CarIndex].Specs.CurrentRollDueToShakingAngularSpeed += (double)Math.Sign(a1 - a0) * SpringAcceleration * TimeElapsed;
				double x = (double)Math.Sign(Train.Cars[CarIndex].Specs.CurrentRollDueToShakingAngularSpeed) * SpringDeceleration * TimeElapsed;
				if (Math.Abs(x) < Math.Abs(Train.Cars[CarIndex].Specs.CurrentRollDueToShakingAngularSpeed))
				{
					Train.Cars[CarIndex].Specs.CurrentRollDueToShakingAngularSpeed -= x;
				}
				else
				{
					Train.Cars[CarIndex].Specs.CurrentRollDueToShakingAngularSpeed = 0.0;
				}
				a0 += Train.Cars[CarIndex].Specs.CurrentRollDueToShakingAngularSpeed * TimeElapsed;
				Train.Cars[CarIndex].Specs.CurrentRollDueToShakingAngle = a0;
			}
			// roll due to cant (incorporates shaking)
			{
				double cantAngle = Math.Atan(c / Game.RouteRailGauge);
				Train.Cars[CarIndex].Specs.CurrentRollDueToCantAngle = cantAngle + Train.Cars[CarIndex].Specs.CurrentRollDueToShakingAngle;
			}
			// pitch due to acceleration
			{
				for (int i = 0; i < 3; i++)
				{
					double a, v, j;
					if (i == 0)
					{
						a = Train.Cars[CarIndex].Specs.CurrentAcceleration;
						v = Train.Cars[CarIndex].Specs.CurrentPitchDueToAccelerationFastValue;
						j = 1.8;
					}
					else if (i == 1)
					{
						a = Train.Cars[CarIndex].Specs.CurrentPitchDueToAccelerationFastValue;
						v = Train.Cars[CarIndex].Specs.CurrentPitchDueToAccelerationMediumValue;
						j = 1.2;
					}
					else
					{
						a = Train.Cars[CarIndex].Specs.CurrentPitchDueToAccelerationFastValue;
						v = Train.Cars[CarIndex].Specs.CurrentPitchDueToAccelerationSlowValue;
						j = 1.0;
					}
					double d = a - v;
					if (d < 0.0)
					{
						v -= j * TimeElapsed;
						if (v < a) v = a;
					}
					else
					{
						v += j * TimeElapsed;
						if (v > a) v = a;
					}
					if (i == 0)
					{
						Train.Cars[CarIndex].Specs.CurrentPitchDueToAccelerationFastValue = v;
					}
					else if (i == 1)
					{
						Train.Cars[CarIndex].Specs.CurrentPitchDueToAccelerationMediumValue = v;
					}
					else
					{
						Train.Cars[CarIndex].Specs.CurrentPitchDueToAccelerationSlowValue = v;
					}
				}
				{
					double d = Train.Cars[CarIndex].Specs.CurrentPitchDueToAccelerationSlowValue - Train.Cars[CarIndex].Specs.CurrentPitchDueToAccelerationFastValue;
					Train.Cars[CarIndex].Specs.CurrentPitchDueToAccelerationTargetAngle = 0.03 * Math.Atan(d);
				}
				{
					double a = 3.0 * (double)Math.Sign(Train.Cars[CarIndex].Specs.CurrentPitchDueToAccelerationTargetAngle - Train.Cars[CarIndex].Specs.CurrentPitchDueToAccelerationAngle);
					Train.Cars[CarIndex].Specs.CurrentPitchDueToAccelerationAngularSpeed += a * TimeElapsed;
					double s = Math.Abs(Train.Cars[CarIndex].Specs.CurrentPitchDueToAccelerationTargetAngle - Train.Cars[CarIndex].Specs.CurrentPitchDueToAccelerationAngle);
					if (Math.Abs(Train.Cars[CarIndex].Specs.CurrentPitchDueToAccelerationAngularSpeed) > s)
					{
						Train.Cars[CarIndex].Specs.CurrentPitchDueToAccelerationAngularSpeed = s * (double)Math.Sign(Train.Cars[CarIndex].Specs.CurrentPitchDueToAccelerationAngularSpeed);
					}
					Train.Cars[CarIndex].Specs.CurrentPitchDueToAccelerationAngle += Train.Cars[CarIndex].Specs.CurrentPitchDueToAccelerationAngularSpeed * TimeElapsed;
				}
			}
			// derailment
			if (Interface.CurrentOptions.Derailments & !Train.Cars[CarIndex].Derailed)
			{
				double a = Train.Cars[CarIndex].Specs.CurrentRollDueToTopplingAngle + Train.Cars[CarIndex].Specs.CurrentRollDueToCantAngle;
				double sa = (double)Math.Sign(a);
				double tc = Train.Cars[CarIndex].Specs.CriticalTopplingAngle;
				if (a * sa > tc)
				{
					Train.Derail(CarIndex, TimeElapsed);
				}
			}
			// toppling roll
			if (Interface.CurrentOptions.Toppling | Train.Cars[CarIndex].Derailed)
			{
				double a = Train.Cars[CarIndex].Specs.CurrentRollDueToTopplingAngle;
				double ab = Train.Cars[CarIndex].Specs.CurrentRollDueToTopplingAngle + Train.Cars[CarIndex].Specs.CurrentRollDueToCantAngle;
				double h = Train.Cars[CarIndex].Specs.CenterOfGravityHeight;
				double s = Math.Abs(Train.Cars[CarIndex].Specs.CurrentSpeed);
				double rmax = 2.0 * h * s * s / (Game.RouteAccelerationDueToGravity * Game.RouteRailGauge);
				double ta;
				Train.Cars[CarIndex].Topples = false;
				if (Train.Cars[CarIndex].Derailed)
				{
					double sab = (double)Math.Sign(ab);
					ta = 0.5 * Math.PI * (sab == 0.0 ? Program.RandomNumberGenerator.NextDouble() < 0.5 ? -1.0 : 1.0 : sab);
				}
				else
				{
					if (r != 0.0)
					{
						if (r < rmax)
						{
							double s0 = Math.Sqrt(r * Game.RouteAccelerationDueToGravity * Game.RouteRailGauge / (2.0 * h));
							const double fac = 0.25; // arbitrary coefficient
							ta = -fac * (s - s0) * rs;
							Train.Topple(CarIndex, TimeElapsed);
						}
						else
						{
							ta = 0.0;
						}
					}
					else
					{
						ta = 0.0;
					}
				}
				double td;
				if (Train.Cars[CarIndex].Derailed)
				{
					td = Math.Abs(ab);
					if (td < 0.1) td = 0.1;
				}
				else
				{
					td = 1.0;
				}
				if (a > ta)
				{
					double d = a - ta;
					if (td > d) td = d;
					a -= td * TimeElapsed;
				}
				else if (a < ta)
				{
					double d = ta - a;
					if (td > d) td = d;
					a += td * TimeElapsed;
				}
				Train.Cars[CarIndex].Specs.CurrentRollDueToTopplingAngle = a;
			}
			else
			{
				Train.Cars[CarIndex].Specs.CurrentRollDueToTopplingAngle = 0.0;
			}
			// apply position due to cant/toppling
			{
				double a = Train.Cars[CarIndex].Specs.CurrentRollDueToTopplingAngle + Train.Cars[CarIndex].Specs.CurrentRollDueToCantAngle;
				double x = Math.Sign(a) * 0.5 * Game.RouteRailGauge * (1.0 - Math.Cos(a));
				double y = Math.Abs(0.5 * Game.RouteRailGauge * Math.Sin(a));
				double cx = sx * x + ux * y;
				double cy = sy * x + uy * y;
				double cz = sz * x + uz * y;
				Train.Cars[CarIndex].FrontAxle.Follower.WorldPosition.X += cx;
				Train.Cars[CarIndex].FrontAxle.Follower.WorldPosition.Y += cy;
				Train.Cars[CarIndex].FrontAxle.Follower.WorldPosition.Z += cz;
				Train.Cars[CarIndex].RearAxle.Follower.WorldPosition.X += cx;
				Train.Cars[CarIndex].RearAxle.Follower.WorldPosition.Y += cy;
				Train.Cars[CarIndex].RearAxle.Follower.WorldPosition.Z += cz;
			}
			// apply rolling
			{
				double a = -Train.Cars[CarIndex].Specs.CurrentRollDueToTopplingAngle - Train.Cars[CarIndex].Specs.CurrentRollDueToCantAngle;
				double cosa = Math.Cos(a);
				double sina = Math.Sin(a);
				World.Rotate(ref sx, ref sy, ref sz, dx, dy, dz, cosa, sina);
				World.Rotate(ref ux, ref uy, ref uz, dx, dy, dz, cosa, sina);
				Train.Cars[CarIndex].Up.X = ux;
				Train.Cars[CarIndex].Up.Y = uy;
				Train.Cars[CarIndex].Up.Z = uz;
			}
			// apply pitching
			if (Train.Cars[CarIndex].CurrentCarSection >= 0 && Train.Cars[CarIndex].CarSections[Train.Cars[CarIndex].CurrentCarSection].Overlay)
			{
				double a = Train.Cars[CarIndex].Specs.CurrentPitchDueToAccelerationAngle;
				double cosa = Math.Cos(a);
				double sina = Math.Sin(a);
				World.Rotate(ref dx, ref dy, ref dz, sx, sy, sz, cosa, sina);
				World.Rotate(ref ux, ref uy, ref uz, sx, sy, sz, cosa, sina);
				double cx = 0.5 * (Train.Cars[CarIndex].FrontAxle.Follower.WorldPosition.X + Train.Cars[CarIndex].RearAxle.Follower.WorldPosition.X);
				double cy = 0.5 * (Train.Cars[CarIndex].FrontAxle.Follower.WorldPosition.Y + Train.Cars[CarIndex].RearAxle.Follower.WorldPosition.Y);
				double cz = 0.5 * (Train.Cars[CarIndex].FrontAxle.Follower.WorldPosition.Z + Train.Cars[CarIndex].RearAxle.Follower.WorldPosition.Z);
				Train.Cars[CarIndex].FrontAxle.Follower.WorldPosition.X -= cx;
				Train.Cars[CarIndex].FrontAxle.Follower.WorldPosition.Y -= cy;
				Train.Cars[CarIndex].FrontAxle.Follower.WorldPosition.Z -= cz;
				Train.Cars[CarIndex].RearAxle.Follower.WorldPosition.X -= cx;
				Train.Cars[CarIndex].RearAxle.Follower.WorldPosition.Y -= cy;
				Train.Cars[CarIndex].RearAxle.Follower.WorldPosition.Z -= cz;
				World.Rotate(ref Train.Cars[CarIndex].FrontAxle.Follower.WorldPosition, sx, sy, sz, cosa, sina);
				World.Rotate(ref Train.Cars[CarIndex].RearAxle.Follower.WorldPosition, sx, sy, sz, cosa, sina);
				Train.Cars[CarIndex].FrontAxle.Follower.WorldPosition.X += cx;
				Train.Cars[CarIndex].FrontAxle.Follower.WorldPosition.Y += cy;
				Train.Cars[CarIndex].FrontAxle.Follower.WorldPosition.Z += cz;
				Train.Cars[CarIndex].RearAxle.Follower.WorldPosition.X += cx;
				Train.Cars[CarIndex].RearAxle.Follower.WorldPosition.Y += cy;
				Train.Cars[CarIndex].RearAxle.Follower.WorldPosition.Z += cz;
				Train.Cars[CarIndex].Up.X = ux;
				Train.Cars[CarIndex].Up.Y = uy;
				Train.Cars[CarIndex].Up.Z = uz;
			}
			// spring sound
			{
				double a = Train.Cars[CarIndex].Specs.CurrentRollDueToShakingAngle;
				double diff = a - Train.Cars[CarIndex].Sounds.SpringPlayedAngle;
				const double angleTolerance = 0.001;
				if (diff < -angleTolerance)
				{
					Sounds.SoundBuffer buffer = Train.Cars[CarIndex].Sounds.SpringL.Buffer;
					if (buffer != null)
					{
						if (!Sounds.IsPlaying(Train.Cars[CarIndex].Sounds.SpringL.Source))
						{
							OpenBveApi.Math.Vector3 pos = Train.Cars[CarIndex].Sounds.SpringL.Position;
							Train.Cars[CarIndex].Sounds.SpringL.Source = Sounds.PlaySound(buffer, 1.0, 1.0, pos, Train, CarIndex, false);
						}
					}
					Train.Cars[CarIndex].Sounds.SpringPlayedAngle = a;
				}
				else if (diff > angleTolerance)
				{
					Sounds.SoundBuffer buffer = Train.Cars[CarIndex].Sounds.SpringR.Buffer;
					if (buffer != null)
					{
						if (!Sounds.IsPlaying(Train.Cars[CarIndex].Sounds.SpringR.Source))
						{
							OpenBveApi.Math.Vector3 pos = Train.Cars[CarIndex].Sounds.SpringR.Position;
							Train.Cars[CarIndex].Sounds.SpringR.Source = Sounds.PlaySound(buffer, 1.0, 1.0, pos, Train, CarIndex, false);
						}
					}
					Train.Cars[CarIndex].Sounds.SpringPlayedAngle = a;
				}
			}
			// flange sound
			{
				/*
				 * This determines the amount of flange noise as a result of the angle at which the
				 * line that forms between the axles hits the rail, i.e. the less perpendicular that
				 * line is to the rails, the more flange noise there will be.
				 * */
				Vector3 d = Train.Cars[CarIndex].FrontAxle.Follower.WorldPosition - Train.Cars[CarIndex].RearAxle.Follower.WorldPosition;
				World.Normalize(ref d.X, ref d.Y, ref d.Z);
				double b0 = d.X * Train.Cars[CarIndex].RearAxle.Follower.WorldSide.X + d.Y * Train.Cars[CarIndex].RearAxle.Follower.WorldSide.Y + d.Z * Train.Cars[CarIndex].RearAxle.Follower.WorldSide.Z;
				double b1 = d.X * Train.Cars[CarIndex].FrontAxle.Follower.WorldSide.X + d.Y * Train.Cars[CarIndex].FrontAxle.Follower.WorldSide.Y + d.Z * Train.Cars[CarIndex].FrontAxle.Follower.WorldSide.Z;
				double spd = Math.Abs(Train.Cars[CarIndex].Specs.CurrentSpeed);
				double pitch = 0.5 + 0.04 * spd;
				double b2 = Math.Abs(b0) + Math.Abs(b1);
				double basegain = 0.5 * b2 * b2 * spd * spd;
				/*
				 * This determines additional flange noise as a result of the roll angle of the car
				 * compared to the roll angle of the rails, i.e. if the car bounces due to inaccuracies,
				 * there will be additional flange noise.
				 * */
				double cdti = Math.Abs(Train.Cars[CarIndex].FrontAxle.Follower.CantDueToInaccuracy) + Math.Abs(Train.Cars[CarIndex].RearAxle.Follower.CantDueToInaccuracy);
				basegain += 0.2 * spd * spd * cdti * cdti;
				/*
				 * This applies the settings.
				 * */
				if (basegain < 0.0) basegain = 0.0;
				if (basegain > 0.75) basegain = 0.75;
				if (pitch > Train.Cars[CarIndex].Sounds.FlangePitch)
				{
					Train.Cars[CarIndex].Sounds.FlangePitch += TimeElapsed;
					if (Train.Cars[CarIndex].Sounds.FlangePitch > pitch) Train.Cars[CarIndex].Sounds.FlangePitch = pitch;
				}
				else
				{
					Train.Cars[CarIndex].Sounds.FlangePitch -= TimeElapsed;
					if (Train.Cars[CarIndex].Sounds.FlangePitch < pitch) Train.Cars[CarIndex].Sounds.FlangePitch = pitch;
				}
				pitch = Train.Cars[CarIndex].Sounds.FlangePitch;
				for (int i = 0; i < Train.Cars[CarIndex].Sounds.Flange.Length; i++)
				{
					if (i == Train.Cars[CarIndex].Sounds.FrontAxleFlangeIndex | i == Train.Cars[CarIndex].Sounds.RearAxleFlangeIndex)
					{
						Train.Cars[CarIndex].Sounds.FlangeVolume[i] += TimeElapsed;
						if (Train.Cars[CarIndex].Sounds.FlangeVolume[i] > 1.0) Train.Cars[CarIndex].Sounds.FlangeVolume[i] = 1.0;
					}
					else
					{
						Train.Cars[CarIndex].Sounds.FlangeVolume[i] -= TimeElapsed;
						if (Train.Cars[CarIndex].Sounds.FlangeVolume[i] < 0.0) Train.Cars[CarIndex].Sounds.FlangeVolume[i] = 0.0;
					}
					double gain = basegain * Train.Cars[CarIndex].Sounds.FlangeVolume[i];
					if (Sounds.IsPlaying(Train.Cars[CarIndex].Sounds.Flange[i].Source))
					{
						if (pitch > 0.01 & gain > 0.0001)
						{
							Train.Cars[CarIndex].Sounds.Flange[i].Source.Pitch = pitch;
							Train.Cars[CarIndex].Sounds.Flange[i].Source.Volume = gain;
						}
						else
						{
							Train.Cars[CarIndex].Sounds.Flange[i].Source.Stop();
						}
					}
					else if (pitch > 0.02 & gain > 0.01)
					{
						Sounds.SoundBuffer buffer = Train.Cars[CarIndex].Sounds.Flange[i].Buffer;
						if (buffer != null)
						{
							OpenBveApi.Math.Vector3 pos = Train.Cars[CarIndex].Sounds.Flange[i].Position;
							Train.Cars[CarIndex].Sounds.Flange[i].Source = Sounds.PlaySound(buffer, pitch, gain, pos, Train, CarIndex, true);
						}
					}
				}
			}
		}