Skip to content

Commit 56abfa2

Browse files
committed
Bugfixes and support for brake rigging animations
1 parent 939e17a commit 56abfa2

File tree

8 files changed

+114
-4
lines changed

8 files changed

+114
-4
lines changed

Source/Documentation/Manual/features-rollingstock.rst

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1487,6 +1487,53 @@ to the oscillation from center point to an oscillation end point. The file shoul
14871487
one cue point at its beginning and one after the time interval of a complete bell swing
14881488
forward and backward, and should have a final fadeoff for best result.
14891489

1490+
Brake Equipment Animations
1491+
==========================
1492+
1493+
Open Rails now supports animation of brake rigging components driven by the brake system
1494+
simulation.
1495+
1496+
Brake Cylinder Animation
1497+
------------------------
1498+
1499+
On engines and wagons with :ref:`advanced brake cylinder parameters <physics-braking-parameters>`
1500+
`ORTSBrakeCylinderDiameter` and `ORTSBrakeCylinderPistonTravel` defined, Open Rails will
1501+
simulate the motion of the brake cylinders and brake rigging. This simulation can be used
1502+
to drive animations of brake cylinders using animation matricies with names that
1503+
start with `ORTSBRAKECYLINDER`. This animation type should NOT be used for any brake equipment
1504+
that can be actuated by the brake cylinder and the handbrakes. See the section on brake rigging
1505+
animation for details.
1506+
1507+
Unlike other animation types, the keyframes for brake cylinders do not represent time.
1508+
Instead, the key value represents the level of brake cylinder extension. A value of 0.8
1509+
represents the state where the brake cylinder has taken up the slack in the brake rigging,
1510+
1.0 represents the level of brake cylinder extension at 50 psi/3.5 bar, and the maximum
1511+
value of cylinder extension is 1.6, which should not be possible in normal operation.
1512+
These values are the same regardless of the settings used in the engine or wagon file.
1513+
1514+
Note that the advanced brake cylinder calculations are only run on air brake systems on the
1515+
player train to save computing power. As such, brake cylinder animations on AI trains
1516+
or brake systems other than air brakes behave in a simplified manner.
1517+
1518+
Handbrake Animation
1519+
-------------------
1520+
1521+
Handbrake wheels and levers can also be animated using the same process as two-state animations
1522+
such as mirrors. The matrix name for animated handbrakes must begin with `ORTSHANDBRAKE`.
1523+
1524+
Brake Rigging and Brake Shoe Animation
1525+
--------------------------------------
1526+
1527+
For any brake equipment that is actuated by both the handbrake and the brake cylinders (typically,
1528+
this includes brake rigging and brake shoes, but sometimes also includes brake cylinders themselves),
1529+
use animations with a matrix name starting with `ORTSBRAKERIGGING`. For this type of animation, the
1530+
animation state will respond to the brake cylinder travel or the handbrake, whichever input is greater.
1531+
1532+
The same keyframe rules as brake cylinder animations apply here. The 0.8 key should represent the state
1533+
where the brake shoes have made contact with the friction surface, and no further motion of brake shoes
1534+
should occur after 0.8. Applying the handbrake will drive the animation to the 1.0 state, the same
1535+
as a 50 psi/3.5 bar application of the brakes.
1536+
14901537
Coupler and Airhose Animation
14911538
=============================
14921539

Source/Documentation/Manual/physics.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3143,6 +3143,8 @@ notch of the train brake controller, where 0 means no dynamic brake and 1 means
31433143
Native Open Rails Braking Parameters
31443144
------------------------------------
31453145

3146+
.. _physics-braking-parameters:
3147+
31463148
Open Rails has implemented additional specific braking parameters to
31473149
deliver realism in braking performance in the simulation.
31483150

Source/Orts.Simulation/Simulation/RollingStocks/SubSystems/Brakes/BrakeSystem.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ public abstract class BrakeSystem
7676
public abstract float GetCylPressurePSI();
7777
public abstract float GetCylVolumeM3();
7878
public abstract float GetTotalCylVolumeM3();
79+
public abstract float GetNormalizedCylTravel();
7980
public abstract float GetVacResPressurePSI();
8081
public abstract float GetVacResVolume();
8182
public abstract float GetVacBrakeCylNumber();

Source/Orts.Simulation/Simulation/RollingStocks/SubSystems/Brakes/MSTS/AirSinglePipe.cs

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,11 @@ public override float GetTotalCylVolumeM3()
300300
return TotalCylVolumeM3;
301301
}
302302

