Skip to content

Commit 766be51

Browse files
committed
Add object hiding to .sd file and to freight animations
1 parent a5d08b9 commit 766be51

File tree

10 files changed

+116
-12
lines changed

10 files changed

+116
-12
lines changed

Source/Documentation/Manual/features-rollingstock.rst

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -496,7 +496,7 @@ shape file and looking at the ``shader_names`` block near the top of the file. T
496496
top of the list has an index of 0, the next one down has an index of 1, and so on. The accepted shader names are
497497
``Tex`` (fullbright), ``TexDiff`` (diffuse lighting), ``BlendATex`` (fullbright with transparency), ``BlendATextDiff``
498498
(diffuse with transparency), ``AddATex`` (fullbright with x-ray transparency), ``AddATexDiff`` (diffuse with x-ray
499-
transparency), if an invalid shader name is given a warning will be added to the log.
499+
transparency), and all are case-sensitive. If an invalid shader name is given a warning will be added to the log.
500500

501501
For example, the US2BSignal2.s shape included with Marias Pass uses one shader, the ``TexDiff`` shader. This
502502
disallows transparency. If, for any reason, transparency were desired on this shape, the shape descriptor file
@@ -615,6 +615,56 @@ for them to appear in the intended locations. The location of a sub object is me
615615
so if the sub object parent is changed, it's position in the 3D world will change as well (unless corrected for,
616616
as was done here).
617617

