Skip to content

Commit b7efe2e

Browse files
committed
Add level of detail override to shape descriptor, add error correction for LODs with a distance of 0 meters
1 parent 7f9047f commit b7efe2e

File tree

4 files changed

+112
-0
lines changed

4 files changed

+112
-0
lines changed

Source/Documentation/Manual/features-rollingstock.rst

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -529,6 +529,52 @@ matrix with the old name can be found, a warning will be produced and nothing wi
529529
that the matrix rename step occurs *after* the previously described matrix modifications, so
530530
the old matrix name must be used by any other .sd parameters that require a matrix name.
531531

532+
LOD Distance Override
533+
'''''''''''''''''''''
534+
535+
Shape files are generally products of the time they were created, and this includes the level of
536+
detail distances used. Even if the complexity of the shape is the same, modern shapes tend to
537+
use longer LOD distances than their legacy counterparts, which can lead to visual inconsistenty.
538+
The :ref:`"level of detail bias" option<options-lod-bias>` can be used to extend the LOD distance
539+
of *all* shapes, but this would not solve the inconsistency between shapes.
540+
541+
.. index::
542+
single: ESD_ORTSLODOverride
543+
544+
To resolve this without editing shape files themsleves, the ``ESD_ORTSLODOverride ( LODindex LODdistance )``
545+
parameter can be used in the shape descriptor file to set the LOD distance of the LOD at ``LODindex`` to a value
546+
of ``LODdistance``, where LODindex is an integer specifying which LOD to edit (NOTE: LOD 0 is the closest LOD, LOD 1 is
547+
the second closest, and so on) and LODdistance is a decimal measured in units of distance (default meters) specifying
548+
the maximum view distance of the LOD. The number of LODs and their default distances can be seen in shape viewing programs.
549+
550+
As an example, a shape from 2011 has LOD distances of 100m, 300m, 700m, and 2000m. Because the shape was already of high
551+
quality, a later product from 2020 used the same shape but with upgraded textures and LODs bumped to 300m, 750m, 1000m and 2000m.
552+
To upgrade the old shape LOD to match the new shape, the following shape descriptor could be used::
553+
554+
SIMISA@@@@@@@@@@JINX0t1t______
555+
556+
Shape ( 50ft_BOX_BNSF722974.s
557+
ESD_Detail_Level ( 0 )
558+
ESD_Software_DLev ( 3 )
559+
ESD_Alternative_Texture ( 0 )
560+
ESD_Bounding_Box (
561+
-1.539 0.61059 -8.08
562+
1.549 3.54 8.08 )
563+
564+
Comment ( New addition: LOD improvement )
565+
ESD_ORTSLODOverride (
566+
0 300m
567+
1 750m
568+
2 1000m
569+
)
570+
)
571+
572+
573+
Observe how multiple LODs can be edited with a single parameter by specifying additional pairs of LOD indices and distances,
574+
and how not all LODs need to be present (LOD 3 is 2000m on both the old and new shape, so doesn't need to be changed).
575+
If a given shape does not have the LOD index specified (eg: LOD 4 does not exist on this shape, as the shape only has 4 LODs)
576+
then a warning is added to the log and the missing LOD is skipped.
577+
532578
Automatic wagon size calculation
533579
--------------------------------
534580

Source/Documentation/Manual/options.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,8 @@ In more modern routes, content may be assigned to categories between 0 and 49.
352352
Content builders are advised to reserve values 50 to 99 for objects used in building the route.
353353

354354

355+
.. _options-lod-bias:
356+
355357
Level of detail bias
356358
--------------------
357359

Source/Orts.Formats.Msts/ShapeDescriptorFile.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,18 @@ public SDShape(STFReader stf)
9090
new STFReader.TokenProcessor("esd_ortsmatrixtranslation", ()=>{ ParseMatrixOverride(STFReader.UNITS.Distance, stf, ref ESD_MatrixTranslation); }),
9191
new STFReader.TokenProcessor("esd_ortsmatrixscale", ()=>{ ParseMatrixOverride(STFReader.UNITS.None, stf, ref ESD_MatrixScale); }),
9292
new STFReader.TokenProcessor("esd_ortsmatrixrotation", ()=>{ ParseMatrixOverride(STFReader.UNITS.Angle, stf, ref ESD_MatrixRotation); }),
93+
new STFReader.TokenProcessor("esd_ortslodoverride", ()=>{
94+
stf.MustMatch("(");
95+
// Allow for multiple pairs of replaced and replacement values
96+
while (!stf.EndOfBlock())
97+
{
98+
int replaced = stf.ReadInt(null);
99+
float replacement = stf.ReadFloat(STFReader.UNITS.Distance, null);
100+
// Add pair of values so long as we haven't reached the end of block
101+
if (replacement != 0)
102+
ESD_LODOverride.Add(replaced, replacement);
103+
}
104+
}),
93105
});
94106

95107
// Store set of all matrices that got modified
@@ -122,6 +134,8 @@ public SDShape(STFReader stf)
122134
public Dictionary<string, Vector3> ESD_MatrixScale = new Dictionary<string, Vector3>();
123135
// Dictionary of <matrix name, matrix rotation x/y/z vector>
124136
public Dictionary<string, Vector3> ESD_MatrixRotation = new Dictionary<string, Vector3>();
137+
// Dictionary of <LOD index, LOD distance>
138+
public Dictionary<int, float> ESD_LODOverride = new Dictionary<int, float>();
125139

126140
// Handle parameters concerning replacement of string values
127141
protected void ParseReplacementStrings(STFReader stf, ref Dictionary<string, string> renamePairs)

Source/RunActivity/Viewer3D/Shapes.cs

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2224,6 +2224,56 @@ void LoadContent()
22242224
Trace.TraceWarning("Shape descriptor file {0} specifies matrix name {1} to be modified, " +
22252225
"but shape {2} does not contain this matrix.", (filePath + "d"), matName, filePath);
22262226
}
2227+
2228+
// Manipulate LOD settings as defined in the sd file
2229+
foreach (lod_control lodControl in sFile.shape.lod_controls)
2230+
{
2231+
foreach (KeyValuePair<int, float> lodIndex in sdFile.shape.ESD_LODOverride)
2232+
{
2233+
if (lodIndex.Key >= 0 && lodIndex.Key < lodControl.distance_levels.Count)
2234+
{
2235+
lodControl.distance_levels[lodIndex.Key].distance_level_header.dlevel_selection = lodIndex.Value;
2236+
}
2237+
else // LOD index defined doesn't eist
2238+
{
2239+
Trace.TraceWarning("Shape descriptor file {0} specifies LOD index {1} to be modified, " +
2240+
"but shape {2} does not have this many LODs.", (filePath + "d"), lodIndex.Key, filePath);
2241+
}
2242+
}
2243+
}
2244+
}
2245+
2246+
// Fix for LODs having a LOD distance of 0
2247+
// This may occur due to improper settings when exporting shape file
2248+
// To prevent missing scenery, force LOD distance to be something reasonable
2249+
foreach (lod_control lodControl in sFile.shape.lod_controls)
2250+
{
2251+
for (int dLevel = 0; dLevel < lodControl.distance_levels.Count; dLevel++)
2252+
{
2253+
if (lodControl.distance_levels[dLevel].distance_level_header.dlevel_selection <= 0f)
2254+
{
2255+
// Estimate a reasonable LOD distance based on surrounding LODs
2256+
// If there are no other LODs, use 2 km as the LOD distance
2257+
float thisLODDistance = 2000f;
2258+
float prevLODDistance = -1f;
2259+
float nextLODDistance = -1f;
2260+
2261+
if (dLevel + 1 < lodControl.distance_levels.Count)
2262+
nextLODDistance = lodControl.distance_levels[dLevel + 1].distance_level_header.dlevel_selection;
2263+
if (dLevel - 1 >= 0)
2264+
prevLODDistance = lodControl.distance_levels[dLevel - 1].distance_level_header.dlevel_selection;
2265+
2266+
// Both a previous and next LOD were found
2267+
if (prevLODDistance >= 0f && nextLODDistance >= 0f)
2268+
thisLODDistance = (prevLODDistance + nextLODDistance) / 2.0f;
2269+
else if (prevLODDistance >= 0f) // Only a previous LOD was found
2270+
thisLODDistance = prevLODDistance * 2.0f;
2271+
else if (nextLODDistance >= 0f) // Only a subsequent LOD was found
2272+
thisLODDistance = nextLODDistance / 2.0f;
2273+
2274+
lodControl.distance_levels[dLevel].distance_level_header.dlevel_selection = thisLODDistance;
2275+
}
2276+
}
22272277
}
22282278

22292279
var matrixCount = sFile.shape.matrices.Count;

0 commit comments

Comments
 (0)