This post is somewhat lengthy and technical, but it is worth a read. Using these Python modules that I created, you can accomplish tasks that would otherwise be practically impossible to do by hand - the type of things that might make people go, “What in the ... kind of sorcery is this?”
For example (the Python script that did this):
V4hs1t_RKL_Curve.png
As far as I’m aware, there are no other tools available that can do what these modules are capable of, other than performing manual edits using a text editor. The closest neighboring tools might be ShapeConverter and TSFV - and those aren’t really editing tools, but rather utilities that can, to some extent, convert shapes back into 3D modeling formats.
With these modules, all editing takes place directly in the raw .s file, so using them does not cause the loss of things like texture coordinates or animations. There is no 3D modeling software involved; everything is done purely through programmatic access to the shape’s data structure.
This thread is a continuation of my shape-editing adventures that started here:
https://www.trainsim.com/forums/foru...es-with-python
From those experiments, I have created this set of Python modules with far more advanced editing capabilities than just moving points around, as I was doing at the beginning of that thread.
These are:
- ShapeIO - for reading and writing uncompressed shape files.
- ShapeEdit - a wrapper around the shape data structure from ShapeIO that provides advanced, easy-to-use editing capabilities.
- TrackshapeUtils - additional utilities that make it easy to work with track shapes.
- pyFFEDITC - a wrapper around ffeditc_unicode.exe so it can be used easily from within Python scripts (necessary since ShapeIO is currently unable to deal with compressed shapes).
ShapeIO loads the entire shape data structure as Python objects, so in principle you can edit anything and then save the modified shape. However, because the shape data structure contains many invariants, it’s very easy to break things if you don’t know exactly what you’re doing.
That’s where ShapeEdit comes in. It’s a DSL-like wrapper around the shape data structure that allows you to easily access all of the shape’s geometry. It also provides operations that won’t break the shape to the point where it can’t load in Open Rails. Things might end up looking really wonky depending on what you do, but the shape will still load in the sim.
TrackshapeUtils includes additional functions that allow you to load track centers from a tsection.dat file and use them to calculate new vertex positions relative to the track center. It also provides operations such as calculating distances from the nearest track center and along the length of the track. This is quite useful for creating scripts that can handle both straight and curved track sections.
These modules can be installed from PyPI using pip:
You can then use the modules within Python scripts. I don’t expect breaking changes in the external APIs of the modules at this point. However, keep in mind that the modules are currently at version 0.5.0-beta2. It’s still early days, and it is possible for functionality to break due to unhandled edge-cases, particularly when loading shapes, but I think the modules are now stable enough to share and allow others to use them.
ShapeEdit currently provides the following operations:
- Easy access to the entire geometry structure, including LOD controls, distance levels, sub-objects, primitives, vertices, and triangles.
- Access to and modification of vertex data (points, UV coordinates, normals).
- Access to and modification of triangle data (face normals).
- Insertion of new vertices.
- Insertion of new triangles.
- Removal of specific triangles.
- Removal of all triangles connected to a vertex.
ShapeEdit does not yet support adding new textures, primitives, or sub-objects. However, this functionality is coming eventually, as I will need it to insert custom Danish switch stands on Norbert’s DBTracks switches.
Once I implement that functionality, you could, in principle, model something in Blender, export it to a new shape using the MSTS/ORTS exporter, and then transfer the newly modeled geometry into an existing shape.
Here are some examples of what has already been done using earlier versions of these tools:
- NR_Emb shapes with ATracks railheads
- Insertion of overhead wires onto Laci1959's animated DblSlip7_5d switches
I am in the process of making some customizations to DBTracks shapes here. And in doing so, I am also creating shapes that don’t exist in the original DBTracks packages. Xover7_5d's with overhead wires being one example. And who knows, maybe even the missing switches at one point (but no promises there, as the level of difficulty in doing that is quite high even with these modules).
Here are a couple more screenshots showing what’s possible through scripting with these Python modules. You can use them on any kind of shape - not just track sections, which is what I’ve mostly been working with so far.
NohabMyMirrors.png
This script made the above edit. Here the side mirrors are removed from the Nohab MY locomotive model by Pál Tamás.
NR_Emb_AT.png
This script made the above edit. This is probably the most advanced script I've made up until now. It shrinks the rail tops and then quite literally weaves new geometry into the rail sides to make the NR_Emb shapes fit Eric's ATracks. And it handles straight/curved track as well as parallel tracks correctly.
To give you an idea of how little scripting it takes to perform simple edits, here are some quick examples:
In this example, all vertex data within a sub-object is set to zero:
And here's how to add vertices/triangles:
And removal of triangles:
If you use the modules and have feedback, suggestions, questions, or encounter issues or bugs, feel free to post here or open issues on GitHub. Documentation and example scripts are also available on GitHub, and code contributions are also welcome there.
Have fun if you decide to use these modules for something! They are a powerful set of development tools, and you can do a lot of interesting things with them.
Edit: Updated some of the links to GitHub since I split the examples out into separate repositories
For example (the Python script that did this):
V4hs1t_RKL_Curve.png
As far as I’m aware, there are no other tools available that can do what these modules are capable of, other than performing manual edits using a text editor. The closest neighboring tools might be ShapeConverter and TSFV - and those aren’t really editing tools, but rather utilities that can, to some extent, convert shapes back into 3D modeling formats.
With these modules, all editing takes place directly in the raw .s file, so using them does not cause the loss of things like texture coordinates or animations. There is no 3D modeling software involved; everything is done purely through programmatic access to the shape’s data structure.
This thread is a continuation of my shape-editing adventures that started here:
https://www.trainsim.com/forums/foru...es-with-python
From those experiments, I have created this set of Python modules with far more advanced editing capabilities than just moving points around, as I was doing at the beginning of that thread.
These are:
- ShapeIO - for reading and writing uncompressed shape files.
- ShapeEdit - a wrapper around the shape data structure from ShapeIO that provides advanced, easy-to-use editing capabilities.
- TrackshapeUtils - additional utilities that make it easy to work with track shapes.
- pyFFEDITC - a wrapper around ffeditc_unicode.exe so it can be used easily from within Python scripts (necessary since ShapeIO is currently unable to deal with compressed shapes).
ShapeIO loads the entire shape data structure as Python objects, so in principle you can edit anything and then save the modified shape. However, because the shape data structure contains many invariants, it’s very easy to break things if you don’t know exactly what you’re doing.
That’s where ShapeEdit comes in. It’s a DSL-like wrapper around the shape data structure that allows you to easily access all of the shape’s geometry. It also provides operations that won’t break the shape to the point where it can’t load in Open Rails. Things might end up looking really wonky depending on what you do, but the shape will still load in the sim.
TrackshapeUtils includes additional functions that allow you to load track centers from a tsection.dat file and use them to calculate new vertex positions relative to the track center. It also provides operations such as calculating distances from the nearest track center and along the length of the track. This is quite useful for creating scripts that can handle both straight and curved track sections.
These modules can be installed from PyPI using pip:
Code:
pip install --upgrade shapeio pip install --upgrade shapeedit pip install --upgrade trackshapeutils pip install --upgrade pyffeditc
ShapeEdit currently provides the following operations:
- Easy access to the entire geometry structure, including LOD controls, distance levels, sub-objects, primitives, vertices, and triangles.
- Access to and modification of vertex data (points, UV coordinates, normals).
- Access to and modification of triangle data (face normals).
- Insertion of new vertices.
- Insertion of new triangles.
- Removal of specific triangles.
- Removal of all triangles connected to a vertex.
ShapeEdit does not yet support adding new textures, primitives, or sub-objects. However, this functionality is coming eventually, as I will need it to insert custom Danish switch stands on Norbert’s DBTracks switches.
Once I implement that functionality, you could, in principle, model something in Blender, export it to a new shape using the MSTS/ORTS exporter, and then transfer the newly modeled geometry into an existing shape.
Here are some examples of what has already been done using earlier versions of these tools:
- NR_Emb shapes with ATracks railheads
- Insertion of overhead wires onto Laci1959's animated DblSlip7_5d switches
I am in the process of making some customizations to DBTracks shapes here. And in doing so, I am also creating shapes that don’t exist in the original DBTracks packages. Xover7_5d's with overhead wires being one example. And who knows, maybe even the missing switches at one point (but no promises there, as the level of difficulty in doing that is quite high even with these modules).
Here are a couple more screenshots showing what’s possible through scripting with these Python modules. You can use them on any kind of shape - not just track sections, which is what I’ve mostly been working with so far.
NohabMyMirrors.png
This script made the above edit. Here the side mirrors are removed from the Nohab MY locomotive model by Pál Tamás.
NR_Emb_AT.png
This script made the above edit. This is probably the most advanced script I've made up until now. It shrinks the rail tops and then quite literally weaves new geometry into the rail sides to make the NR_Emb shapes fit Eric's ATracks. And it handles straight/curved track as well as parallel tracks correctly.
To give you an idea of how little scripting it takes to perform simple edits, here are some quick examples:
In this example, all vertex data within a sub-object is set to zero:
Code:
import shapeio
from shapeedit import ShapeEditor
my_shape = shapeio.load("./path/to/example.s")
shape_editor = ShapeEditor(my_shape)
sub_object = shape_editor.lod_control(0).distance_level(200).sub_object(0)
# Set point/uv_point/normal data of all vertices within the subobject to zero.
for vertex in sub_object.vertices():
vertex.point.x = 0.0
vertex.point.y = 0.0
vertex.point.z = 0.0
vertex.uv_point.u = 0.0
vertex.uv_point.v = 0.0
vertex.normal.x = 0.0
vertex.normal.y = 0.0
vertex.normal.z = 0.0
shapeio.dump(my_shape, "./path/to/output.s")
And here's how to add vertices/triangles:
Code:
import shapeio
from shapeio.shape import Point, UVPoint, Vector
from shapeedit import ShapeEditor
my_shape = shapeio.load("./path/to/example.s")
shape_editor = ShapeEditor(my_shape)
# Add three new vertices to primitives associated with prim_state_idx 22.
# Connect the three vertices added to each primitive with a triangle.
for lod_control in shape_editor.lod_controls():
for distance_level in lod_control.distance_levels():
for sub_object in distance_level.sub_objects():
for primitive in sub_object.primitives(prim_state_index=22):
new_point1 = Point(0.0, 0.0, 0.0)
new_point2 = Point(1.0, 0.0, 0.0)
new_point3 = Point(2.0, 0.0, 1.0)
new_uv_point = UVPoint(0.0, 0.0)
new_normal = Vector(0.0, 0.0, 0.0)
new_vertex1 = primitive.add_vertex(new_point1, new_uv_point, new_normal)
new_vertex2 = primitive.add_vertex(new_point2, new_uv_point, new_normal)
new_vertex3 = primitive.add_vertex(new_point3, new_uv_point, new_normal)
primitive.insert_triangle(new_vertex1, new_vertex2, new_vertex3)
shapeio.dump(my_shape, "./path/to/output.s")
And removal of triangles:
Code:
import shapeio
from shapeedit import ShapeEditor
my_shape = shapeio.load("./path/to/example.s")
shape_editor = ShapeEditor(my_shape)
# Remove all triangles from primitives associated with any prim_state named "Rails".
for lod_control in shape_editor.lod_controls():
for distance_level in lod_control.distance_levels():
for sub_object in distance_level.sub_objects():
for primitive in sub_object.primitives(prim_state_name="Rails"):
for vertex in primitive.vertices():
primitive.remove_triangles_connected_to(vertex)
shapeio.dump(my_shape, "./path/to/output.s")
If you use the modules and have feedback, suggestions, questions, or encounter issues or bugs, feel free to post here or open issues on GitHub. Documentation and example scripts are also available on GitHub, and code contributions are also welcome there.
Have fun if you decide to use these modules for something! They are a powerful set of development tools, and you can do a lot of interesting things with them.

Edit: Updated some of the links to GitHub since I split the examples out into separate repositories





Comment