A library that provides experimental operations for modifying existing MSTS/ORTS shape files.
Project description
shapeedit
This Python module provides a wrapper around the shape data structure from shapeio, offering operations for modifying existing MSTS and ORTS shape files.
Depending on how the module is used, you may end up with unusual-looking shapes; however, the implemented operations ensure that shapes remain error-free and usable in MSTS and Open Rails.
At this stage, only a limited set of operations is implemented. If you need additional functionality or have any other suggestions, you are welcome to request them by creating an issue.
List of companion modules:
- shapeio - offers functions to convert shapes between structured text format and Python objects.
- trackshape-utils - offers additional utilities for working with track shapes.
- pyffeditc - handles compression and decompression of shape files through the
ffeditc_unicode.exeutility found in MSTS installations. - pytkutils - handles compression and decompression of shape files through the
TK.MSTS.Tokens.dlllibrary by Okrasa Ghia.
Installation
Install from PyPI
pip install --upgrade shapeedit
Install from wheel
If you have downloaded a .whl file from the Releases page, install it with:
pip install path/to/shapeedit-<version>‑py3‑none‑any.whl
Replace <version> with the actual version number in the filename.
Install from source
git clone https://github.com/pgroenbaek/shapeedit.git
pip install --upgrade ./shapeedit
Capabilities
The exact capabilities of this Python module might not be immediately obvious, so here's an overview with some examples and screenshots.
You can perform pretty cool edits on existing shapes using this set of tools.
Modification of existing geometry
Existing geometry within shapes can be modified. Vertex positions, texture coordinates, and normal vectors are all adjustable.
The original shapes and textures below are by Norbert Rieger. In the first example, the LZB cable is moved below the trackbed, and texture images are swapped to convert DB1s track sections into DB1fb.
In the second example, trackbed vertices are repositioned to mimic the concrete slabs of the V4hs_RKL track sections. Here, texture images are also swapped.
Addition of new geometry
Brand-new geometry, i.e., new vertices and triangles, can also be inserted into existing shapes.
In this example, the original NR_Emb shapes are by Norbert Rieger. The square railheads that match the old XTracks track system are extended with new geometry and folded up to match Eric's newer ATracks shapes.
This particular example is somewhat more advanced compared to the others, as the script quite literally weaves new geometry into both curved and straight track sections from stratch.
The edited NR_Emb shapes with ATracks rails are available for download at trainsim.com.
Removal of geometry
Triangles connecting existing vertices can also be removed, effetively making geometry no longer visible even though the vertices technically remain.
In this example, the side mirrors of the Nohab MY locomotive model by Pál Tamás are removed.
Transferring geometry
Geometry can also be transferred from one shape to another.
In this example, the overhead wires are copied from one of Norbert Rieger's DblSlip7_5d shapes onto the animated DblSlip7_5d switches by Laci1959.
The edited DblSlip7_5d shapes are available for download at the-train.de.
Addition of new textures, primitives or sub-objects
At present, operations that add new textures, primitives, or sub-objects are not implemented in this module. The goal is to eventually support those capabilites.
You can attempt such modifications manually through shapeio, as the entire shape data structure can be accessed and modified using that module.
Just keep in mind that you are not going to have the abstraction layer provided in this module. The one that makes things easy and keeps the shape error-free.
Usage
Before using this Python module, it is important to understand the basic structure of how geometry is stored within a shape.
The overall structure consists of six elements. These are all accessible through the API of the module:
- LOD Control: The top-level element, which contains one or more distance levels. Usually, there is only one of these per shape.
- Distance Level: Defines a distance level in meters. All sub-objects contained within it are visible when the shape is viewed within that range in the simulator. Typically, shapes have one or more of these, with the father distance levels containing less detailed geometry than those closer to the shape.
- Sub Object: Defines a sub-part of the overall shape at a given distance level. Typically, there are one or more of these within each distance level. Each sub-object contains a list of vertices and primitives.
- Vertex: Defines a point within the shape geometry. Each vertex also references a texture coordinate (UV) and a normal vector used for lighting calculations.
- Primitive: A collection of triangles. Typically, one or more are defined, and each primitive is associated with a primstate and a matrix. Primstates define which lighting configurations and which texture gets applied to the primitive's faces. Matrices define how the internal coordinate system within the shape is transformed into world-space coordinates when the shape is rendered.
- Triangle: A set of three vertices that forms a triangle within the shape geometry. Each triangle also references a normal vector that determine its facing direction.
The basic structure of these elements is as follows:
LOD Controls
└── Distance Levels
└── Sub Objects
├── Vertices
└── Primitives
└── Triangles
In the module's API, vertices that are associated with a specific primitive or triangle can be accessed through those elements for convenience.
You will need to inspect the shape manually in a text editor to determine the exact distance level values, sub-object indexes, and primstate names/indexes to use.
Modification of existing geometry
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")
Addition of new vertices and triangles
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")
Removal of triangles
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")
Running Tests
You can run tests manually or use tox to test across multiple Python versions.
Run Tests Manually
First, install the required dependencies:
pip install pytest
Then, run tests with:
pytest
Run Tests with tox
First, install the required dependencies:
pip install tox
Then, run tests with:
tox
This will execute tests for all Python versions specified in tox.ini.
Roadmap
Possible future features to be added:
- Ability to add new textures and primitives
- Ability to remove vertices from primitives
- Ability to edit things like light configurations, animations, etc.
- And possibly more..
Please make an issue if you have any good ideas, or if you need something that has not yet been implemented.
Pull requests are also welcome.
Contributing
Contributions of all kinds are welcome. These could be suggestions, issues, bug fixes, documentation improvements, or new features.
For more details see the contribution guidelines.
License
This Python module was created by Peter Grønbæk Andersen and is licensed under GNU GPL v3.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file shapeedit-0.5.0b3.tar.gz.
File metadata
- Download URL: shapeedit-0.5.0b3.tar.gz
- Upload date:
- Size: 42.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
89ff6bc8353e926bf92b8a2c57480c9fbbc74cadef8f3c3ac6cbe3b9008a2f4b
|
|
| MD5 |
a22a6fedbf2fcec67b1060c64d1b88ab
|
|
| BLAKE2b-256 |
49ecfe88daa1585f2ea4dfe3187a04107722409bcf98cda228d6ae4163be25ef
|
File details
Details for the file shapeedit-0.5.0b3-py3-none-any.whl.
File metadata
- Download URL: shapeedit-0.5.0b3-py3-none-any.whl
- Upload date:
- Size: 61.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
63a66b33059f15f886fa40578facd3070d32f370680bc390fbe42f5394ece085
|
|
| MD5 |
e97ca1d4744bd2f1faf689e72251c14a
|
|
| BLAKE2b-256 |
648bc93be77af9fd8afe0bd319d7ee2f9241327bbbbf5b93ed5552a34f14b9a7
|