Skip to content

Commit 6b62c2a

Browse files
authored
Merge pull request #1121 from SteelFill/manual_articulate
Manually Override Articulation
2 parents 5ad5bc1 + de2a45d commit 6b62c2a

File tree

3 files changed

+61
-16
lines changed

3 files changed

+61
-16
lines changed

Source/Documentation/Manual/features-rollingstock.rst

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -403,6 +403,45 @@ OR supports tilting trains. A train tilts when its .con file name contains the
403403

404404
.. image:: images/features-tilting.png
405405

406+
Features to assist content creation
407+
===================================
408+
409+
OR now includes some features that don't change the functionality of rolling stock, but simplify
410+
some steps of the content creation process or allow more control over content than was previously
411+
possible. The goal of these features is to save content creators' time, give additional power to
412+
creators, and to simplify the installation process for end users.
413+
414+
Advanced articulation control
415+
-----------------------------
416+
417+
A wide variety of modern rolling stock uses articulation, in which multiple rail vehicles
418+
share a single "Jacobs Bogie". Open Rails offers partial support for such passenger and
419+
freight units by allowing one wagon to include a bogie in its 3D model while the next
420+
wagon removes the bogie from its 3D model. Ideally, OR will then add an invisible bogie
421+
to the end of the wagon without the bogie to emulate "sharing" the bogie with the previous
422+
wagon.
423+
424+
However, this automatic system is limited. OR will check for wheels in the wagon's 3D
425+
model and will assume the wagon is articulated at one end if there are no wheels towards
426+
that end of the 3D model. This approach will only be used on 3D models with 3, 2, or 0 axles
427+
(the 1-axle case is excluded for compatibility reasons) and won't be used on locomotives.
428+
In some cases, this approach will result in false negative or false positive detection
429+
of articulation. Should the automatic articulation method not produce the expected track
430+
following behavior, it is now possible to manually define whether a wagon or engine
431+
should use the articulation behavior.
432+
433+
.. index::
434+
single: ORTSFrontArticulation
435+
single: ORTSRearArticulation
436+
437+
To forcibly enable the articulation behavior at the front of the rail vehicle, use
438+
``ORTSFrontArticulation ( 1 )`` and at the rear use ``ORTSRearArticulation ( 1 )``.
439+
Conversely, use ``ORTSFrontArticulation ( 0 )`` or ``ORTSRearArticulation ( 0 )`` to
440+
force disable articulation behavior. Articulation should generally be enabled on the
441+
'floating' end(s) of a vehicle, where a bogie or wheels are not present in the 3D
442+
model, and disabled on the end(s) that have wheels. Entering a value of -1 provides
443+
the default (automatic) behavior.
444+
406445
Freight animations and pickups
407446
==============================
408447

Source/Orts.Simulation/Simulation/RollingStocks/MSTSWagon.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1278,6 +1278,8 @@ public virtual void Parse(string lowercasetoken, STFReader stf)
12781278
CarLengthM = stf.ReadFloat(STFReader.UNITS.Distance, null);
12791279
stf.SkipRestOfBlock();
12801280
break;
1281+
case "wagon(ortsfrontarticulation": FrontArticulation = stf.ReadIntBlock(null); break;
1282+
case "wagon(ortsreararticulation": RearArticulation = stf.ReadIntBlock(null); break;
12811283
case "wagon(ortslengthbogiecentre": CarBogieCentreLengthM = stf.ReadFloatBlock(STFReader.UNITS.Distance, null); break;
12821284
case "wagon(ortslengthcarbody": CarBodyLengthM = stf.ReadFloatBlock(STFReader.UNITS.Distance, null); break;
12831285
case "wagon(ortslengthairhose": CarAirHoseLengthM = stf.ReadFloatBlock(STFReader.UNITS.Distance, null); break;

