// ------------------ // smoothen out turns private static void SmoothenOutTurns(int subdivisions) { if (subdivisions < 2) { throw new InvalidOperationException(); } // subdivide track int length = TrackManager.CurrentTrack.Elements.Length; int newLength = (length - 1) * subdivisions + 1; double[] midpointsTrackPositions = new double[newLength]; Vector3[] midpointsWorldPositions = new Vector3[newLength]; Vector3[] midpointsWorldDirections = new Vector3[newLength]; Vector3[] midpointsWorldUps = new Vector3[newLength]; Vector3[] midpointsWorldSides = new Vector3[newLength]; double[] midpointsCant = new double[newLength]; for (int i = 0; i < newLength; i++) { int m = i % subdivisions; if (m != 0) { int q = i / subdivisions; TrackManager.TrackFollower follower = new TrackManager.TrackFollower(); double r = (double)m / (double)subdivisions; double p = (1.0 - r) * TrackManager.CurrentTrack.Elements[q].StartingTrackPosition + r * TrackManager.CurrentTrack.Elements[q + 1].StartingTrackPosition; TrackManager.UpdateTrackFollower(ref follower, -1.0, true, false); TrackManager.UpdateTrackFollower(ref follower, p, true, false); midpointsTrackPositions[i] = p; midpointsWorldPositions[i] = follower.WorldPosition; midpointsWorldDirections[i] = follower.WorldDirection; midpointsWorldUps[i] = follower.WorldUp; midpointsWorldSides[i] = follower.WorldSide; midpointsCant[i] = follower.CurveCant; } } Array.Resize<TrackManager.TrackElement>(ref TrackManager.CurrentTrack.Elements, newLength); for (int i = length - 1; i >= 1; i--) { TrackManager.CurrentTrack.Elements[subdivisions * i] = TrackManager.CurrentTrack.Elements[i]; } for (int i = 0; i < TrackManager.CurrentTrack.Elements.Length; i++) { int m = i % subdivisions; if (m != 0) { int q = i / subdivisions; int j = q * subdivisions; TrackManager.CurrentTrack.Elements[i] = TrackManager.CurrentTrack.Elements[j]; TrackManager.CurrentTrack.Elements[i].Events = new TrackManager.GeneralEvent[] { }; TrackManager.CurrentTrack.Elements[i].StartingTrackPosition = midpointsTrackPositions[i]; TrackManager.CurrentTrack.Elements[i].WorldPosition = midpointsWorldPositions[i]; TrackManager.CurrentTrack.Elements[i].WorldDirection = midpointsWorldDirections[i]; TrackManager.CurrentTrack.Elements[i].WorldUp = midpointsWorldUps[i]; TrackManager.CurrentTrack.Elements[i].WorldSide = midpointsWorldSides[i]; TrackManager.CurrentTrack.Elements[i].CurveCant = midpointsCant[i]; TrackManager.CurrentTrack.Elements[i].CurveCantTangent = 0.0; } } // find turns bool[] isTurn = new bool[TrackManager.CurrentTrack.Elements.Length]; { TrackManager.TrackFollower follower = new TrackManager.TrackFollower(); for (int i = 1; i < TrackManager.CurrentTrack.Elements.Length - 1; i++) { int m = i % subdivisions; if (m == 0) { double p = 0.00000001 * TrackManager.CurrentTrack.Elements[i - 1].StartingTrackPosition + 0.99999999 * TrackManager.CurrentTrack.Elements[i].StartingTrackPosition; TrackManager.UpdateTrackFollower(ref follower, p, true, false); Vector3 d1 = TrackManager.CurrentTrack.Elements[i].WorldDirection; Vector3 d2 = follower.WorldDirection; Vector3 d = d1 - d2; double t = d.X * d.X + d.Z * d.Z; const double e = 0.0001; if (t > e) { isTurn[i] = true; } } } } // replace turns by curves double totalShortage = 0.0; for (int i = 0; i < TrackManager.CurrentTrack.Elements.Length; i++) { if (isTurn[i]) { // estimate radius Vector3 AP = TrackManager.CurrentTrack.Elements[i - 1].WorldPosition; Vector3 AS = TrackManager.CurrentTrack.Elements[i - 1].WorldSide; Vector3 BP = TrackManager.CurrentTrack.Elements[i + 1].WorldPosition; Vector3 BS = TrackManager.CurrentTrack.Elements[i + 1].WorldSide; Vector3 S = AS - BS; double rx; if (S.X * S.X > 0.000001) { rx = (BP.X - AP.X) / S.X; } else { rx = 0.0; } double rz; if (S.Z * S.Z > 0.000001) { rz = (BP.Z - AP.Z) / S.Z; } else { rz = 0.0; } if (rx != 0.0 | rz != 0.0) { double r; if (rx != 0.0 & rz != 0.0) { if (Math.Sign(rx) == Math.Sign(rz)) { double f = rx / rz; if (f > -1.1 & f < -0.9 | f > 0.9 & f < 1.1) { r = Math.Sqrt(Math.Abs(rx * rz)) * Math.Sign(rx); } else { r = 0.0; } } else { r = 0.0; } } else if (rx != 0.0) { r = rx; } else { r = rz; } if (r * r > 1.0) { // apply radius TrackManager.TrackFollower follower = new TrackManager.TrackFollower(); TrackManager.CurrentTrack.Elements[i - 1].CurveRadius = r; double p = 0.00000001 * TrackManager.CurrentTrack.Elements[i - 1].StartingTrackPosition + 0.99999999 * TrackManager.CurrentTrack.Elements[i].StartingTrackPosition; TrackManager.UpdateTrackFollower(ref follower, p - 1.0, true, false); TrackManager.UpdateTrackFollower(ref follower, p, true, false); TrackManager.CurrentTrack.Elements[i].CurveRadius = r; //TrackManager.CurrentTrack.Elements[i].CurveCant = TrackManager.CurrentTrack.Elements[i].CurveCant; //TrackManager.CurrentTrack.Elements[i].CurveCantInterpolation = TrackManager.CurrentTrack.Elements[i].CurveCantInterpolation; TrackManager.CurrentTrack.Elements[i].WorldPosition = follower.WorldPosition; TrackManager.CurrentTrack.Elements[i].WorldDirection = follower.WorldDirection; TrackManager.CurrentTrack.Elements[i].WorldUp = follower.WorldUp; TrackManager.CurrentTrack.Elements[i].WorldSide = follower.WorldSide; // iterate to shorten track element length p = 0.00000001 * TrackManager.CurrentTrack.Elements[i].StartingTrackPosition + 0.99999999 * TrackManager.CurrentTrack.Elements[i + 1].StartingTrackPosition; TrackManager.UpdateTrackFollower(ref follower, p - 1.0, true, false); TrackManager.UpdateTrackFollower(ref follower, p, true, false); Vector3 d = TrackManager.CurrentTrack.Elements[i + 1].WorldPosition - follower.WorldPosition; double bestT = d.X * d.X + d.Y * d.Y + d.Z * d.Z; int bestJ = 0; int n = 1000; double a = 1.0 / (double)n * (TrackManager.CurrentTrack.Elements[i + 1].StartingTrackPosition - TrackManager.CurrentTrack.Elements[i].StartingTrackPosition); for (int j = 1; j < n - 1; j++) { TrackManager.UpdateTrackFollower(ref follower, TrackManager.CurrentTrack.Elements[i + 1].StartingTrackPosition - (double)j * a, true, false); d = TrackManager.CurrentTrack.Elements[i + 1].WorldPosition - follower.WorldPosition; double t = d.X * d.X + d.Y * d.Y + d.Z * d.Z; if (t < bestT) { bestT = t; bestJ = j; } else { break; } } double s = (double)bestJ * a; for (int j = i + 1; j < TrackManager.CurrentTrack.Elements.Length; j++) { TrackManager.CurrentTrack.Elements[j].StartingTrackPosition -= s; } totalShortage += s; // introduce turn to compensate for curve p = 0.00000001 * TrackManager.CurrentTrack.Elements[i].StartingTrackPosition + 0.99999999 * TrackManager.CurrentTrack.Elements[i + 1].StartingTrackPosition; TrackManager.UpdateTrackFollower(ref follower, p - 1.0, true, false); TrackManager.UpdateTrackFollower(ref follower, p, true, false); Vector3 AB = TrackManager.CurrentTrack.Elements[i + 1].WorldPosition - follower.WorldPosition; Vector3 AC = TrackManager.CurrentTrack.Elements[i + 1].WorldPosition - TrackManager.CurrentTrack.Elements[i].WorldPosition; Vector3 BC = follower.WorldPosition - TrackManager.CurrentTrack.Elements[i].WorldPosition; double sa = Math.Sqrt(BC.X * BC.X + BC.Z * BC.Z); double sb = Math.Sqrt(AC.X * AC.X + AC.Z * AC.Z); double sc = Math.Sqrt(AB.X * AB.X + AB.Z * AB.Z); double denominator = 2.0 * sa * sb; if (denominator != 0.0) { double originalAngle; { double value = (sa * sa + sb * sb - sc * sc) / denominator; if (value < -1.0) { originalAngle = Math.PI; } else if (value > 1.0) { originalAngle = 0; } else { originalAngle = Math.Acos(value); } } TrackManager.TrackElement originalTrackElement = TrackManager.CurrentTrack.Elements[i]; bestT = double.MaxValue; bestJ = 0; for (int j = -1; j <= 1; j++) { double g = (double)j * originalAngle; double cosg = Math.Cos(g); double sing = Math.Sin(g); TrackManager.CurrentTrack.Elements[i] = originalTrackElement; World.Rotate(ref TrackManager.CurrentTrack.Elements[i].WorldDirection.X, ref TrackManager.CurrentTrack.Elements[i].WorldDirection.Y, ref TrackManager.CurrentTrack.Elements[i].WorldDirection.Z, 0.0, 1.0, 0.0, cosg, sing); World.Rotate(ref TrackManager.CurrentTrack.Elements[i].WorldUp.X, ref TrackManager.CurrentTrack.Elements[i].WorldUp.Y, ref TrackManager.CurrentTrack.Elements[i].WorldUp.Z, 0.0, 1.0, 0.0, cosg, sing); World.Rotate(ref TrackManager.CurrentTrack.Elements[i].WorldSide.X, ref TrackManager.CurrentTrack.Elements[i].WorldSide.Y, ref TrackManager.CurrentTrack.Elements[i].WorldSide.Z, 0.0, 1.0, 0.0, cosg, sing); p = 0.00000001 * TrackManager.CurrentTrack.Elements[i].StartingTrackPosition + 0.99999999 * TrackManager.CurrentTrack.Elements[i + 1].StartingTrackPosition; TrackManager.UpdateTrackFollower(ref follower, p - 1.0, true, false); TrackManager.UpdateTrackFollower(ref follower, p, true, false); d = TrackManager.CurrentTrack.Elements[i + 1].WorldPosition - follower.WorldPosition; double t = d.X * d.X + d.Y * d.Y + d.Z * d.Z; if (t < bestT) { bestT = t; bestJ = j; } } { double newAngle = (double)bestJ * originalAngle; double cosg = Math.Cos(newAngle); double sing = Math.Sin(newAngle); TrackManager.CurrentTrack.Elements[i] = originalTrackElement; World.Rotate(ref TrackManager.CurrentTrack.Elements[i].WorldDirection.X, ref TrackManager.CurrentTrack.Elements[i].WorldDirection.Y, ref TrackManager.CurrentTrack.Elements[i].WorldDirection.Z, 0.0, 1.0, 0.0, cosg, sing); World.Rotate(ref TrackManager.CurrentTrack.Elements[i].WorldUp.X, ref TrackManager.CurrentTrack.Elements[i].WorldUp.Y, ref TrackManager.CurrentTrack.Elements[i].WorldUp.Z, 0.0, 1.0, 0.0, cosg, sing); World.Rotate(ref TrackManager.CurrentTrack.Elements[i].WorldSide.X, ref TrackManager.CurrentTrack.Elements[i].WorldSide.Y, ref TrackManager.CurrentTrack.Elements[i].WorldSide.Z, 0.0, 1.0, 0.0, cosg, sing); } // iterate again to further shorten track element length p = 0.00000001 * TrackManager.CurrentTrack.Elements[i].StartingTrackPosition + 0.99999999 * TrackManager.CurrentTrack.Elements[i + 1].StartingTrackPosition; TrackManager.UpdateTrackFollower(ref follower, p - 1.0, true, false); TrackManager.UpdateTrackFollower(ref follower, p, true, false); d = TrackManager.CurrentTrack.Elements[i + 1].WorldPosition - follower.WorldPosition; bestT = d.X * d.X + d.Y * d.Y + d.Z * d.Z; bestJ = 0; n = 1000; a = 1.0 / (double)n * (TrackManager.CurrentTrack.Elements[i + 1].StartingTrackPosition - TrackManager.CurrentTrack.Elements[i].StartingTrackPosition); for (int j = 1; j < n - 1; j++) { TrackManager.UpdateTrackFollower(ref follower, TrackManager.CurrentTrack.Elements[i + 1].StartingTrackPosition - (double)j * a, true, false); d = TrackManager.CurrentTrack.Elements[i + 1].WorldPosition - follower.WorldPosition; double t = d.X * d.X + d.Y * d.Y + d.Z * d.Z; if (t < bestT) { bestT = t; bestJ = j; } else { break; } } s = (double)bestJ * a; for (int j = i + 1; j < TrackManager.CurrentTrack.Elements.Length; j++) { TrackManager.CurrentTrack.Elements[j].StartingTrackPosition -= s; } totalShortage += s; } // compensate for height difference p = 0.00000001 * TrackManager.CurrentTrack.Elements[i].StartingTrackPosition + 0.99999999 * TrackManager.CurrentTrack.Elements[i + 1].StartingTrackPosition; TrackManager.UpdateTrackFollower(ref follower, p - 1.0, true, false); TrackManager.UpdateTrackFollower(ref follower, p, true, false); Vector3 d1 = TrackManager.CurrentTrack.Elements[i + 1].WorldPosition - TrackManager.CurrentTrack.Elements[i].WorldPosition; double a1 = Math.Atan(d1.Y / Math.Sqrt(d1.X * d1.X + d1.Z * d1.Z)); Vector3 d2 = follower.WorldPosition - TrackManager.CurrentTrack.Elements[i].WorldPosition; double a2 = Math.Atan(d2.Y / Math.Sqrt(d2.X * d2.X + d2.Z * d2.Z)); double b = a2 - a1; if (b * b > 0.00000001) { double cosa = Math.Cos(b); double sina = Math.Sin(b); World.Rotate(ref TrackManager.CurrentTrack.Elements[i].WorldDirection.X, ref TrackManager.CurrentTrack.Elements[i].WorldDirection.Y, ref TrackManager.CurrentTrack.Elements[i].WorldDirection.Z, TrackManager.CurrentTrack.Elements[i].WorldSide.X, TrackManager.CurrentTrack.Elements[i].WorldSide.Y, TrackManager.CurrentTrack.Elements[i].WorldSide.Z, cosa, sina); World.Rotate(ref TrackManager.CurrentTrack.Elements[i].WorldUp.X, ref TrackManager.CurrentTrack.Elements[i].WorldUp.Y, ref TrackManager.CurrentTrack.Elements[i].WorldUp.Z, TrackManager.CurrentTrack.Elements[i].WorldSide.X, TrackManager.CurrentTrack.Elements[i].WorldSide.Y, TrackManager.CurrentTrack.Elements[i].WorldSide.Z, cosa, sina); } } } } } // correct events for (int i = 0; i < TrackManager.CurrentTrack.Elements.Length - 1; i++) { double startingTrackPosition = TrackManager.CurrentTrack.Elements[i].StartingTrackPosition; double endingTrackPosition = TrackManager.CurrentTrack.Elements[i + 1].StartingTrackPosition; for (int j = 0; j < TrackManager.CurrentTrack.Elements[i].Events.Length; j++) { double p = startingTrackPosition + TrackManager.CurrentTrack.Elements[i].Events[j].TrackPositionDelta; if (p >= endingTrackPosition) { int len = TrackManager.CurrentTrack.Elements[i + 1].Events.Length; Array.Resize<TrackManager.GeneralEvent>(ref TrackManager.CurrentTrack.Elements[i + 1].Events, len + 1); TrackManager.CurrentTrack.Elements[i + 1].Events[len] = TrackManager.CurrentTrack.Elements[i].Events[j]; TrackManager.CurrentTrack.Elements[i + 1].Events[len].TrackPositionDelta += startingTrackPosition - endingTrackPosition; for (int k = j; k < TrackManager.CurrentTrack.Elements[i].Events.Length - 1; k++) { TrackManager.CurrentTrack.Elements[i].Events[k] = TrackManager.CurrentTrack.Elements[i].Events[k + 1]; } len = TrackManager.CurrentTrack.Elements[i].Events.Length; Array.Resize<TrackManager.GeneralEvent>(ref TrackManager.CurrentTrack.Elements[i].Events, len - 1); j--; } } } }
/// <summary>Renders a graphical visualisation of any events within camera range</summary> /// <param name="Camera">The absolute camera position</param> private static void RenderEvents(Vector3 Camera) { if (Interface.CurrentOptions.ShowEvents == false || TrackManager.Tracks[0].Elements == null) { return; } if (!Initialized) { Init(); Initialized = true; } GL.Enable(EnableCap.CullFace); LibRender.Renderer.CullEnabled = true; GL.Enable(EnableCap.DepthTest); GL.DepthMask(true); if (LibRender.Renderer.LightingEnabled) { GL.Disable(EnableCap.Lighting); LibRender.Renderer.LightingEnabled = false; } if (LibRender.Renderer.AlphaTestEnabled) { GL.Disable(EnableCap.AlphaTest); LibRender.Renderer.AlphaTestEnabled = false; } double da = -World.BackwardViewingDistance - World.ExtraViewingDistance; double db = World.ForwardViewingDistance + World.ExtraViewingDistance; bool[] sta = new bool[CurrentRoute.Stations.Length]; // events for (int i = 0; i < TrackManager.Tracks[0].Elements.Length; i++) { double p = TrackManager.Tracks[0].Elements[i].StartingTrackPosition; double d = p - World.CameraTrackFollower.TrackPosition; if (d >= da & d <= db) { for (int j = 0; j < TrackManager.Tracks[0].Elements[i].Events.Length; j++) { dynamic e = TrackManager.Tracks[0].Elements[i].Events[j]; double dy, dx = 0.0, dz = 0.0; double s; Texture t; if (e is TrackManager.BrightnessChangeEvent) { s = 0.15; dy = 4.0; t = BrightnessChangeTexture; } else if (e is BackgroundChangeEvent) { s = 0.25; dy = 3.5; t = BackgroundChangeTexture; } else if (e is TrackManager.StationStartEvent) { s = 0.25; dy = 1.6; t = StationStartTexture; TrackManager.StationStartEvent f = (TrackManager.StationStartEvent)e; sta[f.StationIndex] = true; } else if (e is TrackManager.StationEndEvent) { s = 0.25; dy = 1.6; t = StationEndTexture; TrackManager.StationEndEvent f = (TrackManager.StationEndEvent)e; sta[f.StationIndex] = true; } else if (e is TrackManager.LimitChangeEvent) { s = 0.2; dy = 1.1; t = LimitTexture; } else if (e is TrackManager.SectionChangeEvent) { s = 0.2; dy = 0.8; t = SectionTexture; } else if (e is TrackManager.TransponderEvent) { s = 0.15; dy = 0.4; t = TransponderTexture; } else if (e is TrackManager.SoundEvent) { TrackManager.SoundEvent f = (TrackManager.SoundEvent)e; s = 0.2; dx = f.Position.X; dy = f.Position.Y < 0.1 ? 0.1 : f.Position.Y; dz = f.Position.Z; t = SoundTexture; } else if (e is TrackManager.PointSoundEvent) { s = 0.2; dx = 0; dy = 0.2; dz = 0; t = PointSoundTexture; } else { s = 0.2; dy = 1.0; t = null; } if (t != null) { TrackManager.TrackFollower f = new TrackManager.TrackFollower(); f.TriggerType = EventTriggerType.None; f.TrackPosition = p; f.Update(p + e.TrackPositionDelta, true, false); f.WorldPosition.X += dx * f.WorldSide.X + dy * f.WorldUp.X + dz * f.WorldDirection.X; f.WorldPosition.Y += dx * f.WorldSide.Y + dy * f.WorldUp.Y + dz * f.WorldDirection.Y; f.WorldPosition.Z += dx * f.WorldSide.Z + dy * f.WorldUp.Z + dz * f.WorldDirection.Z; LibRender.Renderer.DrawCube(f.WorldPosition, f.WorldDirection, f.WorldUp, f.WorldSide, s, Camera, t); } } } } // stops for (int i = 0; i < sta.Length; i++) { if (sta[i]) { for (int j = 0; j < CurrentRoute.Stations[i].Stops.Length; j++) { const double dy = 1.4; const double s = 0.2; double p = CurrentRoute.Stations[i].Stops[j].TrackPosition; TrackManager.TrackFollower f = new TrackManager.TrackFollower(); f.TriggerType = EventTriggerType.None; f.TrackPosition = p; f.Update(p, true, false); f.WorldPosition.X += dy * f.WorldUp.X; f.WorldPosition.Y += dy * f.WorldUp.Y; f.WorldPosition.Z += dy * f.WorldUp.Z; LibRender.Renderer.DrawCube(f.WorldPosition, f.WorldDirection, f.WorldUp, f.WorldSide, s, Camera, StopTexture); } } } // buffers for (int i = 0; i < Game.BufferTrackPositions.Length; i++) { double p = Game.BufferTrackPositions[i]; double d = p - World.CameraTrackFollower.TrackPosition; if (d >= da & d <= db) { const double dy = 2.5; const double s = 0.25; TrackManager.TrackFollower f = new TrackManager.TrackFollower(); f.TriggerType = EventTriggerType.None; f.TrackPosition = p; f.Update(p, true, false); f.WorldPosition.X += dy * f.WorldUp.X; f.WorldPosition.Y += dy * f.WorldUp.Y; f.WorldPosition.Z += dy * f.WorldUp.Z; LibRender.Renderer.DrawCube(f.WorldPosition, f.WorldDirection, f.WorldUp, f.WorldSide, s, Camera, BufferTexture); } } }
// update absolute camera internal static void UpdateAbsoluteCamera(double TimeElapsed) { // zoom double zm = World.CameraCurrentAlignment.Zoom; AdjustAlignment(ref World.CameraCurrentAlignment.Zoom, World.CameraAlignmentDirection.Zoom, ref World.CameraAlignmentSpeed.Zoom, TimeElapsed, true); if (zm != World.CameraCurrentAlignment.Zoom) { ApplyZoom(); } if (CameraMode == CameraViewMode.FlyBy | CameraMode == CameraViewMode.FlyByZooming) { // fly-by AdjustAlignment(ref World.CameraCurrentAlignment.Position.X, World.CameraAlignmentDirection.Position.X, ref World.CameraAlignmentSpeed.Position.X, TimeElapsed); AdjustAlignment(ref World.CameraCurrentAlignment.Position.Y, World.CameraAlignmentDirection.Position.Y, ref World.CameraAlignmentSpeed.Position.Y, TimeElapsed); double tr = World.CameraCurrentAlignment.TrackPosition; AdjustAlignment(ref World.CameraCurrentAlignment.TrackPosition, World.CameraAlignmentDirection.TrackPosition, ref World.CameraAlignmentSpeed.TrackPosition, TimeElapsed); if (tr != World.CameraCurrentAlignment.TrackPosition) { World.CameraTrackFollower.Update(World.CameraCurrentAlignment.TrackPosition, true, false); UpdateViewingDistances(); } // camera double px = World.CameraTrackFollower.WorldPosition.X; double py = World.CameraTrackFollower.WorldPosition.Y; double pz = World.CameraTrackFollower.WorldPosition.Z; // position to focus on double tx, ty, tz; double zoomMultiplier; { const double heightFactor = 0.75; TrainManager.Train bestTrain = null; double bestDistanceSquared = double.MaxValue; TrainManager.Train secondBestTrain = null; double secondBestDistanceSquared = double.MaxValue; foreach (TrainManager.Train train in TrainManager.Trains) { if (train.State == TrainState.Available) { double x = 0.5 * (train.Cars[0].FrontAxle.Follower.WorldPosition.X + train.Cars[0].RearAxle.Follower.WorldPosition.X); double y = 0.5 * (train.Cars[0].FrontAxle.Follower.WorldPosition.Y + train.Cars[0].RearAxle.Follower.WorldPosition.Y) + heightFactor * train.Cars[0].Height; double z = 0.5 * (train.Cars[0].FrontAxle.Follower.WorldPosition.Z + train.Cars[0].RearAxle.Follower.WorldPosition.Z); double dx = x - px; double dy = y - py; double dz = z - pz; double d = dx * dx + dy * dy + dz * dz; if (d < bestDistanceSquared) { secondBestTrain = bestTrain; secondBestDistanceSquared = bestDistanceSquared; bestTrain = train; bestDistanceSquared = d; } else if (d < secondBestDistanceSquared) { secondBestTrain = train; secondBestDistanceSquared = d; } } } if (bestTrain != null) { const double maxDistance = 100.0; double bestDistance = Math.Sqrt(bestDistanceSquared); double secondBestDistance = Math.Sqrt(secondBestDistanceSquared); if (secondBestTrain != null && secondBestDistance - bestDistance <= maxDistance) { double x1 = 0.5 * (bestTrain.Cars[0].FrontAxle.Follower.WorldPosition.X + bestTrain.Cars[0].RearAxle.Follower.WorldPosition.X); double y1 = 0.5 * (bestTrain.Cars[0].FrontAxle.Follower.WorldPosition.Y + bestTrain.Cars[0].RearAxle.Follower.WorldPosition.Y) + heightFactor * bestTrain.Cars[0].Height; double z1 = 0.5 * (bestTrain.Cars[0].FrontAxle.Follower.WorldPosition.Z + bestTrain.Cars[0].RearAxle.Follower.WorldPosition.Z); double x2 = 0.5 * (secondBestTrain.Cars[0].FrontAxle.Follower.WorldPosition.X + secondBestTrain.Cars[0].RearAxle.Follower.WorldPosition.X); double y2 = 0.5 * (secondBestTrain.Cars[0].FrontAxle.Follower.WorldPosition.Y + secondBestTrain.Cars[0].RearAxle.Follower.WorldPosition.Y) + heightFactor * secondBestTrain.Cars[0].Height; double z2 = 0.5 * (secondBestTrain.Cars[0].FrontAxle.Follower.WorldPosition.Z + secondBestTrain.Cars[0].RearAxle.Follower.WorldPosition.Z); double t = 0.5 - (secondBestDistance - bestDistance) / (2.0 * maxDistance); if (t < 0.0) { t = 0.0; } t = 2.0 * t * t; /* in order to change the shape of the interpolation curve */ tx = (1.0 - t) * x1 + t * x2; ty = (1.0 - t) * y1 + t * y2; tz = (1.0 - t) * z1 + t * z2; zoomMultiplier = 1.0 - 2.0 * t; } else { tx = 0.5 * (bestTrain.Cars[0].FrontAxle.Follower.WorldPosition.X + bestTrain.Cars[0].RearAxle.Follower.WorldPosition.X); ty = 0.5 * (bestTrain.Cars[0].FrontAxle.Follower.WorldPosition.Y + bestTrain.Cars[0].RearAxle.Follower.WorldPosition.Y) + heightFactor * bestTrain.Cars[0].Height; tz = 0.5 * (bestTrain.Cars[0].FrontAxle.Follower.WorldPosition.Z + bestTrain.Cars[0].RearAxle.Follower.WorldPosition.Z); zoomMultiplier = 1.0; } } else { tx = 0.0; ty = 0.0; tz = 0.0; zoomMultiplier = 1.0; } } // camera { AbsoluteCameraDirection = new Vector3(CameraTrackFollower.WorldDirection); double ox = World.CameraCurrentAlignment.Position.X; double oy = World.CameraCurrentAlignment.Position.Y; double oz = World.CameraCurrentAlignment.Position.Z; double cx = px + CameraTrackFollower.WorldSide.X * ox + CameraTrackFollower.WorldUp.X * oy + AbsoluteCameraDirection.X * oz; double cy = py + CameraTrackFollower.WorldSide.Y * ox + CameraTrackFollower.WorldUp.Y * oy + AbsoluteCameraDirection.Y * oz; double cz = pz + CameraTrackFollower.WorldSide.Z * ox + CameraTrackFollower.WorldUp.Z * oy + AbsoluteCameraDirection.Z * oz; AbsoluteCameraPosition = new Vector3(cx, cy, cz); AbsoluteCameraDirection.X = tx - cx; AbsoluteCameraDirection.Y = ty - cy; AbsoluteCameraDirection.Z = tz - cz; double t = AbsoluteCameraDirection.Norm(); double ti = 1.0 / t; AbsoluteCameraDirection *= ti; AbsoluteCameraSide = new Vector3(AbsoluteCameraDirection.Z, 0.0, -AbsoluteCameraDirection.X); AbsoluteCameraSide.Normalize(); AbsoluteCameraUp = Vector3.Cross(AbsoluteCameraDirection, AbsoluteCameraSide); UpdateViewingDistances(); if (CameraMode == CameraViewMode.FlyByZooming) { // zoom const double fadeOutDistance = 600.0; /* the distance with the highest zoom factor is half the fade-out distance */ const double maxZoomFactor = 7.0; /* the zoom factor at half the fade-out distance */ const double factor = 256.0 / (fadeOutDistance * fadeOutDistance * fadeOutDistance * fadeOutDistance * fadeOutDistance * fadeOutDistance * fadeOutDistance * fadeOutDistance); double zoom; if (t < fadeOutDistance) { double tdist4 = fadeOutDistance - t; tdist4 *= tdist4; tdist4 *= tdist4; double t4 = t * t; t4 *= t4; zoom = 1.0 + factor * zoomMultiplier * (maxZoomFactor - 1.0) * tdist4 * t4; } else { zoom = 1.0; } World.VerticalViewingAngle = World.OriginalVerticalViewingAngle / zoom; Renderer.UpdateViewport(Renderer.ViewPortChangeMode.NoChange); } } } else { // non-fly-by { // current alignment AdjustAlignment(ref World.CameraCurrentAlignment.Position.X, World.CameraAlignmentDirection.Position.X, ref World.CameraAlignmentSpeed.Position.X, TimeElapsed); AdjustAlignment(ref World.CameraCurrentAlignment.Position.Y, World.CameraAlignmentDirection.Position.Y, ref World.CameraAlignmentSpeed.Position.Y, TimeElapsed); AdjustAlignment(ref World.CameraCurrentAlignment.Position.Z, World.CameraAlignmentDirection.Position.Z, ref World.CameraAlignmentSpeed.Position.Z, TimeElapsed); if ((CameraMode == CameraViewMode.Interior | World.CameraMode == CameraViewMode.InteriorLookAhead) & CameraRestriction == Camera.RestrictionMode.On) { if (CameraCurrentAlignment.Position.Z > 0.75) { CameraCurrentAlignment.Position.Z = 0.75; } } bool q = World.CameraAlignmentSpeed.Yaw != 0.0 | World.CameraAlignmentSpeed.Pitch != 0.0 | World.CameraAlignmentSpeed.Roll != 0.0; AdjustAlignment(ref World.CameraCurrentAlignment.Yaw, World.CameraAlignmentDirection.Yaw, ref World.CameraAlignmentSpeed.Yaw, TimeElapsed); AdjustAlignment(ref World.CameraCurrentAlignment.Pitch, World.CameraAlignmentDirection.Pitch, ref World.CameraAlignmentSpeed.Pitch, TimeElapsed); AdjustAlignment(ref World.CameraCurrentAlignment.Roll, World.CameraAlignmentDirection.Roll, ref World.CameraAlignmentSpeed.Roll, TimeElapsed); double tr = World.CameraCurrentAlignment.TrackPosition; AdjustAlignment(ref World.CameraCurrentAlignment.TrackPosition, World.CameraAlignmentDirection.TrackPosition, ref World.CameraAlignmentSpeed.TrackPosition, TimeElapsed); if (tr != World.CameraCurrentAlignment.TrackPosition) { World.CameraTrackFollower.Update(World.CameraCurrentAlignment.TrackPosition, true, false); q = true; } if (q) { UpdateViewingDistances(); } } // camera Vector3 cF = new Vector3(CameraTrackFollower.WorldPosition); Vector3 dF = new Vector3(CameraTrackFollower.WorldDirection); Vector3 uF = new Vector3(CameraTrackFollower.WorldUp); Vector3 sF = new Vector3(CameraTrackFollower.WorldSide); double lookaheadYaw; double lookaheadPitch; if (CameraMode == CameraViewMode.InteriorLookAhead) { // look-ahead double d = 20.0; if (TrainManager.PlayerTrain.Specs.CurrentAverageSpeed > 0.0) { d += 3.0 * (Math.Sqrt(TrainManager.PlayerTrain.Specs.CurrentAverageSpeed * TrainManager.PlayerTrain.Specs.CurrentAverageSpeed + 1.0) - 1.0); } d -= TrainManager.PlayerTrain.Cars[TrainManager.PlayerTrain.DriverCar].FrontAxle.Position; TrackManager.TrackFollower f = TrainManager.PlayerTrain.Cars[TrainManager.PlayerTrain.DriverCar].FrontAxle.Follower; f.TriggerType = TrackManager.EventTriggerType.None; f.Update(f.TrackPosition + d, true, false); Vector3 r = new Vector3(f.WorldPosition - cF + World.CameraTrackFollower.WorldSide * TrainManager.PlayerTrain.Cars[TrainManager.PlayerTrain.DriverCar].Driver.X + World.CameraTrackFollower.WorldUp * TrainManager.PlayerTrain.Cars[TrainManager.PlayerTrain.DriverCar].Driver.Y + World.CameraTrackFollower.WorldDirection * TrainManager.PlayerTrain.Cars[TrainManager.PlayerTrain.DriverCar].Driver.Z); r.Normalize(); double t = dF.Z * (sF.Y * uF.X - sF.X * uF.Y) + dF.Y * (-sF.Z * uF.X + sF.X * uF.Z) + dF.X * (sF.Z * uF.Y - sF.Y * uF.Z); if (t != 0.0) { t = 1.0 / t; double tx = (r.Z * (-dF.Y * uF.X + dF.X * uF.Y) + r.Y * (dF.Z * uF.X - dF.X * uF.Z) + r.X * (-dF.Z * uF.Y + dF.Y * uF.Z)) * t; double ty = (r.Z * (dF.Y * sF.X - dF.X * sF.Y) + r.Y * (-dF.Z * sF.X + dF.X * sF.Z) + r.X * (dF.Z * sF.Y - dF.Y * sF.Z)) * t; double tz = (r.Z * (sF.Y * uF.X - sF.X * uF.Y) + r.Y * (-sF.Z * uF.X + sF.X * uF.Z) + r.X * (sF.Z * uF.Y - sF.Y * uF.Z)) * t; lookaheadYaw = tx * tz != 0.0 ? Math.Atan2(tx, tz) : 0.0; if (ty < -1.0) { lookaheadPitch = -0.5 * Math.PI; } else if (ty > 1.0) { lookaheadPitch = 0.5 * Math.PI; } else { lookaheadPitch = Math.Asin(ty); } } else { lookaheadYaw = 0.0; lookaheadPitch = 0.0; } } else { lookaheadYaw = 0.0; lookaheadPitch = 0.0; } { // cab pitch and yaw Vector3 d2 = new Vector3(dF); Vector3 u2 = new Vector3(uF); if ((World.CameraMode == CameraViewMode.Interior | World.CameraMode == CameraViewMode.InteriorLookAhead) & TrainManager.PlayerTrain != null) { int c = TrainManager.PlayerTrain.DriverCar; if (c >= 0) { if (TrainManager.PlayerTrain.Cars[c].CarSections.Length == 0 || !TrainManager.PlayerTrain.Cars[c].CarSections[0].Groups[0].Overlay) { double a = TrainManager.PlayerTrain.Cars[TrainManager.PlayerTrain.DriverCar].DriverPitch; double cosa = Math.Cos(-a); double sina = Math.Sin(-a); d2.Rotate(sF, cosa, sina); u2.Rotate(sF, cosa, sina); } } } cF += sF * CameraCurrentAlignment.Position.X + u2 * CameraCurrentAlignment.Position + d2 * CameraCurrentAlignment.Position.Z; } // yaw, pitch, roll double headYaw = World.CameraCurrentAlignment.Yaw + lookaheadYaw; if ((World.CameraMode == CameraViewMode.Interior | World.CameraMode == CameraViewMode.InteriorLookAhead) & TrainManager.PlayerTrain != null) { if (TrainManager.PlayerTrain.DriverCar >= 0) { headYaw += TrainManager.PlayerTrain.Cars[TrainManager.PlayerTrain.DriverCar].DriverYaw; } } double headPitch = World.CameraCurrentAlignment.Pitch + lookaheadPitch; if ((World.CameraMode == CameraViewMode.Interior | World.CameraMode == CameraViewMode.InteriorLookAhead) & TrainManager.PlayerTrain != null) { if (TrainManager.PlayerTrain.DriverCar >= 0) { headPitch += TrainManager.PlayerTrain.Cars[TrainManager.PlayerTrain.DriverCar].DriverPitch; } } double bodyPitch = 0.0; double bodyRoll = 0.0; double headRoll = World.CameraCurrentAlignment.Roll; // rotation if (CameraRestriction == Camera.RestrictionMode.NotAvailable & (CameraMode == CameraViewMode.Interior | CameraMode == CameraViewMode.InteriorLookAhead)) { // with body and head bodyPitch += CurrentDriverBody.Pitch; headPitch -= 0.2 * CurrentDriverBody.Pitch; bodyRoll += CurrentDriverBody.Roll; headRoll += 0.2 * CurrentDriverBody.Roll; const double bodyHeight = 0.6; const double headHeight = 0.1; { // body pitch double ry = (Math.Cos(-bodyPitch) - 1.0) * bodyHeight; double rz = Math.Sin(-bodyPitch) * bodyHeight; cF += dF * rz + uF * ry; if (bodyPitch != 0.0) { double cosa = Math.Cos(-bodyPitch); double sina = Math.Sin(-bodyPitch); dF.Rotate(sF, cosa, sina); uF.Rotate(sF, cosa, sina); } } { // body roll double rx = Math.Sin(bodyRoll) * bodyHeight; double ry = (Math.Cos(bodyRoll) - 1.0) * bodyHeight; cF += sF * rx + uF * ry; if (bodyRoll != 0.0) { double cosa = Math.Cos(-bodyRoll); double sina = Math.Sin(-bodyRoll); uF.Rotate(dF, cosa, sina); sF.Rotate(dF, cosa, sina); } } { // head yaw double rx = Math.Sin(headYaw) * headHeight; double rz = (Math.Cos(headYaw) - 1.0) * headHeight; cF += sF * rx + dF * rz; if (headYaw != 0.0) { double cosa = Math.Cos(headYaw); double sina = Math.Sin(headYaw); dF.Rotate(uF, cosa, sina); sF.Rotate(uF, cosa, sina); } } { // head pitch double ry = (Math.Cos(-headPitch) - 1.0) * headHeight; double rz = Math.Sin(-headPitch) * headHeight; cF += dF * rz + uF * ry; if (headPitch != 0.0) { double cosa = Math.Cos(-headPitch); double sina = Math.Sin(-headPitch); dF.Rotate(sF, cosa, sina); uF.Rotate(sF, cosa, sina); } } { // head roll double rx = Math.Sin(headRoll) * headHeight; double ry = (Math.Cos(headRoll) - 1.0) * headHeight; cF += sF * rx + uF * ry; if (headRoll != 0.0) { double cosa = Math.Cos(-headRoll); double sina = Math.Sin(-headRoll); uF.Rotate(dF, cosa, sina); sF.Rotate(dF, cosa, sina); } } } else { // without body or head double totalYaw = headYaw; double totalPitch = headPitch + bodyPitch; double totalRoll = bodyRoll + headRoll; if (totalYaw != 0.0) { double cosa = Math.Cos(totalYaw); double sina = Math.Sin(totalYaw); dF.Rotate(uF, cosa, sina); sF.Rotate(uF, cosa, sina); } if (totalPitch != 0.0) { double cosa = Math.Cos(-totalPitch); double sina = Math.Sin(-totalPitch); dF.Rotate(sF, cosa, sina); uF.Rotate(sF, cosa, sina); } if (totalRoll != 0.0) { double cosa = Math.Cos(-totalRoll); double sina = Math.Sin(-totalRoll); uF.Rotate(dF, cosa, sina); sF.Rotate(dF, cosa, sina); } } // finish AbsoluteCameraPosition = cF; AbsoluteCameraDirection = dF; AbsoluteCameraUp = uF; AbsoluteCameraSide = sF; } }
// render events private static void RenderEvents(double CameraX, double CameraY, double CameraZ) { if (TrackManager.CurrentTrack.Elements == null) { return; } LastBoundTexture = 0; if (LightingEnabled) { Gl.glDisable(Gl.GL_LIGHTING); LightingEnabled = false; } if (AlphaTestEnabled) { Gl.glDisable(Gl.GL_ALPHA_TEST); AlphaTestEnabled = false; } double da = -World.BackwardViewingDistance - World.ExtraViewingDistance; double db = World.ForwardViewingDistance + World.ExtraViewingDistance; bool[] sta = new bool[Game.Stations.Length]; // events for (int i = 0; i < TrackManager.CurrentTrack.Elements.Length; i++) { double p = TrackManager.CurrentTrack.Elements[i].StartingTrackPosition; double d = p - World.CameraTrackFollower.TrackPosition; if (d >= da & d <= db) { for (int j = 0; j < TrackManager.CurrentTrack.Elements[i].Events.Length; j++) { TrackManager.GeneralEvent e = TrackManager.CurrentTrack.Elements[i].Events[j]; double dy, dx = 0.0, dz = 0.0; double s; int t; if (e is TrackManager.BrightnessChangeEvent) { s = 0.15; dy = 4.0; t = BrightnessChangeTexture; } else if (e is TrackManager.BackgroundChangeEvent) { s = 0.25; dy = 3.5; t = BackgroundChangeTexture; } else if (e is TrackManager.StationStartEvent) { s = 0.25; dy = 1.6; t = StationStartTexture; TrackManager.StationStartEvent f = (TrackManager.StationStartEvent)e; sta[f.StationIndex] = true; } else if (e is TrackManager.StationEndEvent) { s = 0.25; dy = 1.6; t = StationEndTexture; TrackManager.StationEndEvent f = (TrackManager.StationEndEvent)e; sta[f.StationIndex] = true; } else if (e is TrackManager.LimitChangeEvent) { s = 0.2; dy = 1.1; t = LimitTexture; } else if (e is TrackManager.SectionChangeEvent) { s = 0.2; dy = 0.8; t = SectionTexture; } else if (e is TrackManager.TransponderEvent) { s = 0.15; dy = 0.4; t = TransponderTexture; } else if (e is TrackManager.SoundEvent) { TrackManager.SoundEvent f = (TrackManager.SoundEvent)e; s = 0.2; dx = f.Position.X; dy = f.Position.Y < 0.1 ? 0.1 : f.Position.Y; dz = f.Position.Z; t = SoundTexture; } else { s = 0.2; dy = 1.0; t = -1; } if (t >= 0) { TrackManager.TrackFollower f = new TrackManager.TrackFollower(); f.TriggerType = TrackManager.EventTriggerType.None; f.TrackPosition = p; TrackManager.UpdateTrackFollower(ref f, p + e.TrackPositionDelta, true, false); f.WorldPosition.X += dx * f.WorldSide.X + dy * f.WorldUp.X + dz * f.WorldDirection.X; f.WorldPosition.Y += dx * f.WorldSide.Y + dy * f.WorldUp.Y + dz * f.WorldDirection.Y; f.WorldPosition.Z += dx * f.WorldSide.Z + dy * f.WorldUp.Z + dz * f.WorldDirection.Z; RenderCube(f.WorldPosition, f.WorldDirection, f.WorldUp, f.WorldSide, s, CameraX, CameraY, CameraZ, t); } } } } // stops for (int i = 0; i < sta.Length; i++) { if (sta[i]) { for (int j = 0; j < Game.Stations[i].Stops.Length; j++) { const double dy = 1.4; const double s = 0.2; double p = Game.Stations[i].Stops[j].TrackPosition; TrackManager.TrackFollower f = new TrackManager.TrackFollower(); f.TriggerType = TrackManager.EventTriggerType.None; f.TrackPosition = p; TrackManager.UpdateTrackFollower(ref f, p, true, false); f.WorldPosition.X += dy * f.WorldUp.X; f.WorldPosition.Y += dy * f.WorldUp.Y; f.WorldPosition.Z += dy * f.WorldUp.Z; RenderCube(f.WorldPosition, f.WorldDirection, f.WorldUp, f.WorldSide, s, CameraX, CameraY, CameraZ, StopTexture); } } } // buffers for (int i = 0; i < Game.BufferTrackPositions.Length; i++) { double p = Game.BufferTrackPositions[i]; double d = p - World.CameraTrackFollower.TrackPosition; if (d >= da & d <= db) { const double dy = 2.5; const double s = 0.25; TrackManager.TrackFollower f = new TrackManager.TrackFollower(); f.TriggerType = TrackManager.EventTriggerType.None; f.TrackPosition = p; TrackManager.UpdateTrackFollower(ref f, p, true, false); f.WorldPosition.X += dy * f.WorldUp.X; f.WorldPosition.Y += dy * f.WorldUp.Y; f.WorldPosition.Z += dy * f.WorldUp.Z; RenderCube(f.WorldPosition, f.WorldDirection, f.WorldUp, f.WorldSide, s, CameraX, CameraY, CameraZ, BufferTexture); } } }