Skip to content

Commit 1e312c4

Browse files
committed
Automatic merge of T1.5.1-997-gb5992851e1 and 19 pull requests
- Pull request #799 at dfc715e: Consolidated wind simulation - Pull request #839 at d00beb9: First phase of https://blueprints.launchpad.net/or/+spec/additional-cruise-control-parameters - Pull request #876 at f92de76: docs: add source for documents previously on website to source Documentation folder - Pull request #882 at b3f83ed: Blueprint/train car operations UI window - Pull request #885 at 56c17fb: feat: Add notifications to Menu - Pull request #891 at 9a1d6b2: Auto save - Pull request #892 at 1f5ba4c: Signal Function OPP_SIG_ID_TRAINPATH - Pull request #896 at 5866028: First implementation of https://blueprints.launchpad.net/or/+spec/specific-sounds-for-ai-trains - Pull request #900 at c27f32d: DMI updates - Pull request #903 at 3e390b8: Downloading route content (Github, zip) - Pull request #912 at 359cfee: New Triple Valve Features Vol. 2 - Pull request #922 at abe2e52: Autopilot for timetable mode - Pull request #946 at 66f836c: Advanced track sounds - Pull request #949 at 1985cbe: Oil Burning Locomotive - Pull request #950 at a98ff62: Ctrl-F5 showing yellow rectangles where mouse left button is active - Pull request #951 at 486081b: fix: Fix watchdog process state name - Pull request #952 at b2af1f5: Investigation - Pulsing graphics part 1 - Pull request #953 at 9b0ec01: Fix Lights Crash on Corrupt Shapes - Pull request #954 at 23cff01: Add Support for Multiple Track Profiles
21 parents 4a2b670 + b599285 + dfc715e + d00beb9 + f92de76 + b3f83ed + 56c17fb + 9a1d6b2 + 1f5ba4c + 5866028 + c27f32d + 3e390b8 + 359cfee + abe2e52 + 66f836c + 1985cbe + a98ff62 + 486081b + b2af1f5 + 9b0ec01 + 23cff01 commit 1e312c4

File tree

7 files changed

+182
-51
lines changed

7 files changed

+182
-51
lines changed

Source/Orts.Formats.Msts/TrackDatabaseFile.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -794,6 +794,8 @@ public class TrVectorSection
794794

795795
/// <summary>??? (needed for ActivityEditor, but not used here, so why is it defined here?)</summary>
796796
public bool Reduced { get; set; }
797+
/// <summary>The index of the track profile suitable for this section</summary>
798+
public int TRPIndex { get; set; } = -1;
797799

798800
/// <summary>
799801
/// Default constructor used during file parsing.

Source/RunActivity/Viewer3D/DynamicTrack.cs

Lines changed: 94 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -130,21 +130,13 @@ public class DynamicTrackViewer
130130
WorldPosition worldPosition;
131131
public DynamicTrackPrimitive Primitive;
132132

133-
public DynamicTrackViewer(Viewer viewer, DyntrackObj dtrack, WorldPosition position, WorldPosition endPosition)
133+
public DynamicTrackViewer(Viewer viewer, DyntrackObj dtrack, WorldPosition position, WorldPosition endPosition, int trpIndex = 0)
134134
{
135135
Viewer = viewer;
136136
worldPosition = position;
137137

138-
if (viewer.TRP == null)
139-
{
140-
// First to need a track profile creates it
141-
Trace.Write(" TRP");
142-
// Creates profile and loads materials into SceneryMaterials
143-
TRPFile.CreateTrackProfile(viewer, viewer.Simulator.RoutePath, out viewer.TRP);
144-
}
145-
146138
// Instantiate classes
147-
Primitive = new DynamicTrackPrimitive(Viewer, dtrack, worldPosition, endPosition);
139+
Primitive = new DynamicTrackPrimitive(Viewer, dtrack, worldPosition, endPosition, trpIndex);
148140
}
149141

150142
public DynamicTrackViewer(Viewer viewer, WorldPosition position, WorldPosition endPosition)
@@ -219,6 +211,26 @@ public void Mark()
219211
foreach (LOD lod in Primitive.TrProfile.LODs)
220212
lod.Mark();
221213
}
214+
215+
/// <summary>
216+
/// Returns the index of the track profile that best suits the given TrVectorSection
217+
/// The result is cached in the section object for future use
218+
/// </summary>
219+
/// <returns>Index of viewer.TRPs</returns>
220+
public static int GetBestTrackProfile(Viewer viewer, TrVectorSection trSection)
221+
{
222+
string shapeName = "";
223+
if (viewer.Simulator.TSectionDat.TrackShapes.ContainsKey(trSection.ShapeIndex))
224+
shapeName = viewer.Simulator.TSectionDat.TrackShapes.Get(trSection.ShapeIndex).FileName;
225+
226+
viewer.TrackProfileIndicies.TryGetValue(shapeName, out int trpIndex);
227+
228+
trSection.TRPIndex = trpIndex;
229+
return trpIndex;
230+
}
231+
// Note: Dynamic track objects will ALWAYS use TRPIndex = 0
232+
// This is because dynamic tracks don't have shapes, and as such lack the information needed
233+
// to select a specific track profile.
222234
}
223235

