@@ -972,12 +972,6 @@ public virtual void Update(float elapsedClockSeconds)
972972
973973 CarOutsideTempC = InitialCarOutsideTempC - TemperatureHeightVariationDegC ;
974974
975- // gravity force, M32 is up component of forward vector
976- // Percent slope = rise / run -> the Y position of the forward vector gives us the 'rise'
977- // Derive the 'run' by assuming a hypotenuse length of 1, so run = sqrt(1 - rise^2)
978- float rise = WorldPosition . XNAMatrix . M32 ;
979- GravityForceN = MassKG * GravitationalAccelerationMpS2 * rise ;
980- CurrentElevationPercent = 100f * ( rise / ( float ) Math . Sqrt ( 1 - rise * rise ) ) ;
981975 AbsSpeedMpS = Math . Abs ( _SpeedMpS ) ;
982976
983977 //TODO: next if block has been inserted to flip trainset physics in order to get viewing direction coincident with loco direction when using rear cab.
@@ -2172,7 +2166,7 @@ public virtual void Save(BinaryWriter outf)
21722166 outf . Write ( CouplerSlackM ) ;
21732167 outf . Write ( Headlight ) ;
21742168 outf . Write ( OrgConsist ) ;
2175- outf . Write ( PrevTiltingZRot ) ;
2169+ outf . Write ( TiltingZRot ) ;
21762170 outf . Write ( BrakesStuck ) ;
21772171 outf . Write ( IsCarHeatingInitialized ) ;
21782172 outf . Write ( SteamHoseLeakRateRandom ) ;
@@ -2195,7 +2189,7 @@ public virtual void Restore(BinaryReader inf)
21952189 CouplerSlackM = inf . ReadSingle ( ) ;
21962190 Headlight = inf . ReadInt32 ( ) ;
21972191 OrgConsist = inf . ReadString ( ) ;
2198- PrevTiltingZRot = inf . ReadSingle ( ) ;
2192+ TiltingZRot = inf . ReadSingle ( ) ;
21992193 BrakesStuck = inf . ReadBoolean ( ) ;
22002194 IsCarHeatingInitialized = inf . ReadBoolean ( ) ;
22012195 SteamHoseLeakRateRandom = inf . ReadSingle ( ) ;
@@ -2758,6 +2752,8 @@ public void ComputePosition(Traveller traveler, bool backToFront, float elapsedT
27582752
27592753 UpdateCurvePhys ( new Traveller ( traveler ) , BogieZOffsets ) ;
27602754
2755+ int direction = traveler . Direction == Traveller . TravellerDirection . Forward ? - 1 : 1 ;
2756+
27612757 if ( Flipped == backToFront )
27622758 {
27632759 float o = - CarLengthM / 2 - CentreOfGravityM . Z ;
@@ -2773,13 +2769,12 @@ public void ComputePosition(Traveller traveler, bool backToFront, float elapsedT
27732769 location . X += 2048 * ( traveler . TileX - tileX ) ;
27742770 location . Z += 2048 * ( traveler . TileZ - tileZ ) ;
27752771
2776- // This car is flipped, so flip roll direction.
2777- r *= - 1 ;
2778-
2779- WheelAxles [ k ] . Part . AddWheelSetLocation ( 1 , o , location , r ) ;
2772+ // This car is flipped, so flip roll direction in part
2773+ WheelAxles [ k ] . Part . AddWheelSetLocation ( 1 , o , location , - r ) ;
27802774 }
27812775 o = CarLengthM / 2 - CentreOfGravityM . Z - o ;
27822776 traveler . Move ( o ) ;
2777+ direction *= - 1 ;
27832778 }
27842779 else
27852780 {
@@ -2823,7 +2818,7 @@ public void ComputePosition(Traveller traveler, bool backToFront, float elapsedT
28232818 p . FindCenterLine ( ) ;
28242819 }
28252820 }
2826- // Determine facing direction of train car
2821+ // Determine facing direction and position of train car
28272822 p0 . FindCenterLine ( ) ;
28282823 Vector3 fwd = new Vector3 ( p0 . Dir [ 0 ] , p0 . Dir [ 1 ] , - p0 . Dir [ 2 ] ) ;
28292824 // Check if null (0-length) vector
@@ -2839,16 +2834,23 @@ public void ComputePosition(Traveller traveler, bool backToFront, float elapsedT
28392834 m . Up = up ;
28402835 m . Backward = fwd ;
28412836
2837+ // Update gravity force when position is updated, but before any secondary motion is added
2838+ GravityForceN = MassKG * GravitationalAccelerationMpS2 * fwd . Y ;
2839+ CurrentElevationPercent = 100f * ( fwd . Y / ( float ) Math . Sqrt ( 1 - fwd . Y * fwd . Y ) ) ;
2840+
2841+ // Consider body roll from superelevation and from tilting.
2842+ UpdateTilting ( traveler , elapsedTimeS , speed , direction ) ;
28422843 Matrix rollMat = Matrix . Identity ;
2844+ float rollAngle = p0 . Roll + TiltingZRot ;
28432845
2844- if ( p0 . Roll != 0.0f )
2846+ if ( rollAngle != 0.0f )
28452847 {
28462848 // For correct bogie positioning, need to offset rotation axis
28472849 Vector3 offset = new Vector3 ( 0.0f , BogiePivotHeightM , 0.0f ) ;
28482850
28492851 // Roll the car for superelevation about the offset axis of rotation
28502852 rollMat . Translation -= offset ;
2851- rollMat *= Matrix . CreateRotationZ ( p0 . Roll ) ;
2853+ rollMat *= Matrix . CreateRotationZ ( rollAngle ) ;
28522854 rollMat . Translation += offset ;
28532855
28542856 m = rollMat * m ;
@@ -2863,19 +2865,36 @@ public void ComputePosition(Traveller traveler, bool backToFront, float elapsedT
28632865 WorldPosition . TileX = tileX ;
28642866 WorldPosition . TileZ = tileZ ;
28652867
2866- UpdatedTraveler ( traveler , elapsedTimeS , distance , speed ) ;
2868+ UpdateVibration ( traveler , elapsedTimeS , distance , speed ) ;
28672869 }
28682870
28692871 #region Traveller-based updates
28702872 public float CurrentCurveRadiusM ;
28712873
2872- internal void UpdatedTraveler ( Traveller traveler , float elapsedTimeS , float distanceM , float speedMpS )
2874+ internal void UpdateTilting ( Traveller traveller , float elapsedTimeS , float speedMpS , int direction )
28732875 {
2874- // We need to avoid introducing any unbounded effects, so cap the elapsed time to 0.25 seconds (4FPS).
2875- if ( elapsedTimeS > 0.25f )
2876+ // If not a tilting train, skip processing this
2877+ // Future: Rework tilting to be per train car, instead of per consist
2878+ if ( ! Train . IsTilting )
28762879 return ;
28772880
2878- UpdateVibrationAndTilting ( traveler , elapsedTimeS , distanceM , speedMpS ) ;
2881+ float tiltDemand = 0.0f ;
2882+
2883+ // No tilt needed if going too slow, or not on a curve
2884+ if ( speedMpS > MinTiltSpeedMpS && CurrentCurveRadiusM != 0.0f )
2885+ {
2886+ // Compare actual superelevation to ideal, tilt makes up the difference
2887+ // Sine of superelevation angle is v^2 / (g * r)
2888+ float idealElevAngle = ( speedMpS * speedMpS ) / ( GravitationalAccelerationMpS2 * CurrentCurveRadiusM ) ;
2889+ idealElevAngle = ( float ) Math . Asin ( Math . Min ( idealElevAngle , 0.99f ) ) ;
2890+
2891+ tiltDemand = MathHelper . Clamp ( idealElevAngle - SuperElevationAngleRad , 0.0f , MaxTiltAngleRad ) ;
2892+
2893+ tiltDemand *= direction * traveller . CurveDirection ;
2894+ }
2895+
2896+ // Smooth rotation
2897+ TiltingZRot += ( tiltDemand - TiltingZRot ) * elapsedTimeS ;
28792898 }
28802899 #endregion
28812900
@@ -2953,16 +2972,21 @@ public void UpdateCurvePhys(Traveller traveller, float[] offsets)
29532972 int VibrationTrackVectorSection ;
29542973 float VibrationTrackCurvaturepM ;
29552974
2956- float PrevTiltingZRot ; // previous tilting angle
2957- float TiltingZRot ; // actual tilting angle
2975+ float TiltingZRot ; // Actual tilting angle
2976+ float MinTiltSpeedMpS = MpS . FromKpH ( 50.0f ) ; // Minimum speed for tilting to be activated
2977+ float MaxTiltAngleRad = MathHelper . ToRadians ( 8.0f ) ; // Maximum angle of tilting allowed
29582978
2959- internal void UpdateVibrationAndTilting ( Traveller traveler , float elapsedTimeS , float distanceM , float speedMpS )
2979+ internal void UpdateVibration ( Traveller traveler , float elapsedTimeS , float distanceM , float speedMpS )
29602980 {
2981+ // We need to avoid introducing any unbounded effects, so cap the elapsed time to 0.25 seconds (4FPS).
2982+ if ( elapsedTimeS > 0.25f )
2983+ return ;
29612984 // NOTE: Traveller is at the FRONT of the TrainCar!
29622985
29632986 // Don't add vibrations to train cars less than 2.5 meter in length; they're unsuitable for these calculations.
29642987 // Don't let vibrate car before EOT to avoid EOT not moving together with that car
2965- if ( CarLengthM < 2.5f || Train . EOT != null && Train . Cars . Count > 1 && Train . Cars [ Train . Cars . Count - 2 ] == this ) return ;
2988+ if ( CarLengthM < 2.5f || Train . EOT != null && Train . Cars . Count > 1 && Train . Cars [ Train . Cars . Count - 2 ] == this )
2989+ return ;
29662990 if ( Simulator . Settings . CarVibratingLevel != 0 )
29672991 {
29682992
@@ -3031,18 +3055,9 @@ internal void UpdateVibrationAndTilting(Traveller traveler, float elapsedTimeS,
30313055 AddVibrations ( VibrationFactorTrackNode ) ;
30323056 VibrationTrackNode = traveler . TrackNodeIndex ;
30333057 }
3034- }
3035- if ( Train != null && Train . IsTilting )
3036- {
3037- TiltingZRot = traveler . FindTiltedZ ( speedMpS ) ; //rotation if tilted, an indication of centrifugal force
3038- TiltingZRot = PrevTiltingZRot + ( TiltingZRot - PrevTiltingZRot ) * elapsedTimeS ; //smooth rotation
3039- PrevTiltingZRot = TiltingZRot ;
3040- if ( this . Flipped ) TiltingZRot *= - 1f ;
3041- }
3042- if ( Simulator . Settings . CarVibratingLevel != 0 || Train . IsTilting )
3043- {
3044- var rotation = Matrix . CreateFromYawPitchRoll ( VibrationRotationRad . Y , VibrationRotationRad . X , VibrationRotationRad . Z + TiltingZRot ) ;
3045- var translation = Matrix . CreateTranslation ( VibrationTranslationM . X , VibrationTranslationM . Y , 0 ) ;
3058+
3059+ Matrix rotation = Matrix . CreateFromYawPitchRoll ( VibrationRotationRad . Y , VibrationRotationRad . X , VibrationRotationRad . Z ) ;
3060+ Matrix translation = Matrix . CreateTranslation ( VibrationTranslationM . X , VibrationTranslationM . Y , 0 ) ;
30463061 WorldPosition . XNAMatrix = rotation * translation * WorldPosition . XNAMatrix ;
30473062 VibrationInverseMatrix = Matrix . Invert ( rotation * translation ) ;
30483063 }
0 commit comments