Source/Orts.Simulation/Simulation/RollingStocks/TrainCar.cs

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,8 @@ public static Interpolator SteamHeatBoilerFuelUsageGalukpH()
186186
public float CarWidthM = 2.5f;
187187
public float CarLengthM = 40; // derived classes must overwrite these defaults
188188
public float CarHeightM = 4; // derived classes must overwrite these defaults
189+
public int FrontArticulation = -1; // -1: Determine front articulation automatically, 0: Force no front articulation, 1: Force front articulation
190+
public int RearArticulation = -1; // -1: Determine rear articulation automatically, 0: Force no rear articulation, 1: Force rear articulation
189191
public float MassKG = 10000; // Mass in KG at runtime; coincides with InitialMassKG if there is no load and no ORTS freight anim
190192
public float InitialMassKG = 10000;
191193
public bool IsDriveable;
@@ -2908,34 +2910,36 @@ public void SetUpWheels()
29082910
// Decided to control what is sent to SetUpWheelsArticulation()by using
29092911
// WheelAxlesLoaded as a flag. This way, wagons that have to be processed are included
29102912
// and the rest left out.
2911-
bool articulatedFront = !WheelAxles.Any(a => a.OffsetM.Z < 0);
2912-
bool articulatedRear = !WheelAxles.Any(a => a.OffsetM.Z > 0);
2913-
var carIndex = Train.Cars.IndexOf(this);
2914-
//Certain locomotives are testing as articulated wagons for some reason.
2915-
if (WagonType != WagonTypes.Engine)
2916-
if (WheelAxles.Count != 1 && (articulatedFront || articulatedRear))
2917-
{
2918-
WheelAxlesLoaded = true;
2919-
SetUpWheelsArticulation(carIndex);
2920-
}
2913+
2914+
// Force articulation if stock is configured as such
2915+
// Otherwise, use default behavior which gives articulation if there are no axles forward/reareward on the model,
2916+
// disables articulation on engines, and only allows articulation with 3 or fewer axles, but not 1 axle
2917+
bool articulatedFront = (FrontArticulation == 1 ||
2918+
(FrontArticulation == -1 && !WheelAxles.Any(a => a.OffsetM.Z < 0) && WagonType != WagonTypes.Engine && WheelAxles.Count != 1 && WheelAxles.Count <= 3));
2919+
bool articulatedRear = (RearArticulation == 1 ||
2920+
(RearArticulation == -1 && !WheelAxles.Any(a => a.OffsetM.Z > 0) && WagonType != WagonTypes.Engine && WheelAxles.Count != 1 && WheelAxles.Count <= 3));
2921+
2922+
if (articulatedFront || articulatedRear)
2923+
{
2924+
WheelAxlesLoaded = true;
2925+
SetUpWheelsArticulation(articulatedFront, articulatedRear);
2926+
}
29212927
} // end SetUpWheels()
29222928

2923-
protected void SetUpWheelsArticulation(int carIndex)
2929+
protected void SetUpWheelsArticulation(bool front, bool rear)
29242930
{
29252931
// If there are no forward wheels, this car is articulated (joined
29262932
// to the car in front) at the front. Likewise for the rear.
2927-
bool articulatedFront = !WheelAxles.Any(a => a.OffsetM.Z < 0);
2928-
bool articulatedRear = !WheelAxles.Any(a => a.OffsetM.Z > 0);
29292933
// Original process originally used caused too many issues.
29302934
// The original process did include the below process of just using WheelAxles.Add
29312935
// if the initial test did not work. Since the below process is working without issues the
29322936
// original process was stripped down to what is below
2933-
if (articulatedFront || articulatedRear)
2937+
if (front || rear)
29342938
{
2935-
if (articulatedFront && WheelAxles.Count <= 3)
2939+
if (front)
29362940
WheelAxles.Add(new WheelAxle(new Vector3(0.0f, BogiePivotHeightM, -CarLengthM / 2.0f), 0, 0) { Part = Parts[0] });
29372941

2938-
if (articulatedRear && WheelAxles.Count <= 3)
2942+
if (rear)
29392943
WheelAxles.Add(new WheelAxle(new Vector3(0.0f, BogiePivotHeightM, CarLengthM / 2.0f), 0, 0) { Part = Parts[0] });
29402944

29412945
WheelAxles.Sort(WheelAxles[0]);

0 commit comments

Comments
 (0)