Skip to content

Commit 94ca263

Browse files
authored
Merge pull request #369 from cjakeman/external-clocks
Copies ExtClock from OR New Year MG
2 parents 56c0f02 + aa85fbb commit 94ca263

File tree

16 files changed

+671
-41
lines changed

16 files changed

+671
-41
lines changed
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
// COPYRIGHT 2016 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 Newtonsoft.Json;
19+
using Orts.Formats.Msts;
20+
using Orts.Formats.OR;
21+
using Orts.Parsers.Msts;
22+
using System;
23+
using System.Collections.Generic;
24+
using System.IO;
25+
using System.Linq;
26+
using System.Text;
27+
using System.Xml.Linq;
28+
29+
namespace Orts.DataConverter
30+
{
31+
class ClockConverter : IDataConverter
32+
{
33+
public ClockConverter()
34+
{
35+
}
36+
37+
public void ShowConversions()
38+
{
39+
// "1234567890123456789012345678901234567890123456789012345678901234567890123456789"
40+
// " Input Output Description"
41+
Console.WriteLine("openrails/clocks.dat animated.clocks-or Converts from STF to JSON");
42+
}
43+
44+
/// <summary>
45+
/// Not clear what happens if conversion fails.
46+
/// </summary>
47+
/// <param name="conversion"></param>
48+
/// <returns></returns>
49+
public bool DoConversion(DataConversion conversion)
50+
{
51+
// We can convert from .dat files.
52+
if (conversion.Input.EndsWith(@"openrails\clocks.dat") == false)
53+
{
54+
return false;
55+
}
56+
// We can convert to .clocks-or files.
57+
if (conversion.Output.Any(r => r.EndsWith("clocks-or")) == false
58+
|| conversion.Output.Count != 1)
59+
{
60+
return false;
61+
}
62+
if (File.Exists(conversion.Input) == false)
63+
{
64+
throw new FileNotFoundException("", conversion.Input);
65+
}
66+
67+
var clockLists = new List<ClockList>();
68+
try
69+
{
70+
var inFilePath = Path.GetFullPath(conversion.Input);
71+
72+
// Shorten the path to get the route
73+
var routeLength = inFilePath.Length - @"openrails\clocks.dat".Length;
74+
var routePath = inFilePath.Substring(0, routeLength);
75+
76+
clockLists = ParseInput(conversion, routePath);
77+
}
78+
catch (Orts.Parsers.Msts.STFException e)
79+
{
80+
Console.WriteLine("Parse error in " + conversion.Input + e.Message);
81+
throw; // re-throw exception without losing the stack trace
82+
}
83+
84+
var jso = new List<ClockShape>();
85+
86+
try
87+
{
88+
GenerateOutput(conversion, clockLists, jso);
89+
}
90+
catch (Exception e)
91+
{
92+
Console.WriteLine("Error generating " + conversion.Output + e.Message);
93+
throw; // re-throw exception without losing the stack trace
94+
}
95+
96+
return true;
97+
}
98+
99+
private static List<ClockList> ParseInput(DataConversion conversion, string routePath)
100+
{
101+
var clockLists = new List<ClockList>();
102+
{
103+
using (STFReader stf = new STFReader(conversion.Input, false))
104+
{
105+
var clockBlock = new ClockBlock(stf, routePath + @"shapes\", clockLists, "Default");
106+
}
107+
}
108+
return clockLists;
109+
}
110+
111+
private static void GenerateOutput(DataConversion conversion, List<ClockList> clockLists, List<ClockShape> jso)
112+
{
113+
var clockList = clockLists.First();
114+
for (var i = 0; i < clockList.ClockType.Count(); i++)
115+
{
116+
var item = new ClockShape(clockList.ShapeNames[i], clockList.ClockType[i]);
117+
jso.Add(item);
118+
}
119+
120+
File.WriteAllText(conversion.Output.First(), JsonConvert.SerializeObject(jso, Formatting.Indented));
121+
}
122+
}
123+
}

Source/Contrib/DataConverter/DataConverter.csproj

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<?xml version="1.0" encoding="utf-8"?>
1+
<?xml version="1.0" encoding="utf-8"?>
22
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
33
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
44
<PropertyGroup>
@@ -40,20 +40,33 @@
4040
<LangVersion>7.3</LangVersion>
4141
</PropertyGroup>
4242
<ItemGroup>
43+
<Reference Include="Newtonsoft.Json, Version=11.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
44+
<SpecificVersion>False</SpecificVersion>
45+
<HintPath>..\..\3rdPartyLibs\Newtonsoft.Json.dll</HintPath>
46+
</Reference>
4347
<Reference Include="System" />
4448
<Reference Include="System.Xml" />
4549
<Reference Include="System.Xml.Linq" />
4650
</ItemGroup>
4751
<ItemGroup>
4852
<Compile Include="Program.cs" />
4953
<Compile Include="Properties\AssemblyInfo.cs" />
54+
<Compile Include="ClockConverter.cs" />
5055
<Compile Include="TerrainConverter.cs" />
5156
</ItemGroup>
5257
<ItemGroup>
5358
<ProjectReference Include="..\..\Orts.Formats.Msts\Orts.Formats.Msts.csproj">
5459
<Project>{570709FA-0C8A-4B1D-BA2D-D9455AFD9B5C}</Project>
5560
<Name>Orts.Formats.Msts</Name>
5661
</ProjectReference>
62+
<ProjectReference Include="..\..\Orts.Formats.OR\Orts.Formats.OR.csproj">
63+
<Project>{0d8d312b-2c02-4b77-a795-566394a9db95}</Project>
64+
<Name>Orts.Formats.OR</Name>
65+
</ProjectReference>
66+
<ProjectReference Include="..\..\Orts.Parsers.Msts\Orts.Parsers.Msts.csproj">
67+
<Project>{8a84696c-3559-49b8-b27f-bb6932d8a1c6}</Project>
68+
<Name>Orts.Parsers.Msts</Name>
69+
</ProjectReference>
5770
</ItemGroup>
5871
<ItemGroup>
5972
<None Include="app.config" />
@@ -66,4 +79,4 @@
6679
<Target Name="AfterBuild">
6780
</Target>
6881
-->
69-
</Project>
82+
</Project>

Source/Contrib/DataConverter/Program.cs

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,13 @@ namespace Orts.DataConverter
2525
{
2626
class Program
2727
{
28+
//TODO Add a return code for success or not
2829
static void Main(string[] args)
2930
{
3031
var converters = new List<IDataConverter>()
31-
{
32-
new TerrainConverter()
33-
};
32+
{ new TerrainConverter()
33+
, new ClockConverter()
34+
};
3435

3536
try
3637
{
@@ -44,6 +45,7 @@ static void Main(string[] args)
4445
foreach (var conversion in conversions)
4546
{
4647
var valid = false;
48+
// Apply each converter in turn to the conversion
4749
foreach (var converter in converters)
4850
{
4951
if (converter.DoConversion(conversion))
@@ -54,6 +56,7 @@ static void Main(string[] args)
5456
{
5557
Console.WriteLine("--> {0}", output);
5658
}
59+
// After a successful conversion, do not apply any more converters.
5760
break;
5861
}
5962
}
@@ -74,6 +77,10 @@ static void Main(string[] args)
7477
Console.WriteLine();
7578
ShowHelp(converters);
7679
}
80+
catch (Orts.Parsers.Msts.STFException)
81+
{
82+
// End with failure
83+
}
7784
}
7885

7986
static void ShowHelp(List<IDataConverter> converters)
@@ -89,7 +96,7 @@ static void ShowHelp(List<IDataConverter> converters)
8996
Console.WriteLine(" Multiple outputs may be specified for each input.");
9097
Console.WriteLine();
9198
Console.WriteLine(" Available file format conversions");
92-
Console.WriteLine(" Input Output Description");
99+
Console.WriteLine(" Input Output Description");
93100
foreach (var converter in converters)
94101
{
95102
converter.ShowConversions();
@@ -113,7 +120,7 @@ static List<DataConversion> GetConversions(string[] args)
113120
{
114121
throw new InvalidCommandLineException("Expected input filename; got option " + args[i]);
115122
}
116-
conversions.Add(new DataConversion(args[i]));
123+
conversions.Add(new DataConversion(args[i].ToLowerInvariant()));
117124
break;
118125
case "/output":
119126
if (conversions.Count == 0)
@@ -139,7 +146,7 @@ static List<DataConversion> GetConversions(string[] args)
139146
{
140147
throw new InvalidCommandLineException("Expected output filename; got option " + args[i]);
141148
}
142-
conversions.Last().Output.Add(args[i]);
149+
conversions.Last().Output.Add(args[i].ToLowerInvariant());
143150
break;
144151
}
145152
}

Source/Contrib/DataConverter/TerrainConverter.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,9 @@ public TerrainConverter()
3434
public void ShowConversions()
3535
{
3636
// "1234567890123456789012345678901234567890123456789012345678901234567890123456789"
37-
// " Input Output Description"
38-
Console.WriteLine(" t dae Creates a set of COLLADA files for the tile's terrain.");
39-
Console.WriteLine(" w dae Creates a set of COLLADA files for the tile's terrain.");
37+
// " Input Output Description"
38+
Console.WriteLine(" *.t *.dae Creates a set of COLLADA files for the tile's terrain.");
39+
Console.WriteLine(" *.w *.dae Creates a set of COLLADA files for the tile's terrain.");
4040
}
4141

4242
public bool DoConversion(DataConversion conversion)

Source/Documentation/Manual/features-route.rst

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -804,3 +804,124 @@ property::
804804

805805
The value is the fade time in seconds. Use ``0`` to disable the effect
806806
completely.
807+
808+
Animated clocks
809+
===============
810+
811+
.. image:: images/features-animated-clock4.png
812+
813+
Animated clocks that show the simulation time can be added or retro-fitted to a route.
814+
The clocks can have a second-hand that ticks each second, or one that moves smoothly or none at all.
815+
Typically clocks could be station clocks, church tower clocks or clocks at other public buildings.
816+
They are placed as normal static shapes in a route, similar to other shapes such as houses or trees.
817+
818+
Note: Loco cabs already have provision for both analogue and digital clocks.
819+
820+
Overview
821+
--------
822+
823+
You will need:
824+
825+
#. Shape and Texture Files
826+
827+
A shape file which defines each shape of clock, its hands and their animation and the texture files used by the shape.
828+
829+
#. Reference File
830+
831+
For each shape of clock in the route, a reference to the shape file in the reference file.
832+
833+
#. Animated Clocks List File
834+
835+
A file listing the shape file for each clock that Open Rails is to animate.
836+
837+
#. World File
838+
839+
The location of each clock in the world must be given in the world file.
840+
841+
Details
842+
-------
843+
844+
#. Shape and Texture Files
845+
846+
Create a clock just like any other shape. The hands of the clock must be sub-objects within the shape.
847+
They must have specific names and an animation.
848+
849+
Open Rails looks for the following names of clock hands in the shape file and animates them according to the simulation time.
850+
851+
The names for the clock hands must start with:
852+
853+
- "ORTS_HHand_Clock" for the hour hand
854+
- "ORTS_MHand_Clock" for the minute hand
855+
- "ORTS_SHand_Clock" for the second hand
856+
- "ORTS_CHand_Clock" for the centi-second hand
857+
858+
This last is used to provide a smooth movement in hundredths of a second whereas the second hand ticks forward once a second.
859+
It is suggested to use either the second hand or the centisecond hand or neither.
860+
861+
.. image:: images/features-animated-clock5.png
862+
863+
If a clock is to have several hands of the same type, simply append a number to the names of the hands, like this:
864+
865+
.. image:: images/features-animated-clock6.png
866+
867+
The animation requires 4 key frames at the 12, 3, 6 and 9 positions and calculates the intermediate
868+
positions using linear interpolation.
869+
870+
.. image:: images/features-animated-clock3.png
871+
872+
For example: ::
873+
874+
anim_node ORTS_HHand_Clock01 (
875+
controllers ( 1
876+
tcb_rot ( 5
877+
slerp_rot ( 0 0 0 0 -1 )
878+
slerp_rot ( 1 -0.707 0 0 -0.707 )
879+
slerp_rot ( 2 -1 0 0 0 )
880+
slerp_rot ( 3 -0.707 0 0 0.707 )
881+
slerp_rot ( 4 0 0 0 1 )
882+
)
883+
)
884+
)
885+
886+
Finally, move the clock shape and its textures into the corresponding folders SHAPES and TEXTURES of your route,
887+
such as ROUTES\\<route_name>\\SHAPES\\clocks.s
888+
889+
#. Reference File
890+
891+
Add a reference to the shape file into the reference file ROUTES\\<route_name>\\<route_name>.ref
892+
Make sure that this reference begins with the "Static" keyword.::
893+
894+
Static (
895+
Filename ( "ChurchClock.s" )
896+
Class ( "Clocks" )
897+
Align ( None )
898+
Description ( "ChurchClock" )
899+
)
900+
901+
#. Animated Clocks List File
902+
903+
Create a file ROUTES\\<route_name>\\animated.clocks-or file the file for each shape of clock that Open Rails will animate.
904+
The type parameter is always "analog" as "digital" types are not yet supported.::
905+
906+
[
907+
{
908+
"Name": "Clock01.s",
909+
"ClockType": "analog"
910+
},
911+
{
912+
"Name": "ChurchClock.s",
913+
"ClockType": "analog"
914+
},
915+
{
916+
"Name": "PlatformClock.s",
917+
"ClockType": "analog"
918+
}
919+
]
920+
921+
#. World File
922+
923+
Use a route editor to locate the clocks in the world file.
924+
925+
Note: Do not insert the shapes as animated ones.
926+
Otherwise, if MSTS is used to view the route then the hands of the clock will rotate wildly.
927+
In Open Rails they will match the simulation time anyway.
297 KB
Loading
44.9 KB
Loading
78.2 KB
Loading
74.2 KB
Loading

0 commit comments

Comments
 (0)