618+
Hiding Sub Objects
619+
''''''''''''''''''
620+
621+
.. index::
622+
single: ESD_ORTSObjectVisibility
623+
624+
In some cases, it may be desired to simply disable rendering of some shape sub objects (for example,
625+
preventing low-poly objects from being rendered so they can be replaced with higher-poly freight
626+
animations). To achieve this, ``ESD_ORTSObjectVisibility'' may be added to to the Shape ( block of the
627+
shape descriptor file. ``ESD_ORTSObjectVisibility ( MATRIXNAME 0/1 )`` will take rendering of all
628+
sub objects controlled by the matrix called "MATRIXNAME" and make them visible if the second input is not 0,
629+
or invisible if the second input is 0. Note that 1 (the object is visible) is the default setting for all objects.
630+
If an object is set to be invisible, data for that object will not be sent to the GPU, saving render time,
631+
but any CPU calculations such as animations will still be applied to the sub object(s) and ``ORTSShapeHierarchy``
632+
can still be used to attach lights, freight animations, sounds, particles, etc to the object while invisible.
633+
Similarly, anything attached to a different matrix (regardless if that matrix is above or below the one made
634+
invisible) will remain visible and simulated unless specified otherwise.
635+
636+
As an example, we can hide all the (low poly) wheels of an old locomotive in order to replace the wheels with
637+
(high poly) freight animations using the ORTSShapeHierarchy feature of :ref:`ORTS freight animations<orts-freight-anims>`::
638+
639+
SIMISA@@@@@@@@@@JINX0t1t______
640+
641+
shape ( SF_FP45_93.s
642+
ESD_Detail_Level ( 0 )
643+
ESD_Software_DLev ( 2 )
644+
ESD_Alternative_Texture ( 0 )
645+
ESD_Bounding_Box ( -1.632 -0.095 -10.99 1.684 4.628 10.99 )
646+
647+
Comment ( Disable rendering, but not simulation, of all wheels. )
648+
ESD_ORTSObjectVisibility (
649+
WHEELS11 0
650+
WHEELS12 0
651+
WHEELS13 0
652+
WHEELS21 0
653+
WHEELS22 0
654+
WHEELS23 0
655+
)
656+
)
657+
658+
Note that, similar to other parameters, multiple objects can be hidden in one parameter by adding additional
659+
pairs of matrix names and 1/0 values. If a matrix name can't be found, the missing matrix will be skipped and
660+
a warning will be added to the log.
661+
662+
This feature can also be used by freight animations directly using the ``ReplaceObject`` parameter; an ORTS freight
663+
animation with this parameter will disable rendering of the original object to which it is attached whenever the
664+
freight animation is visible. When used in combination with the ``ShapeHierarchy`` ORTS freight animation parameter,
665+
the freight animation can be used to effectively replace specific components of the original model without editing
666+
the .sd file. Users are encouraged to experiment with whichever approach works best.
667+
618668
Transformation Matrix Name Changes
619669
''''''''''''''''''''''''''''''''''
620670

@@ -826,6 +876,8 @@ Other Vehicles:
826876
OR specific freight animations and pickups
827877
------------------------------------------
828878

879+
.. _orts-freight-anims:
880+
829881
General
830882
'''''''
831883

@@ -889,6 +941,7 @@ the first line of the include file must be blank.::
889941
MaxHeight ( 0.3 )
890942
MinHeight ( -2.0 )
891943
FreightWeightWhenFull ( 99t )
944+
ReplaceObject ( 0 )
892945
FullAtStart ( 0 )
893946
)
894947
FreightAnimContinuous (
@@ -902,6 +955,7 @@ the first line of the include file must be blank.::
902955
MaxHeight ( 0.3 )
903956
MinHeight ( -2.0 )
904957
FreightWeightWhenFull ( 99t )
958+
ReplaceObject ( 0 )
905959
FullAtStart ( 0 )
906960
)
907961
)
@@ -1009,6 +1063,12 @@ moment. The parameters of the subblock are described below:
10091063
- ``FreightWeightWhenFull`` defines the mass of the freight when the wagon is full;
10101064
the mass of the wagon is computed by adding the mass of the empty wagon to the
10111065
actual mass of the freight
1066+
- ``ReplaceObject`` if set to 1 (ignored if missing) will disable rendering of the wagon
1067+
sub object to which the freightanim is attached. This works best when combined with
1068+
``ShapeHierarchy`` to disable rendering of specific sub objects, effectively replacing
1069+
the sub object graphic with that of the freightanim. The intended use of this setting
1070+
is to "delete" original shape parts and replace them with higher quality shapes without
1071+
editing the original shape.
10121072
- ``FullAtStart`` defines wether the wagon is fully loaded ( 1 ) or is empty at game
10131073
start; if there are more continuous OR freightanims that have ``FullAtStart``
10141074
set to 1, only the first one is considered.
@@ -1095,6 +1155,7 @@ freightanims. The ``FreightAnimStatic`` subblock has the following format::
10951155
Flip ()
10961156
ShapeHierarchy ( MATRIXNAME )
10971157
Visibility ( "Outside, Cab2D, Cab3D" )
1158+
ReplaceObject ()
10981159
)
10991160
)
11001161

Source/Orts.Formats.Msts/ShapeDescriptorFile.cs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,18 @@ public SDShape(STFReader stf)
103103
new STFReader.TokenProcessor("esd_ortsmatrixtranslation", ()=>{ ParseMatrixOverride(STFReader.UNITS.Distance, stf, ref ESD_MatrixTranslation); }),
104104
new STFReader.TokenProcessor("esd_ortsmatrixscale", ()=>{ ParseMatrixOverride(STFReader.UNITS.None, stf, ref ESD_MatrixScale); }),
105105
new STFReader.TokenProcessor("esd_ortsmatrixrotation", ()=>{ ParseMatrixOverride(STFReader.UNITS.Angle, stf, ref ESD_MatrixRotation); }),
106+
new STFReader.TokenProcessor("esd_ortsobjectvisibility", ()=>{
107+
stf.MustMatch("(");
108+
// Allow for multiple pairs of replaced and replacement values
109+
while (!stf.EndOfBlock())
110+
{
111+
string matName = stf.ReadString();
112+
bool setting = stf.ReadInt(1) != 0;
113+
// Add pair of values so long as we haven't reached the end of block
114+
if (!string.IsNullOrEmpty(matName) && !ESD_ObjectVisibility.ContainsKey(matName))
115+
ESD_ObjectVisibility.Add(matName, setting);
116+
}
117+
}),
106118
new STFReader.TokenProcessor("esd_ortslodoverride", ()=>{
107119
stf.MustMatch("(");
108120
// Allow for multiple pairs of replaced and replacement values
@@ -128,6 +140,8 @@ public SDShape(STFReader stf)
128140
ESD_ModifiedMatrices.Add(mat);
129141
foreach (string mat in ESD_MatrixRotation.Keys)
130142
ESD_ModifiedMatrices.Add(mat);
143+
foreach (string mat in ESD_ObjectVisibility.Keys)
144+
ESD_ModifiedMatrices.Add(mat);
131145
}
132146
public int ESD_Detail_Level;
133147
public int ESD_Alternative_Texture;
@@ -153,6 +167,8 @@ public SDShape(STFReader stf)
153167
public Dictionary<string, Vector3> ESD_MatrixScale = new Dictionary<string, Vector3>();
154168
// Dictionary of <matrix name, matrix rotation x/y/z vector>
155169
public Dictionary<string, Vector3> ESD_MatrixRotation = new Dictionary<string, Vector3>();
170+
// Dictionary of <matrix name, true/false visibility>
171+
public Dictionary<string, bool> ESD_ObjectVisibility = new Dictionary<string, bool>();
156172
// Dictionary of <LOD index, LOD distance>
157173
public Dictionary<int, float> ESD_LODOverride = new Dictionary<int, float>();
158174

Source/Orts.Simulation/Simulation/RollingStocks/SubSystems/FreightAnimations.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -988,8 +988,9 @@ public enum VisibleFrom
988988
}
989989
public bool Flipped = false;
990990
public bool[] Visibility = { true, false, false };
991+
public bool ReplaceObject = false;
991992
public Vector3 Offset = new Vector3();
992-
public int ShapeIndex = -1; // TODO: Allow user inputs to specify ShapeIndex as per lights
993+
public int ShapeIndex = -1;
993994
public string ShapeHierarchy;
994995
}
995996

@@ -1054,6 +1055,7 @@ public FreightAnimationContinuous(STFReader stf, MSTSWagon wagon)
10541055
new STFReader.TokenProcessor("flip", ()=>{ Flipped = stf.ReadBoolBlock(true);}),
10551056
new STFReader.TokenProcessor("shapeindex", ()=>{ ShapeIndex = stf.ReadIntBlock(null);}),
10561057
new STFReader.TokenProcessor("shapehierarchy", ()=>{ ShapeHierarchy = stf.ReadStringBlock(null);}),
1058+
new STFReader.TokenProcessor("replaceobject", ()=>{ ReplaceObject = stf.ReadBoolBlock(true);}),
10571059
new STFReader.TokenProcessor("visibility", ()=>{
10581060
for (int index = 0; index < 3; index++)
10591061
Visibility[index] = false;
@@ -1183,6 +1185,7 @@ public FreightAnimationStatic(STFReader stf)
11831185
new STFReader.TokenProcessor("flip", ()=>{ Flipped = stf.ReadBoolBlock(true);}),
11841186
new STFReader.TokenProcessor("shapeindex", ()=>{ ShapeIndex = stf.ReadIntBlock(null);}),
11851187
new STFReader.TokenProcessor("shapehierarchy", ()=>{ ShapeHierarchy = stf.ReadStringBlock(null);}),
1188+
new STFReader.TokenProcessor("replaceobject", ()=>{ ReplaceObject = stf.ReadBoolBlock(true);}),
11861189
new STFReader.TokenProcessor("visibility", ()=>{
11871190
for (int index = 0; index < 3; index++)
11881191
Visibility[index] = false;

Source/RunActivity/Viewer3D/Lights.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ public LightViewer(Viewer viewer, TrainCar car, TrainCarViewer carViewer)
125125
}
126126
else
127127
{
128-
if (light.ShapeHierarchy != null)
128+
if (!string.IsNullOrEmpty(light.ShapeHierarchy))
129129
{
130130
if ((CarViewer as MSTSWagonViewer).TrainCarShape.SharedShape.MatrixNames.Contains(light.ShapeHierarchy))
131131
{

Source/RunActivity/Viewer3D/ParticleEmitter.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -258,7 +258,7 @@ public ParticleEmitterPrimitive(Viewer viewer, ParticleEmitterData data, MSTSWag
258258
}
259259
else
260260
{
261-
if (EmitterData.ShapeHierarchy != null)
261+
if (!string.IsNullOrEmpty(EmitterData.ShapeHierarchy))
262262
{
263263
if (CarViewer.TrainCarShape.SharedShape.MatrixNames.Contains(EmitterData.ShapeHierarchy))
264264
{

Source/RunActivity/Viewer3D/RollingStock/MSTSLocomotiveViewer.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1358,7 +1358,7 @@ public CabRenderer(Viewer viewer, MSTSLocomotiveViewer carViewer)
13581358
}
13591359
else
13601360
{
1361-
if (view.ShapeHierarchy != null)
1361+
if (!string.IsNullOrEmpty(view.ShapeHierarchy))
13621362
{
13631363
if (carViewer.TrainCarShape.SharedShape.MatrixNames.Contains(view.ShapeHierarchy))
13641364
{

Source/RunActivity/Viewer3D/RollingStock/MSTSWagonViewer.cs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -448,7 +448,7 @@ from data in effect.Value
448448
}
449449
else
450450
{
451-
if (view.ShapeHierarchy != null)
451+
if (!string.IsNullOrEmpty(view.ShapeHierarchy))
452452
{
453453
if (TrainCarShape.SharedShape.MatrixNames.Contains(view.ShapeHierarchy))
454454
{
@@ -739,13 +739,24 @@ public override void PrepareFrame(RenderFrame frame, ElapsedTime elapsedTime)
739739

740740
if (FreightAnimations != null)
741741
{
742-
foreach (var freightAnim in FreightAnimations.Animations)
742+
foreach (FreightAnimationViewer freightAnim in FreightAnimations.Animations)
743743
{
744744
if (freightAnim.FreightShape?.DontRender == false)
745745
{
746746
// Display ORTS freight animation shape if cargo is loaded and visibility settings allow it to show
747+
// Disable rendering of original shape part if applicable
748+
if (freightAnim.Animation.ReplaceObject && TrainCarShape.Visibility[freightAnim.Animation.ShapeIndex])
749+
TrainCarShape.Visibility[freightAnim.Animation.ShapeIndex] = false;
750+
747751
freightAnim.FreightShape.PrepareFrame(frame, elapsedTime);
748752
}
753+
else
754+
{
755+
// Allow original shape parts to render if applicable
756+
if (freightAnim.Animation.ReplaceObject && !TrainCarShape.Visibility[freightAnim.Animation.ShapeIndex] &&
757+
TrainCarShape.SharedShape.Visibility[freightAnim.Animation.ShapeIndex])
758+
TrainCarShape.Visibility[freightAnim.Animation.ShapeIndex] = true;
759+
}
749760
}
750761
}
751762

Source/RunActivity/Viewer3D/RollingStock/SubSystems/FreightAnimationsViewer.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ public FreightAnimationViewer(Viewer viewer, MSTSWagonViewer wagonViewer, string
9494
}
9595
else
9696
{
97-
if (Animation.ShapeHierarchy != null)
97+
if (!string.IsNullOrEmpty(Animation.ShapeHierarchy))
9898
{
9999
if (wagonViewer.TrainCarShape.SharedShape.MatrixNames.Contains(Animation.ShapeHierarchy))
100100
{

Source/RunActivity/Viewer3D/Shapes.cs

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -293,7 +293,8 @@ public class PoseableShape : StaticShape
293293

294294
public readonly int[] Hierarchy;
295295

296-
public bool DontRender; // Flag to be used by other processes to see if the shape should not be rendered
296+
public bool DontRender; // Flag to be used by other processes to see if the entire shape should not be rendered
297+
public bool[] Visibility; // Specific flags to see if individual matrices should be rendered (0: don't render, 1: do render)
297298

298299
public PoseableShape(Viewer viewer, string path, string descriptor, WorldPosition initialPosition, ShapeFlags flags)
299300
: base(viewer, path, descriptor, initialPosition, flags)
@@ -304,6 +305,7 @@ public PoseableShape(Viewer viewer, string path, string descriptor, WorldPositio
304305
ResultMatrices = new Matrix[SharedShape.Matrices.Length];
305306
for (int iMatrix = 0; iMatrix < SharedShape.Matrices.Length; ++iMatrix)
306307
XNAMatrices[iMatrix] = SharedShape.Matrices[iMatrix];
308+
Visibility = SharedShape.Visibility;
307309
}
308310
else // If the shape file is missing or fails to load, we need some default data to prevent crashes
309311
{
@@ -376,7 +378,7 @@ public void UpdateResultMatrices()
376378
{
377379
res = res * XNAMatrices[hIndex];
378380
// Prevent potential infinite loop due to faulty hierarchy definition
379-
if (hIndex != Hierarchy[hIndex])
381+
if (hIndex != Hierarchy[hIndex] && hIndex != Hierarchy[i])
380382
hIndex = Hierarchy[hIndex];
381383
else
382384
break;
@@ -392,7 +394,7 @@ public void UpdateResultMatrices()
392394

393395
public override void PrepareFrame(RenderFrame frame, ElapsedTime elapsedTime)
394396
{
395-
SharedShape.PrepareFrame(frame, Location, XNAMatrices, Flags);
397+
SharedShape.PrepareFrame(frame, Location, XNAMatrices, Flags, Visibility);
396398
}
397399

398400
public void ConditionallyPrepareFrame(RenderFrame frame, ElapsedTime elapsedTime, bool[] matrixVisible = null)
@@ -2057,6 +2059,7 @@ public class SharedShape : IDisposable
20572059
public List<string> MatrixNames = new List<string>();
20582060
public List<string> ImageNames; // Names of textures without paths or file extensions
20592061
public Matrix[] Matrices = new Matrix[0]; // the original natural pose for this shape - shared by all instances
2062+
public bool[] Visibility = new bool[0]; // one flag per matrix to indicate if that matrix should be rendered
20602063
public animations Animations;
20612064
public LodControl[] LodControls;
20622065
public bool HasNightSubObj;
@@ -2132,6 +2135,10 @@ void LoadContent()
21322135

21332136

21342137
var textureFlags = Helpers.TextureFlags.None;
2138+
Visibility = new bool[sFile.shape.matrices.Count];
2139+
for (int vis = 0; vis < Visibility.Length; vis++)
2140+
Visibility[vis] = true; // Assume all matrices should be rendered by default
2141+
21352142
ShapeDescriptorFile sdFile = null;
21362143
bool adjustMAIN = false; // Check to see if the 0th matrix is modified in any way
21372144
if (File.Exists(DescriptorPath))
@@ -2261,6 +2268,12 @@ void LoadContent()
22612268
}
22622269
}
22632270

2271+
// See if the objects associated with this shape should be hidden during rendering
2272+
if (sdFile.shape.ESD_ObjectVisibility.TryGetValue(matName, out bool visible))
2273+
{
2274+
Visibility[i] = visible;
2275+
}
2276+
22642277
found = true;
22652278
}
22662279
}

Source/RunActivity/Viewer3D/Sound.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -757,7 +757,7 @@ public void Initialize(Viewer viewer, WorldLocation worldLocation, Events.Source
757757
}
758758
else
759759
{
760-
if (mstsStream.ShapeHierarchy != null)
760+
if (!string.IsNullOrEmpty(mstsStream.ShapeHierarchy))
761761
{
762762
if (CarViewer.TrainCarShape.SharedShape.MatrixNames.Contains(mstsStream.ShapeHierarchy))
763763
{

0 commit comments

Comments
 (0)