224236
// A track profile consists of a number of groups used for LOD considerations. There are LODs,
@@ -242,26 +254,40 @@ public class TRPFile
242254
/// </summary>
243255
/// <param name="viewer">Viewer.</param>
244256
/// <param name="routePath">Path to route.</param>
245-
/// <param name="trpFile">TRPFile created (out).</param>
246-
public static void CreateTrackProfile(Viewer viewer, string routePath, out TRPFile trpFile)
257+
/// <param name="trpFiles">List of TRPFile(s) created (out).</param>
258+
public static void CreateTrackProfile(Viewer viewer, string routePath, out List<TRPFile> trpFiles)
247259
{
248260
string path = routePath + @"\TrackProfiles";
249-
//Establish default track profile
250-
if (Directory.Exists(path) && File.Exists(path + @"\TrProfile.xml"))
251-
{
252-
// XML-style
253-
trpFile = new TRPFile(viewer, path + @"\TrProfile.xml");
254-
}
255-
else if (Directory.Exists(path) && File.Exists(path + @"\TrProfile.stf"))
256-
{
257-
// MSTS-style
258-
trpFile = new TRPFile(viewer, path + @"\TrProfile.stf");
259-
}
260-
else
261+
List<string> profileNames = new List<string>();
262+
trpFiles = new List<TRPFile>();
263+
264+
if (Directory.Exists(path))
261265
{
262-
// default
263-
trpFile = new TRPFile(viewer, "");
266+
// Get all .xml/.stf files that start with "TrProfile"
267+
string[] xmlProfiles = Directory.GetFiles(path, "TrProfile*.xml");
268+
string[] stfProfiles = Directory.GetFiles(path, "TrProfile*.stf");
269+
270+
foreach (string xmlProfile in xmlProfiles)
271+
{
272+
trpFiles.Add(new TRPFile(viewer, xmlProfile));
273+
profileNames.Add(Path.GetFileNameWithoutExtension(xmlProfile));
274+
}
275+
foreach (string stfProfile in stfProfiles)
276+
{
277+
string stfName = Path.GetFileNameWithoutExtension(stfProfile);
278+
// If an .stf profile and .xml profile have the same name, prefer the xml profile
279+
if (!profileNames.Contains(stfName))
280+
{
281+
trpFiles.Add(new TRPFile(viewer, stfProfile));
282+
profileNames.Add(stfName);
283+
}
284+
}
264285
}
286+
287+
// Add default profile only if no other profiles were added
288+
if (trpFiles.Count <= 0)
289+
trpFiles.Add(new TRPFile(viewer, ""));
290+
265291
// FOR DEBUGGING: Writes XML file from current TRP
266292
//TRP.TrackProfile.SaveAsXML(@"C:/Users/Walt/Desktop/TrProfile.xml");
267293
}
@@ -390,6 +416,7 @@ public class TrProfile
390416
public PitchControls PitchControl = PitchControls.None; // Method of control for profile replication pitch
391417
public float PitchControlScalar; // Scalar parameter for PitchControls
392418
public ArrayList LODs = new ArrayList(); // Array of Levels-Of-Detail
419+
public List<string> Images = new List<string>();
393420

394421
/// <summary>
395422
/// Enumeration of LOD control methods
@@ -552,6 +579,17 @@ public TrProfile(Viewer viewer)
552579

553580
lod.LODItems.Add(lodItem); // Append this LODItem to LODItems array
554581
LODs.Add(lod); // Append this LOD to LODs array
582+
583+
// Add textures
584+
foreach (LOD level in LODs)
585+
{
586+
foreach (LODItem item in level.LODItems)
587+
{
588+
string texFileName = Path.GetFileNameWithoutExtension(item.TexName);
589+
if (!Images.Contains(texFileName))
590+
Images.Add(texFileName);
591+
}
592+
}
555593
}
556594

557595
/// <summary>
@@ -571,7 +609,20 @@ public TrProfile(Viewer viewer, STFReader stf)
571609
new STFReader.TokenProcessor("lod", ()=> { LODs.Add(new LOD(viewer, stf)); }),
572610
});
573611

