Skip to content

Commit 9665144

Browse files
committed
Automatic merge of T1.5.1-346-g3831af9fa and 8 pull requests
- Pull request #570 at f11d169: Experimental glTF 2.0 support with PBR lighting - Pull request #722 at fb9079e: Fix Windows Forms deprecations in ActivityEditor - Pull request #732 at 1edb2e5: Improvements for air brakes - Pull request #751 at 00981a2: Web interface to control cab controls with external hardware - Pull request #767 at 4cb5c77: Refine sunrise and sunset - Pull request #803 at 7157e08: Various adjustments to steam adhesion - Pull request #809 at f67822a: Some on-screen messages not suppressed, Bug #2008012 - Pull request #812 at d07f812: Bug fix for https://bugs.launchpad.net/or/+bug/2009955 Missing code to rotate DMU display in 2D cabs
10 parents 7a7e96d + 3831af9 + f11d169 + fb9079e + 1edb2e5 + 00981a2 + 4cb5c77 + 7157e08 + f67822a + d07f812 commit 9665144

35 files changed

+4062
-434
lines changed
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
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. git clone the gltfLoader repository to the same base directory: https://github.com/KhronosGroup/glTF-CSharp-Loader
4+
4. Open the glTF-CSharp-Loader\CSharp.sln
5+
5. 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).
6+
6. Build the Generator project.
7+
7. Build the gltfLoader project.
8+
8. Copy the resulting glTF-CSharp-Loader\glTFLoader\bin\Debug\netstandard1.3\glTFLoader.dll to this directory.
Lines changed: 13 additions & 0 deletions
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.
Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
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 e.g. display the Khronos glTF-Sample-Models for testing purposes.
29+
/// </summary>
30+
public class ConsistGenerator
31+
{
32+
/// <summary>
33+
/// 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.
34+
/// </summary>
35+
public static bool GltfVisualTestRun;
36+
37+
const string ConsistTemplateStart = @"Train (
38+
TrainCfg ( ""trainname""
39+
Name ( ""trainname"" )
40+
MaxVelocity ( 50 1 )
41+
";
42+
const string ConsistTemplateRecord = @"Engine ( UID ( xx ) EngineData ( ""engfile"" ""trainsetdir"" ) )" + "\r\n";
43+
const string ConsistTemplateEnd = ")\r\n)";
44+
const string EngineTemplate = @"Wagon ( ""enginname""
45+
Type ( Engine )
46+
WagonShape ( ""shapefilename"" )
47+
Size (100m 7m 100m )
48+
)
49+
Engine ( ""enginname""
50+
Wagon ( ""enginname"" )
51+
Type ( Electric )
52+
CabView ( dummy.cvf )
53+
Name ( ""enginname"" )
54+
)";
55+
56+
static readonly Dictionary<string, string> Wagons = new Dictionary<string, string>();
57+
static readonly Dictionary<string, string> SubDirectories = new Dictionary<string, string>
58+
{
59+
{ "glTF-Sample-Models", "glTF"},
60+
{ "glTF-Sample-Models-Binary", "glTF-Binary"},
61+
{ "glTF-Sample-Models-Embedded", "glTF-Embedded"},
62+
{ "glTF-Sample-Models-Draco", "glTF-Draco"},
63+
{ "glTF-Sample-Models-Quantized", "glTF-Quantized"},
64+
};
65+
66+
static string GltfExtension(string keyword) => keyword == "glTF-Sample-Models-Binary" ? ".glb" : ".gltf";
67+
static string RequestedType(string requestedPath) => SubDirectories.FirstOrDefault(ext => ext.Key == Path.GetFileNameWithoutExtension(requestedPath).Split('#', '&').FirstOrDefault()).Key;
68+
69+
public static bool IsConsistRecognized(string requestedPath) => RequestedType(requestedPath) != null;
70+
public static bool IsWagonRecognized(string requestedPath) => Wagons.ContainsKey(Path.GetFileName(requestedPath));
71+
72+
/// <summary>
73+
/// Provides a procedurally generated consist of the requested type of models
74+
/// </summary>
75+
/// <param name="requestedPath"></param>
76+
/// <returns></returns>
77+
public static Stream GetConsist(string requestedPath)
78+
{
79+
var trainsetDir = "glTF-Sample-Models";
80+
var baseDir = Path.Combine(Path.GetDirectoryName(Path.GetDirectoryName(requestedPath)), "TRAINSET", trainsetDir);
81+
if (!Directory.Exists(baseDir))
82+
{
83+
Console.WriteLine($"Cannot find the trainset directory {trainsetDir}.");
84+
return Stream.Null;
85+
}
86+
87+
var keyword = RequestedType(requestedPath);
88+
Debug.Assert(keyword != null);
89+
90+
var consist = ConsistTemplateStart.Replace("trainname", keyword);
91+
92+
if (keyword.StartsWith("glTF"))
93+
{
94+
GltfVisualTestRun = true;
95+
96+
var models = Directory.EnumerateFileSystemEntries(baseDir, "*.*", SearchOption.AllDirectories)
97+
.Where(f => !f.Contains(Path.Combine(baseDir, "2.0")) && !f.Contains(Path.Combine(baseDir, "1.0")) && (f.EndsWith(".gltf") || f.EndsWith(".glb")))
98+
.Select(f => Path.GetFileNameWithoutExtension(f))
99+
.Concat(Directory.EnumerateDirectories(Path.Combine(baseDir, "2.0")))
100+
.Where((m, n) => Path.GetFileName(m) == (Path.GetFileNameWithoutExtension(requestedPath).Split('&').ElementAtOrDefault(1) ?? Path.GetFileName(m)) &&
101+
n == (int.TryParse(Path.GetFileNameWithoutExtension(requestedPath).Split('#').ElementAtOrDefault(1), out var requestedModelNumber) ? requestedModelNumber : n));
102+
103+
var uid = 0;
104+
foreach (var model in models)
105+
{
106+
var dir = Path.Combine(model, SubDirectories[keyword]);
107+
var file = (Directory.Exists(dir)
108+
? Directory.EnumerateFiles(dir).FirstOrDefault(f => f.EndsWith(GltfExtension(keyword)))
109+
: Directory.EnumerateFiles(baseDir).FirstOrDefault(f => f.Contains(model)))
110+
?.Substring(baseDir.Length + 1)
111+
?.Replace(@"\", "/");
112+
113+
if (file == null)
114+
{
115+
// When gltf files got dropped into the sample directory, show that ones too.
116+
file = Directory.GetFiles(baseDir, Path.GetFileNameWithoutExtension(model) + "*.gl*", SearchOption.AllDirectories)
117+
.Where(f => !f.Contains(Path.Combine(baseDir, "2.0")) && !f.Contains(Path.Combine(baseDir, "1.0")) && (f.EndsWith(".gltf") || f.EndsWith(".glb")))
118+
.FirstOrDefault()
119+
?.Substring(baseDir.Length + 1)
120+
?.Replace(@"\", "/");
121+
if (file == null)
122+
continue;
123+
}
124+
125+
var eng = $"{keyword}_{Path.GetFileNameWithoutExtension(file)}.eng";
126+
Wagons.Add(eng, EngineTemplate
127+
.Replace("shapefilename", file)
128+
.Replace("enginname", Path.GetFileNameWithoutExtension(file)));
129+
130+
consist += ConsistTemplateRecord
131+
.Replace("xx", uid.ToString())
132+
.Replace("engfile", Path.GetFileNameWithoutExtension(eng))
133+
.Replace("trainsetdir", trainsetDir);
134+
uid++;
135+
}
136+
}
137+
consist += ConsistTemplateEnd;
138+
139+
var stream = new MemoryStream();
140+
using (var writer = new StreamWriter(stream, Encoding.UTF8, Encoding.UTF8.GetByteCount(consist), true))
141+
{
142+
writer.Write(consist);
143+
writer.Flush();
144+
}
145+
stream.Position = 0;
146+
return stream;
147+
}
148+
149+
public static Stream GetWagon(string requestedEngine)
150+
{
151+
if (!Wagons.ContainsKey(Path.GetFileName(requestedEngine)))
152+
{
153+
Console.WriteLine($"Cannot find the requested procedurally generated engine {requestedEngine}.");
154+
return Stream.Null;
155+
}
156+
157+
var wagon = Wagons[Path.GetFileName(requestedEngine)];
158+
var stream = new MemoryStream();
159+
using (var writer = new StreamWriter(stream, Encoding.UTF8, Encoding.UTF8.GetByteCount(wagon), true))
160+
{
161+
writer.Write(wagon);
162+
writer.Flush();
163+
}
164+
stream.Position = 0;
165+
return stream;
166+
}
167+
}
168+
}

Source/ORTS.Settings/UserSettings.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,10 @@ public enum DirectXFeature
205205
public int DayAmbientLight { get; set; }
206206
[Default(AntiAliasingMethod.MSAA2x)]
207207
public int AntiAliasing { get; set; }
208+
[Default(false)]
209+
public bool GltfAnimations { get; set; }
210+
[Default(true)]
211+
public bool GltfTangentsAlwaysCalculatedPerPixel { get; set; }
208212

209213
// Simulation settings:
210214

Source/Orts.Formats.Msts/ConsistFile.cs

Lines changed: 11 additions & 0 deletions
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.Simulation/Simulation/RollingStocks/MSTSLocomotive.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4872,6 +4872,7 @@ public void ToggleCabRadio( bool newState)
48724872
public void ToggleWipers(bool newState)
48734873
{
48744874
SignalEvent(newState ? Event.WiperOn : Event.WiperOff);
4875+
if (ConsistGenerator.GltfVisualTestRun) Train.SignalEvent(newState ? Event.WiperOn : Event.WiperOff);
48754876
}
48764877

48774878
public void SetBailOff(bool bailOff)
@@ -4939,12 +4940,14 @@ public void GenericItem1Toggle()
49394940
{
49404941
GenericItem1 = !GenericItem1;
49414942
SignalEvent(GenericItem1? Event.GenericItem1On : Event.GenericItem1Off); // hook for sound trigger
4943+
if (ConsistGenerator.GltfVisualTestRun) Train.SignalEvent(GenericItem1 ? Event.GenericItem1On : Event.GenericItem1Off);
49424944
}
49434945

49444946
public void GenericItem2Toggle()
49454947
{
49464948
GenericItem2 = !GenericItem2;
49474949
SignalEvent(GenericItem2 ? Event.GenericItem2On : Event.GenericItem2Off); // hook for sound trigger
4950+
if (ConsistGenerator.GltfVisualTestRun) Train.SignalEvent(GenericItem2 ? Event.GenericItem2On : Event.GenericItem2Off);
49484951
}
49494952

49504953
public override bool GetCabFlipped()
@@ -5022,6 +5025,10 @@ public override void SignalEvent(Event evt)
50225025
}
50235026
break;
50245027
}
5028+
case Event.GenericItem1On: if (ConsistGenerator.GltfVisualTestRun) GenericItem1 = true; break;
5029+
case Event.GenericItem1Off: if (ConsistGenerator.GltfVisualTestRun) GenericItem1 = false; break;
5030+
case Event.GenericItem2On: if (ConsistGenerator.GltfVisualTestRun) GenericItem2 = true; break;
5031+
case Event.GenericItem2Off: if (ConsistGenerator.GltfVisualTestRun) GenericItem2 = false; break;
50255032
}
50265033

50275034
base.SignalEvent(evt);

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -399,7 +399,9 @@ public virtual void LoadFromWagFile(string wagFilePath)
399399
if (File.Exists(orFile))
400400
wagFilePath = orFile;
401401

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

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

Lines changed: 4 additions & 1 deletion
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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1252,7 +1252,7 @@ private Train InitializePlayerTrain()
12521252
// place rear of train on starting location of aiPath.
12531253
train.RearTDBTraveller = new Traveller(TSectionDat, TDB.TrackDB.TrackNodes, aiPath);
12541254

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

@@ -1269,7 +1269,7 @@ private Train InitializePlayerTrain()
12691269
wagonFilePath = wagonFolder + @"\" + wagon.Name + ".eot";
12701270
}
12711271

1272-
if (!File.Exists(wagonFilePath))
1272+
if (!File.Exists(wagonFilePath) && !ConsistGenerator.IsWagonRecognized(wagonFilePath))
12731273
{
12741274
// First wagon is the player's loco and required, so issue a fatal error message
12751275
if (wagon == conFile.Train.TrainCfg.WagonList[0])
16.2 KB
Loading
Loading
Loading
Loading
Loading
Loading
Loading
Lines changed: 3 additions & 0 deletions
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)