303+
public override float GetNormalizedCylTravel()
304+
{
305+
return CurrentCylTravelM / CylStrokeM;
306+
}
307+
303308
public float GetFullServPressurePSI()
304309
{
305310
return FullServPressurePSI;
@@ -653,8 +658,8 @@ public override void Initialize()
653658
// Other assumptions:
654659
// Brake cylinder travel remains at 0 until reaching half the spring counter-pressure
655660
// Brake cylinder travel is linear w.r.t. total air in the cylinder line, not entirely accurate but greatly simplifies code
656-
float[] airPV = new float[5];
657-
float[] cylTravel = new float[5];
661+
float[] airPV = new float[7];
662+
float[] cylTravel = new float[7];
658663

659664
float nomPSI = 50.0f;
660665

@@ -683,6 +688,12 @@ public override void Initialize()
683688

684689
cylTravel[4] = (strokePerPsi * (Math.Max(nomPSI, ReferencePressurePSI) - 50.0f)) + CylStrokeM;
685690
airPV[4] = AdvancedBrakeCylinderAir(Math.Max(nomPSI, ReferencePressurePSI), true);
691+
// Absolute maximum cylinder travel limited to 160% of nominal
692+
cylTravel[5] = CylStrokeM * 1.6f;
693+
airPV[5] = AdvancedBrakeCylinderAir((cylTravel[5] - cylTravel[2]) / strokePerPsi);
694+
695+
cylTravel[6] = cylTravel[5];
696+
airPV[6] = airPV[5] * 2.0f;
686697

687698
CylTravelTab = new Interpolator(airPV, cylTravel);
688699

@@ -811,7 +822,6 @@ public float CalculateBrakeCylinderPressure(ref float airPV, float dp, float tar
811822
}
812823
else if (dp < 0)
813824
{
814-
recalculate = true;
815825
if (pressurePSI < 0)
816826
{
817827
currentAir = 0;
@@ -1407,6 +1417,8 @@ public override void Update(float elapsedClockSeconds)
14071417
dp = QuickServiceLimitPSI - AutoCylPressurePSI;
14081418
if (QuickServiceBulbPressurePSI - (dp * volumeRatio) < AutoCylPressurePSI + dp)
14091419
dp = (QuickServiceBulbPressurePSI - AutoCylPressurePSI) / (1 + volumeRatio);
1420+
if (dp < 0)
1421+
dp = 0;
14101422
if (CylSource == 0)
14111423
AutoCylPressurePSI = CalculateBrakeCylinderPressure(ref AutoCylAirPV, dp, QuickServiceLimitPSI);
14121424
else
@@ -1737,7 +1749,7 @@ public override void Update(float elapsedClockSeconds)
17371749
if (CylDiameterM > 0 && Car.Train.IsPlayerDriven)
17381750
CurrentCylTravelM = CylTravelTab[CylAirPV];
17391751
else
1740-
CurrentCylTravelM = 0;
1752+
CurrentCylTravelM = CylPressurePSI > BrakeCylinderSpringPressurePSI ? CylStrokeM : 0.0f;
17411753

17421754
// During braking wheelslide control is effected throughout the train by additional equipment on each vehicle. In the piping to each pair of brake cylinders are fitted electrically operated
17431755
// dump valves. When axle rotations which are sensed electrically, differ by a predetermined speed the dump valves are operated releasing brake cylinder pressure to both axles of the affected

Source/Orts.Simulation/Simulation/RollingStocks/SubSystems/Brakes/MSTS/ManualBraking.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -356,6 +356,11 @@ public override float GetTotalCylVolumeM3()
356356
return 0;
357357
}
358358

359+
public override float GetNormalizedCylTravel()
360+
{
361+
return ManualBrakingCurrentFraction;
362+
}
363+
359364
public override float GetVacResVolume()
360365
{
361366
return 0;

Source/Orts.Simulation/Simulation/RollingStocks/SubSystems/Brakes/MSTS/VacuumSinglePipe.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,11 @@ public override float GetTotalCylVolumeM3()
216216
return TotalCylVolumeM3;
217217
}
218218

219+
public override float GetNormalizedCylTravel()
220+
{
221+
return CylPressurePSIA > VacResPressureAdjPSIA() ? 1.0f : 0.0f;
222+
}
223+
219224
public override float GetVacResVolume()
220225
{
221226
return VacResVolM3;

Source/RunActivity/Viewer3D/AnimatedPart.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,20 @@ public void SetFrameClamp(float frame)
103103
SetFrame(frame);
104104
}
105105

106+
/// <summary>
107+
/// Smoothly changes the animation to a particular frame whilst clamping it to the frame count range.
108+
/// </summary>
109+
public void UpdateFrameClamp(float frame, ElapsedTime elapsedTime)
110+
{
111+
float newState;
112+
if (Math.Abs(frame - AnimationKey) > 1.0f * elapsedTime.ClockSeconds)
113+
newState = AnimationKey + Math.Sign(frame - AnimationKey) * elapsedTime.ClockSeconds;
114+
else
115+
newState = frame;
116+
117+
SetFrameClamp(newState);
118+
}
119+
106120
/// <summary>
107121
/// Sets the animation to a particular frame whilst cycling back to the start as input goes beyond the last frame.
108122
/// </summary>

Source/RunActivity/Viewer3D/RollingStock/MSTSWagonViewer.cs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,9 @@ public class MSTSWagonViewer : TrainCarViewer
8080
protected AnimatedPart Item2Continuous;
8181
protected AnimatedPart Item1TwoState;
8282
protected AnimatedPart Item2TwoState;
83+
protected AnimatedPart BrakeCylinders;
84+
protected AnimatedPart Handbrakes;
85+
protected AnimatedPart BrakeRigging;
8386
AnimatedPart UnloadingParts;
8487

8588
public Dictionary<string, List<ParticleEmitterViewer>> ParticleDrawers = new Dictionary<string, List<ParticleEmitterViewer>>();
@@ -341,6 +344,9 @@ from data in effect.Value
341344
Item2Continuous = new AnimatedPart(TrainCarShape);
342345
Item1TwoState = new AnimatedPart(TrainCarShape);
343346
Item2TwoState = new AnimatedPart(TrainCarShape);
347+
BrakeCylinders = new AnimatedPart(TrainCarShape);
348+
Handbrakes = new AnimatedPart(TrainCarShape);
349+
BrakeRigging = new AnimatedPart(TrainCarShape);
344350

345351
if (car.FreightAnimations != null)
346352
FreightAnimations = new FreightAnimationsViewer(viewer, car, wagonFolderSlash);
@@ -431,6 +437,9 @@ from data in effect.Value
431437
RightWindowRear.SetState(MSTSWagon.WindowStates[MSTSWagon.RightWindowRearIndex] >= MSTSWagon.WindowState.Opening);
432438
Item1TwoState.SetState(MSTSWagon.GenericItem1);
433439
Item2TwoState.SetState(MSTSWagon.GenericItem2);
440+
BrakeCylinders.SetFrameClamp(MSTSWagon.BrakeSystem.GetNormalizedCylTravel());
441+
Handbrakes.SetState(MSTSWagon.GetTrainHandbrakeStatus());
442+
BrakeRigging.SetFrameClamp(Math.Max(MSTSWagon.BrakeSystem.GetNormalizedCylTravel(), MSTSWagon.GetTrainHandbrakeStatus() ? 1.0f : 0.0f));
434443
UnloadingParts.SetState(MSTSWagon.UnloadingPartsOpen);
435444

436445
InitializeUserInputCommands();
@@ -617,6 +626,18 @@ void MatchMatrixToPart(MSTSWagon car, int matrix, int bogieMatrix)
617626
{
618627
Item2TwoState.AddMatrix(matrix);
619628
}
629+
else if (matrixName.StartsWith("ORTSBRAKECYLINDER")) // brake cylinder animation
630+
{
631+
BrakeCylinders.AddMatrix(matrix);
632+
}
633+
else if (matrixName.StartsWith("ORTSHANDBRAKE")) // handbrake wheel animation
634+
{
635+
Handbrakes.AddMatrix(matrix);
636+
}
637+
else if (matrixName.StartsWith("ORTSBRAKERIGGING")) // brake rigging/brake shoes animation
638+
{
639+
BrakeRigging.AddMatrix(matrix);
640+
}
620641
else
621642
{
622643
if (matrixAnimated && matrix != 0)
@@ -669,6 +690,9 @@ public override void PrepareFrame(RenderFrame frame, ElapsedTime elapsedTime)
669690
UnloadingParts.UpdateState(MSTSWagon.UnloadingPartsOpen, elapsedTime);
670691
Item1TwoState.UpdateState(MSTSWagon.GenericItem1, elapsedTime);
671692
Item2TwoState.UpdateState(MSTSWagon.GenericItem2, elapsedTime);
693+
BrakeCylinders.UpdateFrameClamp(MSTSWagon.BrakeSystem.GetNormalizedCylTravel(), elapsedTime);
694+
Handbrakes.UpdateState(MSTSWagon.GetTrainHandbrakeStatus(), elapsedTime);
695+
BrakeRigging.UpdateFrameClamp(Math.Max(MSTSWagon.BrakeSystem.GetNormalizedCylTravel(), MSTSWagon.GetTrainHandbrakeStatus() ? 1.0f : 0.0f), elapsedTime);
672696
UpdateAnimation(frame, elapsedTime);
673697

674698
var car = Car as MSTSWagon;

0 commit comments

Comments
 (0)