Skip to content

Commit 3658313

Browse files
committed
Automatic merge of T1.5.1-1695-gade019506 and 16 pull requests
- Pull request #570 at 8c7b4a7: glTF 2.0 support with PBR lighting - Pull request #900 at 4629736: DMI in 3D cab + two more dials - Pull request #1030 at d3ae4a2: Refactor settings, in prep for settings exporter - Pull request #1045 at cc4d53c: Bugfix: Empty Start Time for Explore, and other issues loading from Menu Selection and Content Routes - Pull request #1052 at 5bbc476: Content Manager: Add axle count, and lowest derail force - Pull request #1062 at 3b18c48: Train Forces popup Window. - Pull request #1064 at 9b3415e: Add Train Info tab to Help window (F1) - Pull request #1066 at 62c89c1: Log derailment, using TraceInformation. - Pull request #892 at 1f5ba4c: Signal Function OPP_SIG_ID_TRAINPATH - Pull request #1000 at d8d9709: Locomotive operation from control car - Pull request #1029 at 92c74ef: Superelevation Follow Up Fixes - Pull request #1057 at 50c3789: Switchable brake system - Pull request #1069 at 3f153f0: Mouse wheel control only with Alt – Followup for #1051 - Pull request #1055 at f6bf9fa: Route Based TTrack Sounds - Pull request #896 at f1681df: First implementation of https://blueprints.launchpad.net/or/+spec/specific-sounds-for-ai-trains - Pull request #1049 at 4dd8c4f: Re-organise document storage and access
18 parents 42f4d56 + ade0195 + 8c7b4a7 + 4629736 + d3ae4a2 + cc4d53c + 5bbc476 + 3b18c48 + 9b3415e + 62c89c1 + 1f5ba4c + d8d9709 + 92c74ef + 50c3789 + 3f153f0 + f6bf9fa + f1681df + 4dd8c4f commit 3658313

37 files changed

