public MSTSWagonViewer(Viewer viewer, MSTSWagon car) : base(viewer, car) { string steamTexture = viewer.Simulator.BasePath + @"\GLOBAL\TEXTURES\smokemain.ace"; string dieselTexture = viewer.Simulator.BasePath + @"\GLOBAL\TEXTURES\dieselsmoke.ace"; // Particle Drawers called in Wagon so that wagons can also have steam effects. ParticleDrawers = ( from effect in MSTSWagon.EffectData select new KeyValuePair <string, List <ParticleEmitterViewer> >(effect.Key, new List <ParticleEmitterViewer>( from data in effect.Value select new ParticleEmitterViewer(viewer, data, car.WorldPosition)))).ToDictionary(kvp => kvp.Key, kvp => kvp.Value); // Initaialise particle viewers for special steam effects foreach (var emitter in ParticleDrawers) { // Exhaust for steam heating boiler if (emitter.Key.ToLowerInvariant() == "heatingsteamboilerfx") { HeatingSteamBoiler.AddRange(emitter.Value); } foreach (var drawer in HeatingSteamBoiler) { drawer.Initialize(dieselTexture); } // Exhaust for HEP/Power Generator if (emitter.Key.ToLowerInvariant() == "wagongeneratorfx") { WagonGenerator.AddRange(emitter.Value); } foreach (var drawer in WagonGenerator) { drawer.Initialize(dieselTexture); } // Smoke for wood/coal fire if (emitter.Key.ToLowerInvariant() == "wagonsmokefx") { WagonSmoke.AddRange(emitter.Value); } foreach (var drawer in WagonSmoke) { drawer.Initialize(steamTexture); } // Steam leak in heating hose if (emitter.Key.ToLowerInvariant() == "heatinghosefx") { HeatingHose.AddRange(emitter.Value); } foreach (var drawer in HeatingHose) { drawer.Initialize(steamTexture); } } var wagonFolderSlash = Path.GetDirectoryName(car.WagFilePath) + @"\"; TrainCarShape = car.MainShapeFileName != string.Empty ? new PoseableShape(viewer, wagonFolderSlash + car.MainShapeFileName + '\0' + wagonFolderSlash, car.WorldPosition, ShapeFlags.ShadowCaster) : new PoseableShape(viewer, null, car.WorldPosition); // This insection initialises the MSTS style freight animation - can either be for a coal load, which will adjust with usage, or a static animation, such as additional shape. if (car.FreightShapeFileName != null) { car.HasFreightAnim = true; FreightShape = new AnimatedShape(viewer, wagonFolderSlash + car.FreightShapeFileName + '\0' + wagonFolderSlash, new WorldPosition(car.WorldPosition), ShapeFlags.ShadowCaster); // Reproducing MSTS "bug" of not allowing tender animation in case both minLevel and maxLevel are 0 or maxLevel < minLevel // Applies to both a standard tender locomotive or a tank locomotive (where coal load is on same "wagon" as the locomotive - for the coal load on a tender or tank locomotive - in operation it will raise or lower with caol usage if (MSTSWagon.WagonType == TrainCar.WagonTypes.Tender || MSTSWagon is MSTSSteamLocomotive) { var NonTenderSteamLocomotive = MSTSWagon as MSTSSteamLocomotive; if ((MSTSWagon.WagonType == TrainCar.WagonTypes.Tender || MSTSWagon is MSTSLocomotive && (MSTSWagon.EngineType == TrainCar.EngineTypes.Steam && NonTenderSteamLocomotive.IsTenderRequired == 0.0)) && MSTSWagon.FreightAnimMaxLevelM != 0 && MSTSWagon.FreightAnimFlag > 0 && MSTSWagon.FreightAnimMaxLevelM > MSTSWagon.FreightAnimMinLevelM) { // Force allowing animation: if (FreightShape.SharedShape.LodControls.Length > 0 && FreightShape.SharedShape.LodControls[0].DistanceLevels.Length > 0 && FreightShape.SharedShape.LodControls[0].DistanceLevels[0].SubObjects.Length > 0 && FreightShape.SharedShape.LodControls[0].DistanceLevels[0].SubObjects[0].ShapePrimitives.Length > 0 && FreightShape.SharedShape.LodControls[0].DistanceLevels[0].SubObjects[0].ShapePrimitives[0].Hierarchy.Length > 0) { FreightShape.SharedShape.LodControls[0].DistanceLevels[0].SubObjects[0].ShapePrimitives[0].Hierarchy[0] = 1; } } } } if (car.InteriorShapeFileName != null) { InteriorShape = new AnimatedShape(viewer, wagonFolderSlash + car.InteriorShapeFileName + '\0' + wagonFolderSlash, car.WorldPosition, ShapeFlags.Interior, 30.0f); } RunningGear = new AnimatedPart(TrainCarShape); Pantograph1 = new AnimatedPart(TrainCarShape); Pantograph2 = new AnimatedPart(TrainCarShape); Pantograph3 = new AnimatedPart(TrainCarShape); Pantograph4 = new AnimatedPart(TrainCarShape); LeftDoor = new AnimatedPart(TrainCarShape); RightDoor = new AnimatedPart(TrainCarShape); Mirrors = new AnimatedPart(TrainCarShape); Wipers = new AnimatedPart(TrainCarShape); UnloadingParts = new AnimatedPart(TrainCarShape); Bell = new AnimatedPart(TrainCarShape); if (car.FreightAnimations != null) { FreightAnimations = new FreightAnimationsViewer(viewer, car, wagonFolderSlash); } LoadCarSounds(wagonFolderSlash); //if (!(MSTSWagon is MSTSLocomotive)) // LoadTrackSounds(); Viewer.SoundProcess.AddSoundSource(this, new TrackSoundSource(MSTSWagon, Viewer)); // Determine if it has first pantograph. So we can match unnamed panto parts correctly for (var i = 0; i < TrainCarShape.Hierarchy.Length; i++) { if (TrainCarShape.SharedShape.MatrixNames[i].Contains('1')) { if (TrainCarShape.SharedShape.MatrixNames[i].ToUpper().StartsWith("PANTO")) { HasFirstPanto = true; break; } } } // Check bogies and wheels to find out what we have. for (var i = 0; i < TrainCarShape.Hierarchy.Length; i++) { if (TrainCarShape.SharedShape.MatrixNames[i].Equals("BOGIE1")) { bogieMatrix1 = i; numBogie1 += 1; } if (TrainCarShape.SharedShape.MatrixNames[i].Equals("BOGIE2")) { bogieMatrix2 = i; numBogie2 += 1; } if (TrainCarShape.SharedShape.MatrixNames[i].Equals("BOGIE")) { bogieMatrix1 = i; } // For now, the total axle count consisting of axles that are part of the bogie are being counted. if (TrainCarShape.SharedShape.MatrixNames[i].Contains("WHEELS")) { if (TrainCarShape.SharedShape.MatrixNames[i].Length == 8) { var tpmatrix = TrainCarShape.SharedShape.GetParentMatrix(i); if (TrainCarShape.SharedShape.MatrixNames[i].Equals("WHEELS11") && tpmatrix == bogieMatrix1) { bogie1Axles += 1; } if (TrainCarShape.SharedShape.MatrixNames[i].Equals("WHEELS12") && tpmatrix == bogieMatrix1) { bogie1Axles += 1; } if (TrainCarShape.SharedShape.MatrixNames[i].Equals("WHEELS13") && tpmatrix == bogieMatrix1) { bogie1Axles += 1; } if (TrainCarShape.SharedShape.MatrixNames[i].Equals("WHEELS21") && tpmatrix == bogieMatrix1) { bogie1Axles += 1; } if (TrainCarShape.SharedShape.MatrixNames[i].Equals("WHEELS22") && tpmatrix == bogieMatrix1) { bogie1Axles += 1; } if (TrainCarShape.SharedShape.MatrixNames[i].Equals("WHEELS23") && tpmatrix == bogieMatrix1) { bogie1Axles += 1; } if (TrainCarShape.SharedShape.MatrixNames[i].Equals("WHEELS11") && tpmatrix == bogieMatrix2) { bogie2Axles += 1; } if (TrainCarShape.SharedShape.MatrixNames[i].Equals("WHEELS12") && tpmatrix == bogieMatrix2) { bogie2Axles += 1; } if (TrainCarShape.SharedShape.MatrixNames[i].Equals("WHEELS13") && tpmatrix == bogieMatrix2) { bogie2Axles += 1; } if (TrainCarShape.SharedShape.MatrixNames[i].Equals("WHEELS21") && tpmatrix == bogieMatrix2) { bogie2Axles += 1; } if (TrainCarShape.SharedShape.MatrixNames[i].Equals("WHEELS21") && tpmatrix == bogieMatrix2) { bogie2Axles += 1; } if (TrainCarShape.SharedShape.MatrixNames[i].Equals("WHEELS23") && tpmatrix == bogieMatrix2) { bogie2Axles += 1; } } } } // Match up all the matrices with their parts. for (var i = 0; i < TrainCarShape.Hierarchy.Length; i++) { if (TrainCarShape.Hierarchy[i] == -1) { MatchMatrixToPart(car, i, 0); } } car.SetUpWheels(); // If we have two pantographs, 2 is the forwards pantograph, unlike when there's only one. if (!car.Flipped && !Pantograph1.Empty() && !Pantograph2.Empty()) { AnimatedPart.Swap(ref Pantograph1, ref Pantograph2); } Pantograph1.SetState(MSTSWagon.Pantographs[1].CommandUp); Pantograph2.SetState(MSTSWagon.Pantographs[2].CommandUp); if (MSTSWagon.Pantographs.List.Count > 2) { Pantograph3.SetState(MSTSWagon.Pantographs[3].CommandUp); } if (MSTSWagon.Pantographs.List.Count > 3) { Pantograph4.SetState(MSTSWagon.Pantographs[4].CommandUp); } LeftDoor.SetState(MSTSWagon.DoorLeftOpen); RightDoor.SetState(MSTSWagon.DoorRightOpen); Mirrors.SetState(MSTSWagon.MirrorOpen); UnloadingParts.SetState(MSTSWagon.UnloadingPartsOpen); InitializeUserInputCommands(); }
private void UpdateAnimation(RenderFrame frame, ElapsedTime elapsedTime) { float distanceTravelledM = 0.0f; // Distance travelled by non-driven wheels float distanceTravelledDrivenM = 0.0f; // Distance travelled by driven wheels float AnimationWheelRadiusM = 0.0f; // Radius of non driven wheels float AnimationDriveWheelRadiusM = 0.0f; // Radius of driven wheels if (MSTSWagon.IsDriveable && MSTSWagon.Simulator.UseAdvancedAdhesion) { //TODO: next code line has been modified to flip trainset physics in order to get viewing direction coincident with loco direction when using rear cab. // To achieve the same result with other means, without flipping trainset physics, the line should be changed as follows: // distanceTravelledM = MSTSWagon.WheelSpeedMpS * elapsedTime.ClockSeconds; if (Car.EngineType == Orts.Simulation.RollingStocks.TrainCar.EngineTypes.Steam) // Steam locomotive so set up different speeds for different driver and non-driver wheels { distanceTravelledM = ((MSTSWagon.Train != null && MSTSWagon.Train.IsPlayerDriven && ((MSTSLocomotive)MSTSWagon).UsingRearCab) ? -1 : 1) * MSTSWagon.WheelSpeedMpS * elapsedTime.ClockSeconds; distanceTravelledDrivenM = ((MSTSWagon.Train != null && MSTSWagon.Train.IsPlayerDriven && ((MSTSLocomotive)MSTSWagon).UsingRearCab) ? -1 : 1) * MSTSWagon.WheelSpeedSlipMpS * elapsedTime.ClockSeconds; // Set values of wheel radius - assume that drive wheel and non driven wheel are different sizes AnimationWheelRadiusM = MSTSWagon.WheelRadiusM; AnimationDriveWheelRadiusM = MSTSWagon.DriverWheelRadiusM; } else // Other driveable rolling stock - all wheels have same speed. { distanceTravelledM = ((MSTSWagon.Train != null && MSTSWagon.Train.IsPlayerDriven && ((MSTSLocomotive)MSTSWagon).UsingRearCab) ? -1 : 1) * MSTSWagon.WheelSpeedMpS * elapsedTime.ClockSeconds; distanceTravelledDrivenM = ((MSTSWagon.Train != null && MSTSWagon.Train.IsPlayerDriven && ((MSTSLocomotive)MSTSWagon).UsingRearCab) ? -1 : 1) * MSTSWagon.WheelSpeedMpS * elapsedTime.ClockSeconds; // Set values of wheel radius - assume that drive wheel and non driven wheel are same sizes AnimationWheelRadiusM = MSTSWagon.WheelRadiusM; AnimationDriveWheelRadiusM = MSTSWagon.WheelRadiusM; } } else // set values for simple adhesion { distanceTravelledM = ((MSTSWagon.IsDriveable && MSTSWagon.Train != null && MSTSWagon.Train.IsPlayerDriven && ((MSTSLocomotive)MSTSWagon).UsingRearCab) ? -1 : 1) * MSTSWagon.SpeedMpS * elapsedTime.ClockSeconds; distanceTravelledDrivenM = ((MSTSWagon.IsDriveable && MSTSWagon.Train != null && MSTSWagon.Train.IsPlayerDriven && ((MSTSLocomotive)MSTSWagon).UsingRearCab) ? -1 : 1) * MSTSWagon.SpeedMpS * elapsedTime.ClockSeconds; // Set values of wheel radius - assume that drive wheel and non driven wheel are same sizes if (Car.EngineType == Orts.Simulation.RollingStocks.TrainCar.EngineTypes.Steam) // set values for steam stock { AnimationWheelRadiusM = MSTSWagon.WheelRadiusM; AnimationDriveWheelRadiusM = MSTSWagon.DriverWheelRadiusM; } else // set values for non-driveable stock, eg wagons, and driveable stock such as diesels, electric locomotives { AnimationWheelRadiusM = MSTSWagon.WheelRadiusM; AnimationDriveWheelRadiusM = MSTSWagon.WheelRadiusM; } } if (Car.BrakeSkid) // if car wheels are skidding because of brakes lockin wheels up then stop wheels rotating. { distanceTravelledM = 0.0f; distanceTravelledDrivenM = 0.0f; } // Running gear and drive wheel rotation (animation) in steam locomotives if (!RunningGear.Empty() && AnimationDriveWheelRadiusM > 0.001) { RunningGear.UpdateLoop(distanceTravelledDrivenM / MathHelper.TwoPi / AnimationDriveWheelRadiusM); } // Wheel rotation (animation) - for non-drive wheels in steam locomotives and all wheels in other stock if (WheelPartIndexes.Count > 0) { var wheelCircumferenceM = MathHelper.TwoPi * AnimationWheelRadiusM; var rotationalDistanceR = MathHelper.TwoPi * distanceTravelledM / wheelCircumferenceM; // in radians WheelRotationR = MathHelper.WrapAngle(WheelRotationR - rotationalDistanceR); var wheelRotationMatrix = Matrix.CreateRotationX(WheelRotationR); foreach (var iMatrix in WheelPartIndexes) { TrainCarShape.XNAMatrices[iMatrix] = wheelRotationMatrix * TrainCarShape.SharedShape.Matrices[iMatrix]; } } #if DEBUG_WHEEL_ANIMATION Trace.TraceInformation("========================== Debug Animation in MSTSWagonViewer.cs =========================================="); Trace.TraceInformation("Slip speed - Car ID: {0} WheelDistance: {1} SlipWheelDistance: {2}", Car.CarID, distanceTravelledM, distanceTravelledDrivenM); Trace.TraceInformation("Wag Speed - Wheelspeed: {0} Slip: {1} Train: {2}", MSTSWagon.WheelSpeedMpS, MSTSWagon.WheelSpeedSlipMpS, MSTSWagon.SpeedMpS); Trace.TraceInformation("Wheel Radius - DriveWheel: {0} NonDriveWheel: {1}", AnimationDriveWheelRadiusM, AnimationWheelRadiusM); #endif // truck angle animation foreach (var p in Car.Parts) { if (p.iMatrix <= 0) { continue; } Matrix m = Matrix.Identity; m.Translation = TrainCarShape.SharedShape.Matrices[p.iMatrix].Translation; m.M11 = p.Cos; m.M13 = p.Sin; m.M31 = -p.Sin; m.M33 = p.Cos; // To cancel out any vibration, apply the inverse here. If no vibration is present, this matrix will be Matrix.Identity. TrainCarShape.XNAMatrices[p.iMatrix] = Car.VibrationInverseMatrix * m; } // Applies MSTS style freight animation for coal load on the locomotive, crews, and other static animations. // Takes the form of FreightAnim ( A B C ) // MSTS allowed crew figures to be inserted into the tender WAG file and thus be displayed on the locomotive. // It appears that only one MSTS type FA can be used per vehicle (to be confirmed?) // For coal load variation, C should be absent (set to 1 when read in WAG file) or >0 - sets FreightAnimFlag; and A > B // To disable coal load variation and insert a static (crew) shape on the tender breech, one of the conditions indicated above if (FreightShape != null && !(Viewer.Camera.AttachedCar == this.MSTSWagon && Viewer.Camera.Style == Camera.Styles.ThreeDimCab)) { // Define default position of shape FreightShape.Location.XNAMatrix = Car.WorldPosition.XNAMatrix; FreightShape.Location.TileX = Car.WorldPosition.TileX; FreightShape.Location.TileZ = Car.WorldPosition.TileZ; bool SteamAnimShape = false; float FuelControllerLevel = 0.0f; // For coal load variation on locomotives determine the current fuel level - and whether locomotive is a tender or tank type locomotive. if (MSTSWagon.WagonType == TrainCar.WagonTypes.Tender || MSTSWagon is MSTSSteamLocomotive) { var NonTenderSteamLocomotive = MSTSWagon as MSTSSteamLocomotive; if (MSTSWagon.WagonType == TrainCar.WagonTypes.Tender || MSTSWagon is MSTSLocomotive && (MSTSWagon.EngineType == TrainCar.EngineTypes.Steam && NonTenderSteamLocomotive.IsTenderRequired == 0.0)) { if (MSTSWagon.TendersSteamLocomotive == null) { MSTSWagon.FindTendersSteamLocomotive(); } if (MSTSWagon.TendersSteamLocomotive != null) { FuelControllerLevel = MSTSWagon.TendersSteamLocomotive.FuelController.CurrentValue; SteamAnimShape = true; } else if (NonTenderSteamLocomotive != null) { FuelControllerLevel = NonTenderSteamLocomotive.FuelController.CurrentValue; SteamAnimShape = true; } } } // Set height of FAs - if relevant conditions met, use default position co-ords defined above if (FreightShape.XNAMatrices.Length > 0) { // For tender coal load animation if (MSTSWagon.FreightAnimFlag > 0 && MSTSWagon.FreightAnimMaxLevelM > MSTSWagon.FreightAnimMinLevelM && SteamAnimShape) { FreightShape.XNAMatrices[0].M42 = MSTSWagon.FreightAnimMinLevelM + FuelControllerLevel * (MSTSWagon.FreightAnimMaxLevelM - MSTSWagon.FreightAnimMinLevelM); } // reproducing MSTS strange behavior; used to display loco crew when attached to tender else if (MSTSWagon.WagonType == TrainCar.WagonTypes.Tender) { FreightShape.Location.XNAMatrix.M42 += MSTSWagon.FreightAnimMaxLevelM; } } // Display Animation Shape FreightShape.PrepareFrame(frame, elapsedTime); } if (FreightAnimations != null) { foreach (var freightAnim in FreightAnimations.Animations) { if (freightAnim.Animation is FreightAnimationStatic) { var animation = freightAnim.Animation as FreightAnimationStatic; if (!((animation.Visibility[(int)FreightAnimationStatic.VisibleFrom.Cab3D] && Viewer.Camera.AttachedCar == this.MSTSWagon && Viewer.Camera.Style == Camera.Styles.ThreeDimCab) || (animation.Visibility[(int)FreightAnimationStatic.VisibleFrom.Cab2D] && Viewer.Camera.AttachedCar == this.MSTSWagon && Viewer.Camera.Style == Camera.Styles.Cab) || (animation.Visibility[(int)FreightAnimationStatic.VisibleFrom.Outside] && (Viewer.Camera.AttachedCar != this.MSTSWagon || (Viewer.Camera.Style != Camera.Styles.ThreeDimCab && Viewer.Camera.Style != Camera.Styles.Cab))))) { continue; } } if (freightAnim.FreightShape != null && !((freightAnim.Animation is FreightAnimationContinuous) && (freightAnim.Animation as FreightAnimationContinuous).LoadPerCent == 0)) { freightAnim.FreightShape.Location.XNAMatrix = Car.WorldPosition.XNAMatrix; freightAnim.FreightShape.Location.TileX = Car.WorldPosition.TileX; freightAnim.FreightShape.Location.TileZ = Car.WorldPosition.TileZ; if (freightAnim.FreightShape.XNAMatrices.Length > 0) { if (freightAnim.Animation is FreightAnimationContinuous) { var continuousFreightAnim = freightAnim.Animation as FreightAnimationContinuous; if (MSTSWagon.FreightAnimations.IsGondola) { freightAnim.FreightShape.XNAMatrices[0] = TrainCarShape.XNAMatrices[1]; } freightAnim.FreightShape.XNAMatrices[0].M42 = continuousFreightAnim.MinHeight + continuousFreightAnim.LoadPerCent / 100 * (continuousFreightAnim.MaxHeight - continuousFreightAnim.MinHeight); } if (freightAnim.Animation is FreightAnimationStatic) { var staticFreightAnim = freightAnim.Animation as FreightAnimationStatic; freightAnim.FreightShape.XNAMatrices[0].M41 = staticFreightAnim.XOffset; freightAnim.FreightShape.XNAMatrices[0].M42 = staticFreightAnim.YOffset; freightAnim.FreightShape.XNAMatrices[0].M43 = staticFreightAnim.ZOffset; } } // Forcing rotation of freight shape freightAnim.FreightShape.PrepareFrame(frame, elapsedTime); } } } // Control visibility of passenger cabin when inside it if (Viewer.Camera.AttachedCar == this.MSTSWagon && //( Viewer.ViewPoint == Viewer.ViewPoints.Cab || // TODO, restore when we complete cab views - Viewer.Camera.Style == Camera.Styles.Passenger) { // We are in the passenger cabin if (InteriorShape != null) { InteriorShape.PrepareFrame(frame, elapsedTime); } else { TrainCarShape.PrepareFrame(frame, elapsedTime); } } else { // Skip drawing if 2D or 3D Cab view - Cab view already drawn - by GeorgeS changed by DennisAT if (Viewer.Camera.AttachedCar == this.MSTSWagon && (Viewer.Camera.Style == Camera.Styles.Cab || Viewer.Camera.Style == Camera.Styles.ThreeDimCab)) { return; } // We are outside the passenger cabin TrainCarShape.PrepareFrame(frame, elapsedTime); } }
private void UpdateAnimation(RenderFrame frame, ElapsedTime elapsedTime) { float distanceTravelledM = 0.0f; // Distance travelled by non-driven wheels float distanceTravelledDrivenM = 0.0f; // Distance travelled by driven wheels float AnimationWheelRadiusM = 0.0f; // Radius of non driven wheels float AnimationDriveWheelRadiusM = 0.0f; // Radius of driven wheels if (MSTSWagon.IsDriveable && MSTSWagon.Simulator.UseAdvancedAdhesion) { //TODO: next code line has been modified to flip trainset physics in order to get viewing direction coincident with loco direction when using rear cab. // To achieve the same result with other means, without flipping trainset physics, the line should be changed as follows: // distanceTravelledM = MSTSWagon.WheelSpeedMpS * elapsedTime.ClockSeconds; if (Car.EngineType == Orts.Simulation.RollingStocks.TrainCar.EngineTypes.Steam) // Steam locomotive so set up different speeds for different driver and non-driver wheels { distanceTravelledM = ((MSTSWagon.Train != null && MSTSWagon.Train.IsPlayerDriven && ((MSTSLocomotive)MSTSWagon).UsingRearCab) ? -1 : 1) * MSTSWagon.WheelSpeedMpS * elapsedTime.ClockSeconds; distanceTravelledDrivenM = ((MSTSWagon.Train != null && MSTSWagon.Train.IsPlayerDriven && ((MSTSLocomotive)MSTSWagon).UsingRearCab) ? -1 : 1) * MSTSWagon.WheelSpeedSlipMpS * elapsedTime.ClockSeconds; // Set values of wheel radius - assume that drive wheel and non driven wheel are different sizes AnimationWheelRadiusM = MSTSWagon.WheelRadiusM; AnimationDriveWheelRadiusM = MSTSWagon.DriverWheelRadiusM; } else // Other driveable rolling stock - all wheels have same speed. { distanceTravelledM = ((MSTSWagon.Train != null && MSTSWagon.Train.IsPlayerDriven && ((MSTSLocomotive)MSTSWagon).UsingRearCab) ? -1 : 1) * MSTSWagon.WheelSpeedMpS * elapsedTime.ClockSeconds; distanceTravelledDrivenM = ((MSTSWagon.Train != null && MSTSWagon.Train.IsPlayerDriven && ((MSTSLocomotive)MSTSWagon).UsingRearCab) ? -1 : 1) * MSTSWagon.WheelSpeedMpS * elapsedTime.ClockSeconds; // Set values of wheel radius - assume that drive wheel and non driven wheel are same sizes AnimationWheelRadiusM = MSTSWagon.WheelRadiusM; AnimationDriveWheelRadiusM = MSTSWagon.WheelRadiusM; } } else // set values for simple adhesion { distanceTravelledM = MSTSWagon.SpeedMpS * elapsedTime.ClockSeconds; distanceTravelledDrivenM = MSTSWagon.SpeedMpS * elapsedTime.ClockSeconds; // Set values of wheel radius - assume that drive wheel and non driven wheel are same sizes if (Car.EngineType == Orts.Simulation.RollingStocks.TrainCar.EngineTypes.Steam) // set values for steam stock { AnimationWheelRadiusM = MSTSWagon.WheelRadiusM; AnimationDriveWheelRadiusM = MSTSWagon.DriverWheelRadiusM; } else // set values for non-driveable stock, eg wagons, and driveable stock such as diesels, electric locomotives { AnimationWheelRadiusM = MSTSWagon.WheelRadiusM; AnimationDriveWheelRadiusM = MSTSWagon.WheelRadiusM; } } if (Car.BrakeSkid) // if car wheels are skidding because of brakes lockin wheels up then stop wheels rotating. { distanceTravelledM = 0.0f; distanceTravelledDrivenM = 0.0f; } // Running gear and drive wheel rotation (animation) in steam locomotives if (!RunningGear.Empty() && AnimationDriveWheelRadiusM > 0.001) { RunningGear.UpdateLoop(distanceTravelledDrivenM / MathHelper.TwoPi / AnimationDriveWheelRadiusM); } // Wheel rotation (animation) - for non-drive wheels in steam locomotives and all wheels in other stock if (WheelPartIndexes.Count > 0) { var wheelCircumferenceM = MathHelper.TwoPi * AnimationWheelRadiusM; var rotationalDistanceR = MathHelper.TwoPi * distanceTravelledM / wheelCircumferenceM; // in radians WheelRotationR = MathHelper.WrapAngle(WheelRotationR - rotationalDistanceR); var wheelRotationMatrix = Matrix.CreateRotationX(WheelRotationR); foreach (var iMatrix in WheelPartIndexes) { TrainCarShape.XNAMatrices[iMatrix] = wheelRotationMatrix * TrainCarShape.SharedShape.Matrices[iMatrix]; } } #if DEBUG_WHEEL_ANIMATION Trace.TraceInformation("========================== Debug Animation in MSTSWagonViewer.cs =========================================="); Trace.TraceInformation("Slip speed - Car ID: {0} WheelDistance: {1} SlipWheelDistance: {2}", Car.CarID, distanceTravelledM, distanceTravelledDrivenM); Trace.TraceInformation("Wag Speed - Wheelspeed: {0} Slip: {1} Train: {2}", MSTSWagon.WheelSpeedMpS, MSTSWagon.WheelSpeedSlipMpS, MSTSWagon.SpeedMpS); Trace.TraceInformation("Wheel Radius - DriveWheel: {0} NonDriveWheel: {1}", AnimationDriveWheelRadiusM, AnimationWheelRadiusM); #endif // truck angle animation foreach (var p in Car.Parts) { if (p.iMatrix <= 0) { continue; } Matrix m = Matrix.Identity; m.Translation = TrainCarShape.SharedShape.Matrices[p.iMatrix].Translation; m.M11 = p.Cos; m.M13 = p.Sin; m.M31 = -p.Sin; m.M33 = p.Cos; // To cancel out any vibration, apply the inverse here. If no vibration is present, this matrix will be Matrix.Identity. TrainCarShape.XNAMatrices[p.iMatrix] = Car.VibrationInverseMatrix * m; } if (FreightShape != null) { FreightShape.Location.XNAMatrix = Car.WorldPosition.XNAMatrix; FreightShape.Location.TileX = Car.WorldPosition.TileX; FreightShape.Location.TileZ = Car.WorldPosition.TileZ; if (MSTSWagon.WagonType == TrainCar.WagonTypes.Tender) { if (MSTSWagon.TendersSteamLocomotive == null) { MSTSWagon.FindTendersSteamLocomotive(); } if (FreightShape.XNAMatrices.Length > 0 && MSTSWagon.TendersSteamLocomotive != null) { if (MSTSWagon.FreightAnimFlag > 0 && MSTSWagon.FreightAnimMaxLevelM > MSTSWagon.FreightAnimMinLevelM) { FreightShape.XNAMatrices[0].M42 = MSTSWagon.FreightAnimMinLevelM + MSTSWagon.TendersSteamLocomotive.FuelController.CurrentValue * (MSTSWagon.FreightAnimMaxLevelM - MSTSWagon.FreightAnimMinLevelM); } else // reproducing MSTS strange behavior; used to display loco crew { FreightShape.Location.XNAMatrix.M42 += MSTSWagon.FreightAnimMaxLevelM; } } } FreightShape.PrepareFrame(frame, elapsedTime); } if (FreightAnimations != null) { foreach (var freightAnim in FreightAnimations.Animations) { if (freightAnim.FreightShape != null && !((freightAnim.Animation is FreightAnimationContinuous) && (freightAnim.Animation as FreightAnimationContinuous).LoadPerCent == 0)) { freightAnim.FreightShape.Location.XNAMatrix = Car.WorldPosition.XNAMatrix; freightAnim.FreightShape.Location.TileX = Car.WorldPosition.TileX; freightAnim.FreightShape.Location.TileZ = Car.WorldPosition.TileZ; if (freightAnim.FreightShape.XNAMatrices.Length > 0) { if (freightAnim.Animation is FreightAnimationContinuous) { var continuousFreightAnim = freightAnim.Animation as FreightAnimationContinuous; if (MSTSWagon.FreightAnimations.IsGondola) { freightAnim.FreightShape.XNAMatrices[0] = TrainCarShape.XNAMatrices[1]; } freightAnim.FreightShape.XNAMatrices[0].M42 = continuousFreightAnim.MinHeight + continuousFreightAnim.LoadPerCent / 100 * (continuousFreightAnim.MaxHeight - continuousFreightAnim.MinHeight); } if (freightAnim.Animation is FreightAnimationStatic) { var staticFreightAnim = freightAnim.Animation as FreightAnimationStatic; freightAnim.FreightShape.XNAMatrices[0].M41 = staticFreightAnim.XOffset; freightAnim.FreightShape.XNAMatrices[0].M42 = staticFreightAnim.YOffset; freightAnim.FreightShape.XNAMatrices[0].M43 = staticFreightAnim.ZOffset; } } // Forcing rotation of freight shape freightAnim.FreightShape.PrepareFrame(frame, elapsedTime); } } } // Control visibility of passenger cabin when inside it if (Viewer.Camera.AttachedCar == this.MSTSWagon && //( Viewer.ViewPoint == Viewer.ViewPoints.Cab || // TODO, restore when we complete cab views - Viewer.Camera.Style == Camera.Styles.Passenger) { // We are in the passenger cabin if (InteriorShape != null) { InteriorShape.PrepareFrame(frame, elapsedTime); } else { TrainCarShape.PrepareFrame(frame, elapsedTime); } } else { // Skip drawing if CAB view - draw 2D view instead - by GeorgeS if (Viewer.Camera.AttachedCar == this.MSTSWagon && Viewer.Camera.Style == Camera.Styles.Cab) { return; } // We are outside the passenger cabin TrainCarShape.PrepareFrame(frame, elapsedTime); } }
public MSTSWagonViewer(Viewer viewer, MSTSWagon car) : base(viewer, car) { var wagonFolderSlash = Path.GetDirectoryName(car.WagFilePath) + @"\"; TrainCarShape = car.MainShapeFileName != string.Empty ? new PoseableShape(viewer, wagonFolderSlash + car.MainShapeFileName + '\0' + wagonFolderSlash, car.WorldPosition, ShapeFlags.ShadowCaster) : new PoseableShape(viewer, null, car.WorldPosition); if (car.FreightShapeFileName != null) { car.HasFreightAnim = true; FreightShape = new AnimatedShape(viewer, wagonFolderSlash + car.FreightShapeFileName + '\0' + wagonFolderSlash, new WorldPosition(car.WorldPosition), ShapeFlags.ShadowCaster); // Reproducing MSTS "bug" of not allowing tender animation in case both minLevel and maxLevel are 0 or maxLevel < minLevel if (MSTSWagon.WagonType == TrainCar.WagonTypes.Tender && MSTSWagon.FreightAnimMaxLevelM != 0 && MSTSWagon.FreightAnimFlag > 0 && MSTSWagon.FreightAnimMaxLevelM > MSTSWagon.FreightAnimMinLevelM) { // Force allowing animation: if (FreightShape.SharedShape.LodControls.Length > 0 && FreightShape.SharedShape.LodControls[0].DistanceLevels.Length > 0 && FreightShape.SharedShape.LodControls[0].DistanceLevels[0].SubObjects.Length > 0 && FreightShape.SharedShape.LodControls[0].DistanceLevels[0].SubObjects[0].ShapePrimitives.Length > 0 && FreightShape.SharedShape.LodControls[0].DistanceLevels[0].SubObjects[0].ShapePrimitives[0].Hierarchy.Length > 0) { FreightShape.SharedShape.LodControls[0].DistanceLevels[0].SubObjects[0].ShapePrimitives[0].Hierarchy[0] = 1; } } } if (car.InteriorShapeFileName != null) { InteriorShape = new AnimatedShape(viewer, wagonFolderSlash + car.InteriorShapeFileName + '\0' + wagonFolderSlash, car.WorldPosition, ShapeFlags.Interior, 30.0f); } RunningGear = new AnimatedPart(TrainCarShape); Pantograph1 = new AnimatedPart(TrainCarShape); Pantograph2 = new AnimatedPart(TrainCarShape); LeftDoor = new AnimatedPart(TrainCarShape); RightDoor = new AnimatedPart(TrainCarShape); Mirrors = new AnimatedPart(TrainCarShape); Wipers = new AnimatedPart(TrainCarShape); UnloadingParts = new AnimatedPart(TrainCarShape); if (car.FreightAnimations != null) { FreightAnimations = new FreightAnimationsViewer(viewer, car, wagonFolderSlash); } LoadCarSounds(wagonFolderSlash); //if (!(MSTSWagon is MSTSLocomotive)) // LoadTrackSounds(); Viewer.SoundProcess.AddSoundSource(this, new TrackSoundSource(MSTSWagon, Viewer)); // Determine if it has first pantograph. So we can match unnamed panto parts correctly for (var i = 0; i < TrainCarShape.Hierarchy.Length; i++) { if (TrainCarShape.SharedShape.MatrixNames[i].Contains('1')) { if (TrainCarShape.SharedShape.MatrixNames[i].ToUpper().StartsWith("PANTO")) { HasFirstPanto = true; break; } } } // Check bogies and wheels to find out what we have. for (var i = 0; i < TrainCarShape.Hierarchy.Length; i++) { if (TrainCarShape.SharedShape.MatrixNames[i].Equals("BOGIE1")) { bogieMatrix1 = i; numBogie1 += 1; } if (TrainCarShape.SharedShape.MatrixNames[i].Equals("BOGIE2")) { bogieMatrix2 = i; numBogie2 += 1; } if (TrainCarShape.SharedShape.MatrixNames[i].Equals("BOGIE")) { bogieMatrix1 = i; numBogie += 1; } // For now, the total axle count consisting of axles that are part of the bogie are being counted. if (TrainCarShape.SharedShape.MatrixNames[i].Contains("WHEELS")) { if (TrainCarShape.SharedShape.MatrixNames[i].Length == 8) { var tpmatrix = TrainCarShape.SharedShape.GetParentMatrix(i); if (TrainCarShape.SharedShape.MatrixNames[i].Equals("WHEELS11") && tpmatrix == bogieMatrix1) { bogie1Axles += 1; } if (TrainCarShape.SharedShape.MatrixNames[i].Equals("WHEELS12") && tpmatrix == bogieMatrix1) { bogie1Axles += 1; } if (TrainCarShape.SharedShape.MatrixNames[i].Equals("WHEELS13") && tpmatrix == bogieMatrix1) { bogie1Axles += 1; } if (TrainCarShape.SharedShape.MatrixNames[i].Equals("WHEELS21") && tpmatrix == bogieMatrix1) { bogie1Axles += 1; } if (TrainCarShape.SharedShape.MatrixNames[i].Equals("WHEELS22") && tpmatrix == bogieMatrix1) { bogie1Axles += 1; } if (TrainCarShape.SharedShape.MatrixNames[i].Equals("WHEELS23") && tpmatrix == bogieMatrix1) { bogie1Axles += 1; } if (TrainCarShape.SharedShape.MatrixNames[i].Equals("WHEELS11") && tpmatrix == bogieMatrix2) { bogie2Axles += 1; } if (TrainCarShape.SharedShape.MatrixNames[i].Equals("WHEELS12") && tpmatrix == bogieMatrix2) { bogie2Axles += 1; } if (TrainCarShape.SharedShape.MatrixNames[i].Equals("WHEELS13") && tpmatrix == bogieMatrix2) { bogie2Axles += 1; } if (TrainCarShape.SharedShape.MatrixNames[i].Equals("WHEELS21") && tpmatrix == bogieMatrix2) { bogie2Axles += 1; } if (TrainCarShape.SharedShape.MatrixNames[i].Equals("WHEELS21") && tpmatrix == bogieMatrix2) { bogie2Axles += 1; } if (TrainCarShape.SharedShape.MatrixNames[i].Equals("WHEELS23") && tpmatrix == bogieMatrix2) { bogie2Axles += 1; } } } } // Match up all the matrices with their parts. for (var i = 0; i < TrainCarShape.Hierarchy.Length; i++) { if (TrainCarShape.Hierarchy[i] == -1) { MatchMatrixToPart(car, i, 0); } } car.SetUpWheels(); // If we have two pantographs, 2 is the forwards pantograph, unlike when there's only one. if (!car.Flipped && !Pantograph1.Empty() && !Pantograph2.Empty()) { AnimatedPart.Swap(ref Pantograph1, ref Pantograph2); } Pantograph1.SetState(MSTSWagon.Pantographs[1].CommandUp); Pantograph2.SetState(MSTSWagon.Pantographs[2].CommandUp); LeftDoor.SetState(MSTSWagon.DoorLeftOpen); RightDoor.SetState(MSTSWagon.DoorRightOpen); Mirrors.SetState(MSTSWagon.MirrorOpen); UnloadingParts.SetState(MSTSWagon.UnloadingPartsOpen); InitializeUserInputCommands(); }
private void UpdateAnimation(RenderFrame frame, ElapsedTime elapsedTime) { float distanceTravelledM; if (MSTSWagon.IsDriveable && MSTSWagon.Simulator.UseAdvancedAdhesion) { //TODO: next code line has been modified to flip trainset physics in order to get viewing direction coincident with loco direction when using rear cab. // To achieve the same result with other means, without flipping trainset physics, the line should be changed as follows: // distanceTravelledM = MSTSWagon.WheelSpeedMpS * elapsedTime.ClockSeconds; distanceTravelledM = ((MSTSWagon.Train != null && MSTSWagon.Train.IsPlayerDriven && ((MSTSLocomotive)MSTSWagon).UsingRearCab) ? -1 : 1) * MSTSWagon.WheelSpeedMpS * elapsedTime.ClockSeconds; } else { distanceTravelledM = MSTSWagon.SpeedMpS * elapsedTime.ClockSeconds; } // Running gear animation if (!RunningGear.Empty() && MSTSWagon.DriverWheelRadiusM > 0.001) { RunningGear.UpdateLoop(distanceTravelledM / MathHelper.TwoPi / MSTSWagon.DriverWheelRadiusM); } // Wheel animation if (WheelPartIndexes.Count > 0) { var wheelCircumferenceM = MathHelper.TwoPi * MSTSWagon.WheelRadiusM; var rotationalDistanceR = MathHelper.TwoPi * distanceTravelledM / wheelCircumferenceM; // in radians WheelRotationR = MathHelper.WrapAngle(WheelRotationR - rotationalDistanceR); var wheelRotationMatrix = Matrix.CreateRotationX(WheelRotationR); foreach (var iMatrix in WheelPartIndexes) { TrainCarShape.XNAMatrices[iMatrix] = wheelRotationMatrix * TrainCarShape.SharedShape.Matrices[iMatrix]; } } // truck angle animation foreach (var p in Car.Parts) { if (p.iMatrix <= 0) { continue; } Matrix m = Matrix.Identity; m.Translation = TrainCarShape.SharedShape.Matrices[p.iMatrix].Translation; m.M11 = p.Cos; m.M13 = p.Sin; m.M31 = -p.Sin; m.M33 = p.Cos; // To cancel out any vibration, apply the inverse here. If no vibration is present, this matrix will be Matrix.Identity. TrainCarShape.XNAMatrices[p.iMatrix] = Car.VibrationInverseMatrix * m; } if (FreightShape != null) { FreightShape.Location.XNAMatrix = Car.WorldPosition.XNAMatrix; FreightShape.Location.TileX = Car.WorldPosition.TileX; FreightShape.Location.TileZ = Car.WorldPosition.TileZ; if (MSTSWagon.WagonType == TrainCar.WagonTypes.Tender) { if (MSTSWagon.TendersSteamLocomotive == null) { MSTSWagon.FindTendersSteamLocomotive(); } if (FreightShape.XNAMatrices.Length > 0 && MSTSWagon.TendersSteamLocomotive != null) { if (MSTSWagon.FreightAnimFlag > 0 && MSTSWagon.FreightAnimMaxLevelM > MSTSWagon.FreightAnimMinLevelM) { FreightShape.XNAMatrices[0].M42 = MSTSWagon.FreightAnimMinLevelM + MSTSWagon.TendersSteamLocomotive.FuelController.CurrentValue * (MSTSWagon.FreightAnimMaxLevelM - MSTSWagon.FreightAnimMinLevelM); } else // reproducing MSTS strange behavior; used to display loco crew { FreightShape.Location.XNAMatrix.M42 += MSTSWagon.FreightAnimMaxLevelM; } } } FreightShape.PrepareFrame(frame, elapsedTime); } if (FreightAnimations != null) { foreach (var freightAnim in FreightAnimations.Animations) { if (freightAnim.FreightShape != null && !((freightAnim.Animation is FreightAnimationContinuous) && (freightAnim.Animation as FreightAnimationContinuous).LoadPerCent == 0)) { freightAnim.FreightShape.Location.XNAMatrix = Car.WorldPosition.XNAMatrix; freightAnim.FreightShape.Location.TileX = Car.WorldPosition.TileX; freightAnim.FreightShape.Location.TileZ = Car.WorldPosition.TileZ; if (freightAnim.FreightShape.XNAMatrices.Length > 0) { if (freightAnim.Animation is FreightAnimationContinuous) { var continuousFreightAnim = freightAnim.Animation as FreightAnimationContinuous; if (MSTSWagon.FreightAnimations.IsGondola) { freightAnim.FreightShape.XNAMatrices[0] = TrainCarShape.XNAMatrices[1]; } freightAnim.FreightShape.XNAMatrices[0].M42 = continuousFreightAnim.MinHeight + continuousFreightAnim.LoadPerCent / 100 * (continuousFreightAnim.MaxHeight - continuousFreightAnim.MinHeight); } if (freightAnim.Animation is FreightAnimationStatic) { var staticFreightAnim = freightAnim.Animation as FreightAnimationStatic; freightAnim.FreightShape.XNAMatrices[0].M41 = staticFreightAnim.XOffset; freightAnim.FreightShape.XNAMatrices[0].M42 = staticFreightAnim.YOffset; freightAnim.FreightShape.XNAMatrices[0].M43 = staticFreightAnim.ZOffset; } } // Forcing rotation of freight shape freightAnim.FreightShape.PrepareFrame(frame, elapsedTime); } } } // Control visibility of passenger cabin when inside it if (Viewer.Camera.AttachedCar == this.MSTSWagon && //( Viewer.ViewPoint == Viewer.ViewPoints.Cab || // TODO, restore when we complete cab views - Viewer.Camera.Style == Camera.Styles.Passenger) { // We are in the passenger cabin if (InteriorShape != null) { InteriorShape.PrepareFrame(frame, elapsedTime); } else { TrainCarShape.PrepareFrame(frame, elapsedTime); } } else { // Skip drawing if CAB view - draw 2D view instead - by GeorgeS if (Viewer.Camera.AttachedCar == this.MSTSWagon && Viewer.Camera.Style == Camera.Styles.Cab) { return; } // We are outside the passenger cabin TrainCarShape.PrepareFrame(frame, elapsedTime); } }