Пример #1
0
 // ------------------
 // 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--;
             }
         }
     }
 }
Пример #2
0
        /// <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);
                }
            }
        }
Пример #3
0
        // 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;
            }
        }
Пример #4
0
		// 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);
				}
			}
		}