574-
if (LODs.Count == 0) throw new Exception("missing LODs");
612+
if (LODs.Count == 0)
613+
throw new Exception("missing LODs");
614+
else // Add textures
615+
{
616+
foreach (LOD level in LODs)
617+
{
618+
foreach (LODItem item in level.LODItems)
619+
{
620+
string texFileName = Path.GetFileNameWithoutExtension(item.TexName);
621+
if (!Images.Contains(texFileName))
622+
Images.Add(texFileName);
623+
}
624+
}
625+
}
575626
}
576627

577628
/// <summary>
@@ -649,7 +700,20 @@ public TrProfile(Viewer viewer, XmlReader reader)
649700
}
650701
}
651702
}
652-
if (LODs.Count == 0) throw new Exception("missing LODs");
703+
if (LODs.Count == 0)
704+
throw new Exception("missing LODs");
705+
else // Add textures
706+
{
707+
foreach (LOD level in LODs)
708+
{
709+
foreach (LODItem item in level.LODItems)
710+
{
711+
string texFileName = Path.GetFileNameWithoutExtension(item.TexName);
712+
if (!Images.Contains(texFileName))
713+
Images.Add(texFileName);
714+
}
715+
}
716+
}
653717
}
654718

655719
/// <summary>
@@ -986,7 +1050,7 @@ public DynamicTrackPrimitive()
9861050
/// Constructor.
9871051
/// </summary>
9881052
public DynamicTrackPrimitive(Viewer viewer, DyntrackObj track, WorldPosition worldPosition,
989-
WorldPosition endPosition)
1053+
WorldPosition endPosition, int trpIndex = 0)
9901054
{
9911055
// DynamicTrackPrimitive is responsible for creating a mesh for a section with a single subsection.
9921056
// It also must update worldPosition to reflect the end of this subsection, subsequently to
@@ -1013,7 +1077,7 @@ public DynamicTrackPrimitive(Viewer viewer, DyntrackObj track, WorldPosition wor
10131077

10141078
XNAEnd = endPosition.XNAMatrix.Translation;
10151079

1016-
TrProfile = viewer.TRP.TrackProfile;
1080+
TrProfile = viewer.TRPs[trpIndex].TrackProfile;
10171081
// Count all of the LODItems in all the LODs
10181082
int count = 0;
10191083
for (int i = 0; i < TrProfile.LODs.Count; i++)

Source/RunActivity/Viewer3D/Scenery.cs

Lines changed: 67 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -367,24 +367,34 @@ public WorldFile(Viewer viewer, int tileX, int tileZ, bool visible)
367367
// We might not have found the junction node; if so, fall back to the static track shape.
368368
if (trJunctionNode != null)
369369
{
370-
if (viewer.Simulator.UseSuperElevation > 0) SuperElevationManager.DecomposeStaticSuperElevation(viewer, dTrackList, trackObj, worldMatrix, TileX, TileZ, shapeFilePath);
371-
sceneryObjects.Add(new SwitchTrackShape(viewer, shapeFilePath, worldMatrix, trJunctionNode));
370+
if (viewer.Simulator.UseSuperElevation > 0)
371+
SuperElevationManager.DecomposeStaticSuperElevation(viewer, dTrackList, trackObj, worldMatrix, TileX, TileZ, shapeFilePath);
372+
var newScenery = new SwitchTrackShape(viewer, shapeFilePath, worldMatrix, trJunctionNode);
373+
sceneryObjects.Add(newScenery);
374+
375+
string newShapeName = Path.GetFileName(newScenery.SharedShape.FilePath);
376+
if (!viewer.TrackProfileIndicies.ContainsKey(newShapeName))
377+
viewer.TrackProfileIndicies.Add(newShapeName, GetBestTrackProfile(viewer, newScenery.SharedShape));
372378
}
373379
else
374380
{
375381
//if want to use super elevation, we will generate tracks using dynamic tracks
376382
if (viewer.Simulator.UseSuperElevation > 0
377383
&& SuperElevationManager.DecomposeStaticSuperElevation(viewer, dTrackList, trackObj, worldMatrix, TileX, TileZ, shapeFilePath))
378384
{
379-
//var success = SuperElevation.DecomposeStaticSuperElevation(viewer, dTrackList, trackObj, worldMatrix, TileX, TileZ, shapeFilePath);
380-
//if (success == 0) sceneryObjects.Add(new StaticTrackShape(viewer, shapeFilePath, worldMatrix));
385+
// Need to load the static shape file to determine which track profile to use
386+
var superelevatedShape = viewer.ShapeManager.Get(shapeFilePath);
387+
388+
string newShapeName = Path.GetFileName(superelevatedShape.FilePath);
389+
if (!viewer.TrackProfileIndicies.ContainsKey(newShapeName))
390+
viewer.TrackProfileIndicies.Add(newShapeName, GetBestTrackProfile(viewer, superelevatedShape));
381391
}
382392
//otherwise, use shapes
383393
else if (!containsMovingTable) sceneryObjects.Add(new StaticTrackShape(viewer, shapeFilePath, worldMatrix));
384394
else
385395
{
386396
var found = false;
387-
foreach (var movingTable in Program.Simulator. MovingTables)
397+
foreach (var movingTable in Program.Simulator.MovingTables)
388398
{
389399
if (worldObject.UID == movingTable.UID && WFileName == movingTable.WFile)
390400
{
@@ -420,6 +430,14 @@ public WorldFile(Viewer viewer, int tileX, int tileZ, bool visible)
420430
}
421431
else if (worldObject.GetType() == typeof(DyntrackObj))
422432
{
433+
if (viewer.TRPs == null)
434+
{
435+
// First to need a track profile creates it
436+
Trace.Write(" TRP");
437+
// Creates profile and loads materials into SceneryMaterials
438+
TRPFile.CreateTrackProfile(viewer, viewer.Simulator.RoutePath, out viewer.TRPs);
439+
}
440+
423441
if (viewer.Simulator.Settings.Wire == true && viewer.Simulator.TRK.Tr_RouteFile.Electrified == true)
424442
Wire.DecomposeDynamicWire(viewer, dTrackList, (DyntrackObj)worldObject, worldMatrix);
425443
// Add DyntrackDrawers for individual subsections
@@ -646,6 +664,50 @@ public void PrepareFrame(RenderFrame frame, ElapsedTime elapsedTime)
646664
forest.PrepareFrame(frame, elapsedTime);
647665
}
648666

667+
/// <summary>
668+
/// Determines the index of the track profile that would be the most suitable replacement
669+
/// for the given shared shape object.
670+
/// </summary>
671+
public static int GetBestTrackProfile(Viewer viewer, SharedShape shape)
672+
{
673+
if (viewer.TRPs == null)
674+
{
675+
// First to need a track profile creates it
676+
Trace.Write(" TRP");
677+
// Creates profile and loads materials into SceneryMaterials
678+
TRPFile.CreateTrackProfile(viewer, viewer.Simulator.RoutePath, out viewer.TRPs);
679+
}
680+
681+
float score = 0;
682+
int bestIndex = -1;
683+
for (int i = 0; i < viewer.TRPs.Count; i++)
684+
{
685+
686+
float prevScore = score;
687+
// Default behavior: Attempt to match track shape to track profile using texture names alone
688+
foreach (string image in viewer.TRPs[i].TrackProfile.Images)
689+
{
690+
if (shape.ImageNames.Contains(image, StringComparer.InvariantCultureIgnoreCase))
691+
score++;
692+
else // Slight bias to prefer track profiles with more textures defined
693+
score += 0.05f;
694+
}
695+
foreach (string image in shape.ImageNames)
696+
{
697+
// Bias against track profiles that are missing textures
698+
if (!viewer.TRPs[i].TrackProfile.Images.Contains(image, StringComparer.InvariantCultureIgnoreCase))
699+
score -= 0.25f;
700+
}
701+
if (score > prevScore)
702+
bestIndex = i;
703+
}
704+
705+
if (bestIndex < 0)
706+
return 0;
707+
else
708+
return bestIndex;
709+
}
710+
649711
/// <summary>
650712
/// MSTS WFiles represent some location with a position, quaternion and tile coordinates
651713
/// This converts it to the ORTS WorldPosition representation

Source/RunActivity/Viewer3D/Shapes.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1960,6 +1960,7 @@ public class SharedShape : IDisposable
19601960

19611961
// This data is common to all instances of the shape
19621962
public List<string> MatrixNames = new List<string>();
1963+
public List<string> ImageNames; // Names of textures without paths or file extensions
19631964
public Matrix[] Matrices = new Matrix[0]; // the original natural pose for this shape - shared by all instances
19641965
public animations Animations;
19651966
public LodControl[] LodControls;
@@ -2044,6 +2045,8 @@ void LoadContent()
20442045
}
20452046
Animations = sFile.shape.animations;
20462047

2048+
ImageNames = new List<string>(sFile.shape.images.ConvertAll(img => Path.GetFileNameWithoutExtension(img)));
2049+
20472050
#if DEBUG_SHAPE_HIERARCHY
20482051
var debugShapeHierarchy = new StringBuilder();
20492052
debugShapeHierarchy.AppendFormat("Shape {0}:\n", Path.GetFileNameWithoutExtension(FilePath).ToUpper());

0 commit comments

Comments
 (0)