/// <summary>This method should be called once a frame to update the position, speed and state of all trains within the simulation</summary> /// <param name="TimeElapsed">The time elapsed since the last call to this function</param> internal static void UpdateTrains(double TimeElapsed) { for (int i = 0; i < Trains.Length; i++) { Trains[i].Update(TimeElapsed); } // ReSharper disable once PossibleInvalidCastExceptionInForeachLoop foreach (TrackFollowingObject Train in TFOs) //Must not use var, as otherwise the wrong inferred type { Train.Update(TimeElapsed); } // detect collision if (!Game.MinimalisticSimulation & Interface.CurrentOptions.Collisions) { //for (int i = 0; i < Trains.Length; i++) { System.Threading.Tasks.Parallel.For(0, Trains.Length, i => { // with other trains if (Trains[i].State == TrainState.Available) { double a = Trains[i].FrontCarTrackPosition(); double b = Trains[i].RearCarTrackPosition(); for (int j = i + 1; j < Trains.Length; j++) { if (Trains[j].State == TrainState.Available) { double c = Trains[j].FrontCarTrackPosition(); double d = Trains[j].RearCarTrackPosition(); if (a > d & b < c) { if (a > c) { // i > j int k = Trains[i].Cars.Length - 1; if (Trains[i].Cars[k].CurrentSpeed < Trains[j].Cars[0].CurrentSpeed) { double v = Trains[j].Cars[0].CurrentSpeed - Trains[i].Cars[k].CurrentSpeed; double s = (Trains[i].Cars[k].CurrentSpeed * Trains[i].Cars[k].CurrentMass + Trains[j].Cars[0].CurrentSpeed * Trains[j].Cars[0].CurrentMass) / (Trains[i].Cars[k].CurrentMass + Trains[j].Cars[0].CurrentMass); Trains[i].Cars[k].CurrentSpeed = s; Trains[j].Cars[0].CurrentSpeed = s; double e = 0.5 * (c - b) + 0.0001; Trains[i].Cars[k].FrontAxle.Follower.UpdateRelative(e, false, false); Trains[i].Cars[k].RearAxle.Follower.UpdateRelative(e, false, false); Trains[j].Cars[0].FrontAxle.Follower.UpdateRelative(-e, false, false); Trains[j].Cars[0].RearAxle.Follower.UpdateRelative(-e, false, false); if (Interface.CurrentOptions.Derailments) { double f = 2.0 / (Trains[i].Cars[k].CurrentMass + Trains[j].Cars[0].CurrentMass); double fi = Trains[j].Cars[0].CurrentMass * f; double fj = Trains[i].Cars[k].CurrentMass * f; double vi = v * fi; double vj = v * fj; if (vi > Trains[i].CriticalCollisionSpeedDifference) { Trains[i].Derail(k, TimeElapsed); } if (vj > Trains[j].CriticalCollisionSpeedDifference) { Trains[j].Derail(i, TimeElapsed); } } // adjust cars for train i for (int h = Trains[i].Cars.Length - 2; h >= 0; h--) { a = Trains[i].Cars[h + 1].FrontAxle.Follower.TrackPosition - Trains[i].Cars[h + 1].FrontAxle.Position + 0.5 * Trains[i].Cars[h + 1].Length; b = Trains[i].Cars[h].RearAxle.Follower.TrackPosition - Trains[i].Cars[h].RearAxle.Position - 0.5 * Trains[i].Cars[h].Length; d = b - a - Trains[i].Cars[h].Coupler.MinimumDistanceBetweenCars; if (d < 0.0) { d -= 0.0001; Trains[i].Cars[h].FrontAxle.Follower.UpdateRelative(-d, false, false); Trains[i].Cars[h].RearAxle.Follower.UpdateRelative(-d, false, false); if (Interface.CurrentOptions.Derailments) { double f = 2.0 / (Trains[i].Cars[h + 1].CurrentMass + Trains[i].Cars[h].CurrentMass); double fi = Trains[i].Cars[h + 1].CurrentMass * f; double fj = Trains[i].Cars[h].CurrentMass * f; double vi = v * fi; double vj = v * fj; if (vi > Trains[i].CriticalCollisionSpeedDifference) { Trains[i].Derail(h + 1, TimeElapsed); } if (vj > Trains[j].CriticalCollisionSpeedDifference) { Trains[i].Derail(h, TimeElapsed); } } Trains[i].Cars[h].CurrentSpeed = Trains[i].Cars[h + 1].CurrentSpeed; } } // adjust cars for train j for (int h = 1; h < Trains[j].Cars.Length; h++) { a = Trains[j].Cars[h - 1].RearAxle.Follower.TrackPosition - Trains[j].Cars[h - 1].RearAxle.Position - 0.5 * Trains[j].Cars[h - 1].Length; b = Trains[j].Cars[h].FrontAxle.Follower.TrackPosition - Trains[j].Cars[h].FrontAxle.Position + 0.5 * Trains[j].Cars[h].Length; d = a - b - Trains[j].Cars[h - 1].Coupler.MinimumDistanceBetweenCars; if (d < 0.0) { d -= 0.0001; Trains[j].Cars[h].FrontAxle.Follower.UpdateRelative(d, false, false); Trains[j].Cars[h].RearAxle.Follower.UpdateRelative(d, false, false); if (Interface.CurrentOptions.Derailments) { double f = 2.0 / (Trains[j].Cars[h - 1].CurrentMass + Trains[j].Cars[h].CurrentMass); double fi = Trains[j].Cars[h - 1].CurrentMass * f; double fj = Trains[j].Cars[h].CurrentMass * f; double vi = v * fi; double vj = v * fj; if (vi > Trains[j].CriticalCollisionSpeedDifference) { Trains[j].Derail(h - 1, TimeElapsed); } if (vj > Trains[j].CriticalCollisionSpeedDifference) { Trains[j].Derail(h, TimeElapsed); } } Trains[j].Cars[h].CurrentSpeed = Trains[j].Cars[h - 1].CurrentSpeed; } } } } else { // i < j int k = Trains[j].Cars.Length - 1; if (Trains[i].Cars[0].CurrentSpeed > Trains[j].Cars[k].CurrentSpeed) { double v = Trains[i].Cars[0].CurrentSpeed - Trains[j].Cars[k].CurrentSpeed; double s = (Trains[i].Cars[0].CurrentSpeed * Trains[i].Cars[0].CurrentMass + Trains[j].Cars[k].CurrentSpeed * Trains[j].Cars[k].CurrentMass) / (Trains[i].Cars[0].CurrentMass + Trains[j].Cars[k].CurrentMass); Trains[i].Cars[0].CurrentSpeed = s; Trains[j].Cars[k].CurrentSpeed = s; double e = 0.5 * (a - d) + 0.0001; Trains[i].Cars[0].FrontAxle.Follower.UpdateRelative(-e, false, false); Trains[i].Cars[0].RearAxle.Follower.UpdateRelative(-e, false, false); Trains[j].Cars[k].FrontAxle.Follower.UpdateRelative(e, false, false); Trains[j].Cars[k].RearAxle.Follower.UpdateRelative(e, false, false); if (Interface.CurrentOptions.Derailments) { double f = 2.0 / (Trains[i].Cars[0].CurrentMass + Trains[j].Cars[k].CurrentMass); double fi = Trains[j].Cars[k].CurrentMass * f; double fj = Trains[i].Cars[0].CurrentMass * f; double vi = v * fi; double vj = v * fj; if (vi > Trains[i].CriticalCollisionSpeedDifference) { Trains[i].Derail(0, TimeElapsed); } if (vj > Trains[j].CriticalCollisionSpeedDifference) { Trains[j].Derail(k, TimeElapsed); } } // adjust cars for train i for (int h = 1; h < Trains[i].Cars.Length; h++) { a = Trains[i].Cars[h - 1].RearAxle.Follower.TrackPosition - Trains[i].Cars[h - 1].RearAxle.Position - 0.5 * Trains[i].Cars[h - 1].Length; b = Trains[i].Cars[h].FrontAxle.Follower.TrackPosition - Trains[i].Cars[h].FrontAxle.Position + 0.5 * Trains[i].Cars[h].Length; d = a - b - Trains[i].Cars[h - 1].Coupler.MinimumDistanceBetweenCars; if (d < 0.0) { d -= 0.0001; Trains[i].Cars[h].FrontAxle.Follower.UpdateRelative(d, false, false); Trains[i].Cars[h].RearAxle.Follower.UpdateRelative(d, false, false); if (Interface.CurrentOptions.Derailments) { double f = 2.0 / (Trains[i].Cars[h - 1].CurrentMass + Trains[i].Cars[h].CurrentMass); double fi = Trains[i].Cars[h - 1].CurrentMass * f; double fj = Trains[i].Cars[h].CurrentMass * f; double vi = v * fi; double vj = v * fj; if (vi > Trains[i].CriticalCollisionSpeedDifference) { Trains[i].Derail(h - 1, TimeElapsed); } if (vj > Trains[i].CriticalCollisionSpeedDifference) { Trains[i].Derail(h, TimeElapsed); } } Trains[i].Cars[h].CurrentSpeed = Trains[i].Cars[h - 1].CurrentSpeed; } } // adjust cars for train j for (int h = Trains[j].Cars.Length - 2; h >= 0; h--) { a = Trains[j].Cars[h + 1].FrontAxle.Follower.TrackPosition - Trains[j].Cars[h + 1].FrontAxle.Position + 0.5 * Trains[j].Cars[h + 1].Length; b = Trains[j].Cars[h].RearAxle.Follower.TrackPosition - Trains[j].Cars[h].RearAxle.Position - 0.5 * Trains[j].Cars[h].Length; d = b - a - Trains[j].Cars[h].Coupler.MinimumDistanceBetweenCars; if (d < 0.0) { d -= 0.0001; Trains[j].Cars[h].FrontAxle.Follower.UpdateRelative(-d, false, false); Trains[j].Cars[h].RearAxle.Follower.UpdateRelative(-d, false, false); if (Interface.CurrentOptions.Derailments) { double f = 2.0 / (Trains[j].Cars[h + 1].CurrentMass + Trains[j].Cars[h].CurrentMass); double fi = Trains[j].Cars[h + 1].CurrentMass * f; double fj = Trains[j].Cars[h].CurrentMass * f; double vi = v * fi; double vj = v * fj; if (vi > Trains[i].CriticalCollisionSpeedDifference) { Trains[j].Derail(h + 1, TimeElapsed); } if (vj > Trains[j].CriticalCollisionSpeedDifference) { Trains[j].Derail(h, TimeElapsed); } } Trains[j].Cars[h].CurrentSpeed = Trains[j].Cars[h + 1].CurrentSpeed; } } } } } } } } // with buffers if (Trains[i].IsPlayerTrain) { double a = Trains[i].Cars[0].FrontAxle.Follower.TrackPosition - Trains[i].Cars[0].FrontAxle.Position + 0.5 * Trains[i].Cars[0].Length; double b = Trains[i].Cars[Trains[i].Cars.Length - 1].RearAxle.Follower.TrackPosition - Trains[i].Cars[Trains[i].Cars.Length - 1].RearAxle.Position - 0.5 * Trains[i].Cars[0].Length; for (int j = 0; j < Program.CurrentRoute.BufferTrackPositions.Length; j++) { if (a > Program.CurrentRoute.BufferTrackPositions[j] & b < Program.CurrentRoute.BufferTrackPositions[j]) { a += 0.0001; b -= 0.0001; double da = a - Program.CurrentRoute.BufferTrackPositions[j]; double db = Program.CurrentRoute.BufferTrackPositions[j] - b; if (da < db) { // front Trains[i].Cars[0].UpdateTrackFollowers(-da, false, false); if (Interface.CurrentOptions.Derailments && Math.Abs(Trains[i].Cars[0].CurrentSpeed) > Trains[i].CriticalCollisionSpeedDifference) { Trains[i].Derail(0, TimeElapsed); } Trains[i].Cars[0].CurrentSpeed = 0.0; for (int h = 1; h < Trains[i].Cars.Length; h++) { a = Trains[i].Cars[h - 1].RearAxle.Follower.TrackPosition - Trains[i].Cars[h - 1].RearAxle.Position - 0.5 * Trains[i].Cars[h - 1].Length; b = Trains[i].Cars[h].FrontAxle.Follower.TrackPosition - Trains[i].Cars[h].FrontAxle.Position + 0.5 * Trains[i].Cars[h].Length; double d = a - b - Trains[i].Cars[h - 1].Coupler.MinimumDistanceBetweenCars; if (d < 0.0) { d -= 0.0001; Trains[i].Cars[h].UpdateTrackFollowers(d, false, false); if (Interface.CurrentOptions.Derailments && Math.Abs(Trains[i].Cars[h].CurrentSpeed) > Trains[j].CriticalCollisionSpeedDifference) { Trains[i].Derail(h, TimeElapsed); } Trains[i].Cars[h].CurrentSpeed = 0.0; } } } else { // rear int c = Trains[i].Cars.Length - 1; Trains[i].Cars[c].UpdateTrackFollowers(db, false, false); if (Interface.CurrentOptions.Derailments && Math.Abs(Trains[i].Cars[c].CurrentSpeed) > Trains[i].CriticalCollisionSpeedDifference) { Trains[i].Derail(c, TimeElapsed); } Trains[i].Cars[c].CurrentSpeed = 0.0; for (int h = Trains[i].Cars.Length - 2; h >= 0; h--) { a = Trains[i].Cars[h + 1].FrontAxle.Follower.TrackPosition - Trains[i].Cars[h + 1].FrontAxle.Position + 0.5 * Trains[i].Cars[h + 1].Length; b = Trains[i].Cars[h].RearAxle.Follower.TrackPosition - Trains[i].Cars[h].RearAxle.Position - 0.5 * Trains[i].Cars[h].Length; double d = b - a - Trains[i].Cars[h].Coupler.MinimumDistanceBetweenCars; if (d < 0.0) { d -= 0.0001; Trains[i].Cars[h].UpdateTrackFollowers(-d, false, false); if (Interface.CurrentOptions.Derailments && Math.Abs(Trains[i].Cars[h].CurrentSpeed) > Trains[i].CriticalCollisionSpeedDifference) { Trains[i].Derail(h, TimeElapsed); } Trains[i].Cars[h].CurrentSpeed = 0.0; } } } } } } }); } // compute final angles and positions //for (int i = 0; i < Trains.Length; i++) { System.Threading.Tasks.Parallel.For(0, Trains.Length, i => { if (Trains[i].State != TrainState.Disposed & Trains[i].State != TrainState.Bogus) { for (int j = 0; j < Trains[i].Cars.Length; j++) { Trains[i].Cars[j].FrontAxle.Follower.UpdateWorldCoordinates(true); Trains[i].Cars[j].FrontBogie.FrontAxle.Follower.UpdateWorldCoordinates(true); Trains[i].Cars[j].FrontBogie.RearAxle.Follower.UpdateWorldCoordinates(true); Trains[i].Cars[j].RearAxle.Follower.UpdateWorldCoordinates(true); Trains[i].Cars[j].RearBogie.FrontAxle.Follower.UpdateWorldCoordinates(true); Trains[i].Cars[j].RearBogie.RearAxle.Follower.UpdateWorldCoordinates(true); if (TimeElapsed == 0.0 | TimeElapsed > 0.5) { //Don't update the toppling etc. with excessive or no time continue; } Trains[i].Cars[j].UpdateTopplingCantAndSpring(TimeElapsed); Trains[i].Cars[j].FrontBogie.UpdateTopplingCantAndSpring(); Trains[i].Cars[j].RearBogie.UpdateTopplingCantAndSpring(); } } }); System.Threading.Tasks.Parallel.For(0, TFOs.Length, i => { if (TFOs[i].State != TrainState.Disposed & TFOs[i].State != TrainState.Bogus) { TrackFollowingObject t = (TrackFollowingObject)TFOs[i]; foreach (var Car in t.Cars) { Car.FrontAxle.Follower.UpdateWorldCoordinates(true); Car.FrontBogie.FrontAxle.Follower.UpdateWorldCoordinates(true); Car.FrontBogie.RearAxle.Follower.UpdateWorldCoordinates(true); Car.RearAxle.Follower.UpdateWorldCoordinates(true); Car.RearBogie.FrontAxle.Follower.UpdateWorldCoordinates(true); Car.RearBogie.RearAxle.Follower.UpdateWorldCoordinates(true); if (TimeElapsed == 0.0 | TimeElapsed > 0.5) { //Don't update the toppling etc. with excessive or no time continue; } Car.UpdateTopplingCantAndSpring(TimeElapsed); Car.FrontBogie.UpdateTopplingCantAndSpring(); Car.RearBogie.UpdateTopplingCantAndSpring(); } } }); }
internal void CreateObject(Vector3 Position, Transformation BaseTransformation, Transformation AuxTransformation, int SectionIndex, double TrackPosition, double Brightness) { int a = AnimatedWorldObjectsUsed; if (a >= AnimatedWorldObjects.Length) { Array.Resize <WorldObject>(ref AnimatedWorldObjects, AnimatedWorldObjects.Length << 1); } Transformation FinalTransformation = new Transformation(AuxTransformation, BaseTransformation); //Place track followers if required if (TrackFollowerFunction != null) { var o = this.Clone(); o.ObjectIndex = CreateDynamicObject(); TrackFollowingObject currentObject = new TrackFollowingObject { Position = Position, Direction = FinalTransformation.Z, Up = FinalTransformation.Y, Side = FinalTransformation.X, Object = o, SectionIndex = SectionIndex, TrackPosition = TrackPosition, }; currentObject.FrontAxleFollower.TrackPosition = TrackPosition + FrontAxlePosition; currentObject.RearAxleFollower.TrackPosition = TrackPosition + RearAxlePosition; currentObject.FrontAxlePosition = FrontAxlePosition; currentObject.RearAxlePosition = RearAxlePosition; currentObject.FrontAxleFollower.UpdateWorldCoordinates(false); currentObject.RearAxleFollower.UpdateWorldCoordinates(false); for (int i = 0; i < currentObject.Object.States.Length; i++) { if (currentObject.Object.States[i].Object == null) { currentObject.Object.States[i].Object = new StaticObject(Program.CurrentHost) { RendererIndex = -1 }; } } double r = 0.0; for (int i = 0; i < currentObject.Object.States.Length; i++) { for (int j = 0; j < currentObject.Object.States[i].Object.Mesh.Materials.Length; j++) { currentObject.Object.States[i].Object.Mesh.Materials[j].Color *= Brightness; } for (int j = 0; j < currentObject.Object.States[i].Object.Mesh.Vertices.Length; j++) { double t = States[i].Object.Mesh.Vertices[j].Coordinates.Norm(); if (t > r) { r = t; } } } currentObject.Radius = Math.Sqrt(r); currentObject.Visible = false; currentObject.Object.Initialize(0, false, false); AnimatedWorldObjects[a] = currentObject; } else { var o = this.Clone(); o.ObjectIndex = CreateDynamicObject(); AnimatedWorldObject currentObject = new AnimatedWorldObject { Position = Position, Direction = FinalTransformation.Z, Up = FinalTransformation.Y, Side = FinalTransformation.X, Object = o, SectionIndex = SectionIndex, TrackPosition = TrackPosition, }; for (int i = 0; i < currentObject.Object.States.Length; i++) { if (currentObject.Object.States[i].Object == null) { currentObject.Object.States[i].Object = new StaticObject(Program.CurrentHost) { RendererIndex = -1 }; } } double r = 0.0; for (int i = 0; i < currentObject.Object.States.Length; i++) { for (int j = 0; j < currentObject.Object.States[i].Object.Mesh.Materials.Length; j++) { currentObject.Object.States[i].Object.Mesh.Materials[j].Color *= Brightness; } for (int j = 0; j < currentObject.Object.States[i].Object.Mesh.Vertices.Length; j++) { double t = States[i].Object.Mesh.Vertices[j].Coordinates.Norm(); if (t > r) { r = t; } } } currentObject.Radius = Math.Sqrt(r); currentObject.Visible = false; currentObject.Object.Initialize(0, false, false); AnimatedWorldObjects[a] = currentObject; } AnimatedWorldObjectsUsed++; }