+4510
-502
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
1. git clone the glTF schema repository to any directory: https://github.com/KhronosGroup/glTF
2+
2. Replace the glTF\specification\2.0\schema\extras.schema.json file with the one provided here by the OpenRails project.
3+
3. Replace the glTF\specification\2.0\schema\animation.channel.target.schema.json file with the one provided here by the OpenRails project.
4+
4. Copy the glTF\extensions\2.0\Khronos\KHR_animation_pointer\schema\animation.channel.target.KHR_animation_pointer.schema.json file in to the glTF\specification\2.0\schema\ folder.
5+
5. git clone the gltfLoader repository to the same base directory: https://github.com/KhronosGroup/glTF-CSharp-Loader
6+
6. Open the glTF-CSharp-Loader\CSharp.sln
7+
7. Bump the version of Newtonsoft.Json in the gltfLoader project to match with that one used in OpenRails (11.0.2 as of writing this).
8+
8. Build the Generator project.
9+
9. Build the gltfLoader project.
10+
10. Copy the resulting glTF-CSharp-Loader\glTFLoader\bin\Debug\netstandard1.3\glTFLoader.dll to this directory.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
{
2+
"$schema": "https://json-schema.org/draft/2020-12/schema",
3+
"$id": "animation.channel.target.schema.json",
4+
"title": "Animation Channel Target",
5+
"type": "object",
6+
"description": "The descriptor of the animated property.",
7+
"allOf": [ { "$ref": "glTFProperty.schema.json" } ],
8+
"properties": {
9+
"node": {
10+
"allOf": [ { "$ref": "glTFid.schema.json" } ],
11+
"description": "The index of the node to animate. When undefined, the animated object **MAY** be defined by an extension."
12+
},
13+
"path": {
14+
"description": "The name of the node's TRS property to animate, or the `\"weights\"` of the Morph Targets it instantiates. For the `\"translation\"` property, the values that are provided by the sampler are the translation along the X, Y, and Z axes. For the `\"rotation\"` property, the values are a quaternion in the order (x, y, z, w), where w is the scalar. For the `\"scale\"` property, the values are the scaling factors along the X, Y, and Z axes.",
15+
"anyOf": [
16+
{
17+
"const": "pointer"
18+
},
19+
{
20+
"const": "translation"
21+
},
22+
{
23+
"const": "rotation"
24+
},
25+
{
26+
"const": "scale"
27+
},
28+
{
29+
"const": "weights"
30+
},
31+
{
32+
"type": "string"
33+
}
34+
]
35+
},
36+
"extensions": { },
37+
"extras": { }
38+
},
39+
"required": [ "path" ]
40+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"$schema": "https://json-schema.org/draft/2020-12/schema",
3+
"$id": "extras.schema.json",
4+
"title": "Extras",
5+
"type": "object",
6+
"description": "Application-specific data.",
7+
"gltf_sectionDescription": "Although `extras` **MAY** have any type, it is common for applications to store and access custom data as key/value pairs. Therefore, `extras` **SHOULD** be a JSON object rather than a primitive value for best portability.",
8+
"properties": {
9+
},
10+
"additionalProperties": {
11+
"type": "object"
12+
}
13+
}
62.5 KB
Binary file not shown.
+198
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
// COPYRIGHT 2023 by the Open Rails project.
2+
//
3+
// This file is part of Open Rails.
4+
//
5+
// Open Rails is free software: you can redistribute it and/or modify
6+
// it under the terms of the GNU General Public License as published by
7+
// the Free Software Foundation, either version 3 of the License, or
8+
// (at your option) any later version.
9+
//
10+
// Open Rails is distributed in the hope that it will be useful,
11+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
// GNU General Public License for more details.
14+
//
15+
// You should have received a copy of the GNU General Public License
16+
// along with Open Rails. If not, see <http://www.gnu.org/licenses/>.
17+
18+
using System;
19+
using System.Collections.Generic;
20+
using System.Diagnostics;
21+
using System.IO;
22+
using System.Linq;
23+
using System.Text;
24+
25+
namespace ORTS.Common
26+
{
27+
/// <summary>
28+
/// Utility functions to procedurally generate con and eng files to display the Khronos glTF-Sample-Assets for testing purposes.
29+
/// For this to work 'git clone https://github.com/KhronosGroup/glTF-Sample-Assets.git' to the MSTS/TRAINS/TRAINSET folder,
30+
/// so that the models will be available in e.g. MSTS/TRAINS/TRAINSET/glTF-Sample-Assets/Models/... folder. Then start like:
31+
/// RunActivity.exe -start -explorer "C:\Devel\MSTS\ROUTES\USA2\PATHS\tut6path.pat" "glTF-Sample-Assets" 12:00 1 0
32+
/// RunActivity.exe -start -explorer "C:\Devel\MSTS\ROUTES\USA2\PATHS\tut6path.pat" "glTF-Sample-Assets&AnimatedTriangle" 12:00 1 0
33+
/// RunActivity.exe -start -explorer "C:\Devel\MSTS\ROUTES\USA2\PATHS\tut6path.pat" "glTF-Sample-Assets#2" 12:00 1 0
34+
/// </summary>
35+
public class ConsistGenerator
36+
{
37+
/// <summary>
38+
/// Indicates if the current run is a glTF visual test only. It is possible to generate an on-the-fly consist of all the Khronos test models.
39+
/// </summary>
40+
public static bool GltfVisualTestRun;
41+
42+
const string ConsistTemplateStart =
43+
@"Train (
44+
TrainCfg ( ""trainname""
45+
Name ( ""trainname"" )
46+
MaxVelocity ( 50 1 )
47+
";
48+
const string ConsistTemplateRecord = @"Engine ( UID ( xx ) EngineData ( ""engfile"" ""trainsetdir"" ) )" + "\r\n";
49+
const string ConsistTemplateEnd = ")\r\n)";
50+
const string EngineTemplate =
51+
@"Wagon ( ""enginname""
52+
Type ( Engine )
53+
WagonShape ( ""shapefilename"" )
54+
Size (100m 7m 100m )
55+
Lights ( 1
56+
Light (
57+
Type ( 1 )
58+
ShapeHierarchy ( Headlight )
59+
Conditions (
60+
Headlight ( 3 )
61+
Unit ( 2 )
62+
)
63+
Cycle ( 0 )
64+
FadeIn ( 1 )
65+
FadeOut ( 1 )
66+
States ( 1
67+
State (
68+
Duration ( 0.0 )
69+
LightColour ( ffffffff )
70+
Position ( 0 3.5 0 )
71+
Transition ( 0 )
72+
Radius ( 400 )
73+
Angle ( 15 )
74+
)
75+
)
76+
)
77+
)
78+
)
79+
Engine ( ""enginname""
80+
Wagon ( ""enginname"" )
81+
Type ( Electric )
82+
CabView ( dummy.cvf )
83+
Name ( ""enginname"" )
84+
)";
85+
86+
static readonly Dictionary<string, string> Wagons = new Dictionary<string, string>();
87+
static readonly Dictionary<string, string> SubDirectories = new Dictionary<string, string>
88+
{
89+
{ "glTF-Sample-Assets", "glTF"},
90+
{ "glTF-Sample-Assets-Binary", "glTF-Binary"},
91+
{ "glTF-Sample-Assets-Embedded", "glTF-Embedded"},
92+
{ "glTF-Sample-Assets-Draco", "glTF-Draco"},
93+
{ "glTF-Sample-Assets-Quantized", "glTF-Quantized"},
94+
};
95+
96+
static string GltfExtension(string keyword) => keyword.EndsWith("-Binary") ? ".glb" : ".gltf";
97+
static string RequestedType(string requestedPath) => SubDirectories.FirstOrDefault(ext => ext.Key == Path.GetFileNameWithoutExtension(requestedPath).Split('#', '&').FirstOrDefault()).Key;
98+
99+
public static bool IsConsistRecognized(string requestedPath) => RequestedType(requestedPath) != null;
100+
public static bool IsWagonRecognized(string requestedPath) => Wagons.ContainsKey(Path.GetFileName(requestedPath));
101+
102+
/// <summary>
103+
/// Provides a procedurally generated consist of the requested type of models
104+
/// </summary>
105+
/// <param name="requestedPath"></param>
106+
/// <returns></returns>
107+
public static Stream GetConsist(string requestedPath)
108+
{
109+
var trainsetDir = "glTF-Sample-Assets";
110+
var baseDir = Path.Combine(Path.GetDirectoryName(Path.GetDirectoryName(requestedPath)), "TRAINSET", trainsetDir);
111+
if (!Directory.Exists(baseDir))
112+
{
113+
Console.WriteLine($"Cannot find the trainset directory {trainsetDir}.");
114+
return Stream.Null;
115+
}
116+
117+
var keyword = RequestedType(requestedPath);
118+
Debug.Assert(keyword != null);
119+
120+
var consist = ConsistTemplateStart.Replace("trainname", keyword);
121+
122+
if (keyword.StartsWith("glTF"))
123+
{
124+
GltfVisualTestRun = true;
125+
126+
var models = Directory.EnumerateFileSystemEntries(baseDir, "*.*", SearchOption.AllDirectories)
127+
.Where(f => !f.Contains(Path.Combine(baseDir, "Models")) && (f.EndsWith(".gltf") || f.EndsWith(".glb")))
128+
.Select(f => Path.GetFileNameWithoutExtension(f))
129+
.Concat(Directory.EnumerateDirectories(Path.Combine(baseDir, "Models")))
130+
.Where((m, n) => Path.GetFileName(m) == (Path.GetFileNameWithoutExtension(requestedPath).Split('&').ElementAtOrDefault(1) ?? Path.GetFileName(m)) &&
131+
n == (int.TryParse(Path.GetFileNameWithoutExtension(requestedPath).Split('#').ElementAtOrDefault(1), out var requestedModelNumber) ? requestedModelNumber : n));
132+
133+
var uid = 0;
134+
foreach (var model in models)
135+
{
136+
var dir = Path.Combine(model, SubDirectories[keyword]);
137+
var file = (Directory.Exists(dir)
138+
? Directory.EnumerateFiles(dir).FirstOrDefault(f => f.EndsWith(GltfExtension(keyword)))
139+
: Directory.EnumerateFiles(baseDir).FirstOrDefault(f => f.Contains(model)))
140+
?.Substring(baseDir.Length + 1)
141+
?.Replace(@"\", "/");
142+
143+
if (file == null)
144+
{
145+
// When gltf files got dropped into the sample directory, show that ones too.
146+
file = Directory.GetFiles(baseDir, Path.GetFileNameWithoutExtension(model) + "*.gl*", SearchOption.AllDirectories)
147+
.Where(f => !f.Contains(Path.Combine(baseDir, "Models")) && (f.EndsWith(".gltf") || f.EndsWith(".glb")))
148+
.FirstOrDefault()
149+
?.Substring(baseDir.Length + 1)
150+
?.Replace(@"\", "/");
151+
if (file == null)
152+
continue;
153+
}
154+
155+
var eng = $"{keyword}_{Path.GetFileNameWithoutExtension(file)}.eng";
156+
Wagons.Add(eng, EngineTemplate
157+
.Replace("shapefilename", file)
158+
.Replace("enginname", Path.GetFileNameWithoutExtension(file)));
159+
160+
consist += ConsistTemplateRecord
161+
.Replace("xx", uid.ToString())
162+
.Replace("engfile", Path.GetFileNameWithoutExtension(eng))
163+
.Replace("trainsetdir", trainsetDir);
164+
uid++;
165+
}
166+
}
167+
consist += ConsistTemplateEnd;
168+
169+
var stream = new MemoryStream();
170+
using (var writer = new StreamWriter(stream, Encoding.UTF8, Encoding.UTF8.GetByteCount(consist), true))
171+
{
172+
writer.Write(consist);
173+
writer.Flush();
174+
}
175+
stream.Position = 0;
176+
return stream;
177+
}
178+
179+
public static Stream GetWagon(string requestedEngine)
180+
{
181+
if (!Wagons.ContainsKey(Path.GetFileName(requestedEngine)))
182+
{
183+
Console.WriteLine($"Cannot find the requested procedurally generated engine {requestedEngine}.");
184+
return Stream.Null;
185+
}
186+
187+
var wagon = Wagons[Path.GetFileName(requestedEngine)];
188+
var stream = new MemoryStream();
189+
using (var writer = new StreamWriter(stream, Encoding.UTF8, Encoding.UTF8.GetByteCount(wagon), true))
190+
{
191+
writer.Write(wagon);
192+
writer.Flush();
193+
}
194+
stream.Position = 0;
195+
return stream;
196+
}
197+
}
198+
}

Source/Orts.Formats.Msts/ConsistFile.cs

+11
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,17 @@ public ConsistFile(string filePath)
3939
Name = Train.TrainCfg.Name;
4040
}
4141

42+
/// <summary>
43+
/// Allows to use a procedurally generated consist file.
44+
/// </summary>
45+
public ConsistFile(Stream inputStream, string filePath)
46+
{
47+
using (var stf = new STFReader(inputStream, filePath, System.Text.Encoding.UTF8, false))
48+
stf.ParseFile(new STFReader.TokenProcessor[] {
49+
new STFReader.TokenProcessor("train", ()=>{ Train = new Train_Config(stf); }),
50+
});
51+
}
52+
4253
public override string ToString()
4354
{
4455
return Name;

Source/Orts.Settings/UserSettings.cs

+4
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,10 @@ public enum DirectXFeature
182182
public int DayAmbientLight { get; set; }
183183
[Default(AntiAliasingMethod.MSAA2x)]
184184
public int AntiAliasing { get; set; }
185+
[Default(false)]
186+
public bool GltfAnimations { get; set; }
187+
[Default(true)]
188+
public bool GltfTangentsAlwaysCalculatedPerPixel { get; set; }
185189

186190
// Simulation settings:
187191
[Default(false)]

Source/Orts.Simulation/Simulation/RollingStocks/MSTSLocomotive.cs

+7
Original file line numberDiff line numberDiff line change
@@ -5027,6 +5027,7 @@ public void ToggleCabRadio( bool newState)
50275027
public void ToggleWipers(bool newState)
50285028
{
50295029
SignalEvent(newState ? Event.WiperOn : Event.WiperOff);
5030+
if (ConsistGenerator.GltfVisualTestRun) Train.SignalEvent(newState ? Event.WiperOn : Event.WiperOff);
50305031
}
50315032

50325033
public void SetBailOff(bool bailOff)
@@ -5090,12 +5091,14 @@ public void GenericItem1Toggle()
50905091
{
50915092
GenericItem1 = !GenericItem1;
50925093
SignalEvent(GenericItem1? Event.GenericItem1On : Event.GenericItem1Off); // hook for sound trigger
5094+
if (ConsistGenerator.GltfVisualTestRun) Train.SignalEvent(GenericItem1 ? Event.GenericItem1On : Event.GenericItem1Off);
50935095
}
50945096

50955097
public void GenericItem2Toggle()
50965098
{
50975099
GenericItem2 = !GenericItem2;
50985100
SignalEvent(GenericItem2 ? Event.GenericItem2On : Event.GenericItem2Off); // hook for sound trigger
5101+
if (ConsistGenerator.GltfVisualTestRun) Train.SignalEvent(GenericItem2 ? Event.GenericItem2On : Event.GenericItem2Off);
50995102
}
51005103

51015104
public override bool GetCabFlipped()
@@ -5178,6 +5181,10 @@ public override void SignalEvent(Event evt)
51785181
}
51795182
break;
51805183
}
5184+
case Event.GenericItem1On: if (ConsistGenerator.GltfVisualTestRun) GenericItem1 = true; break;
5185+
case Event.GenericItem1Off: if (ConsistGenerator.GltfVisualTestRun) GenericItem1 = false; break;
5186+
case Event.GenericItem2On: if (ConsistGenerator.GltfVisualTestRun) GenericItem2 = true; break;
5187+
case Event.GenericItem2Off: if (ConsistGenerator.GltfVisualTestRun) GenericItem2 = false; break;
51815188
}
51825189

51835190
base.SignalEvent(evt);

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

+3-1
Original file line numberDiff line numberDiff line change
@@ -400,7 +400,9 @@ public virtual void LoadFromWagFile(string wagFilePath)
400400
if (File.Exists(orFile))
401401
wagFilePath = orFile;
402402

403-
using (STFReader stf = new STFReader(wagFilePath, true))
403+
using (STFReader stf = ConsistGenerator.IsWagonRecognized(wagFilePath)
404+
? new STFReader(ConsistGenerator.GetWagon(wagFilePath), wagFilePath, System.Text.Encoding.UTF8, true)
405+
: new STFReader(wagFilePath, true))
404406
{
405407
while (!stf.Eof)
406408
{

Source/Orts.Simulation/Simulation/RollingStocks/RollingStock.cs

+4-1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
using Orts.Parsers.Msts;
2020
using Orts.Simulation.Physics;
2121
using Orts.Simulation.RollingStocks.SubSystems;
22+
using ORTS.Common;
2223
using System;
2324
using System.Collections.Generic;
2425
using System.Diagnostics;
@@ -138,7 +139,9 @@ public GenericWAGFile(string filenamewithpath)
138139

139140
public void WagFile(string filenamewithpath)
140141
{
141-
using (STFReader stf = new STFReader(filenamewithpath, false))
142+
using (STFReader stf = ConsistGenerator.IsWagonRecognized(filenamewithpath)
143+
? new STFReader(ConsistGenerator.GetWagon(filenamewithpath), filenamewithpath, System.Text.Encoding.UTF8, false)
144+
: new STFReader(filenamewithpath, false))
142145
stf.ParseBlock(new STFReader.TokenProcessor[] {
143146
new STFReader.TokenProcessor("engine", ()=>{ Engine = new EngineClass(stf); }),
144147
new STFReader.TokenProcessor("_openrails", ()=>{ OpenRails = new OpenRailsData(stf); }),

Source/Orts.Simulation/Simulation/Simulator.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1301,7 +1301,7 @@ private Train InitializePlayerTrain()
13011301
// place rear of train on starting location of aiPath.
13021302
train.RearTDBTraveller = new Traveller(TSectionDat, TDB.TrackDB.TrackNodes, aiPath);
13031303

1304-
ConsistFile conFile = new ConsistFile(conFileName);
1304+
ConsistFile conFile = ConsistGenerator.IsConsistRecognized(conFileName) ? new ConsistFile(ConsistGenerator.GetConsist(conFileName), conFileName) : new ConsistFile(conFileName);
13051305
CurveDurability = conFile.Train.TrainCfg.Durability; // Finds curve durability of consist based upon the value in consist file
13061306
train.TcsParametersFileName = conFile.Train.TrainCfg.TcsParametersFileName;
13071307

@@ -1318,7 +1318,7 @@ private Train InitializePlayerTrain()
13181318
wagonFilePath = wagonFolder + @"\" + wagon.Name + ".eot";
13191319
}
13201320

1321-
if (!File.Exists(wagonFilePath))
1321+
if (!File.Exists(wagonFilePath) && !ConsistGenerator.IsWagonRecognized(wagonFilePath))
13221322
{
13231323
// First wagon is the player's loco and required, so issue a fatal error message
13241324
if (wagon == conFile.Train.TrainCfg.WagonList[0])
16.2 KB
Loading
Loading
Loading
Loading
Loading
Loading
Loading
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Downloaded from: https://polyhaven.com/a/champagne_castle_1
2+
Converted to TGA by: https://seenax.com/portfolio/HDR_Compressor.rar
3+
//Converted to cube map by: https://matheowis.github.io/HDRI-to-CubeMap
Loading

0 commit comments

Comments
 (0)