17
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-10-17 20:19:20 +02:00
Commit Graph

78 Commits

Author SHA1 Message Date
maj00r
3876945ef3 Track what cooked data was made from, and cook it as a step of its own
A baked heightfield was trusted for as long as its file was readable, and the logic
container while the .scn was older than it: an edited include went unnoticed by both,
and node keys leaned on file dates, so merely touching a file changed them. And cooking
only ever happened as a side effect of starting a scenery.

Every cooked artifact now has a manifest beside it - <scenario>.ehf.deps,
<scenario>.logic.deps - listing each file it was made from with its size, modification
time and a hash of its content (simulation/cookdeps). The parser records what it
includes as it reads, so the list is exactly what the load read. An artifact is current
when every listed file still has its content: size and time are the quick check, and
when either differs the content is hashed, so a touched or copied file does not force a
rebuild and an edited one always does. A file added to the scenery cannot be in a
manifest; the files read are compared with it after the load, and a mismatch sends the
terrain to be baked on the next start - the new file's nodes, whose keys are unknown,
are drawn as geometry meanwhile - and the logic, cooked from the loaded state, is cooked
at once.

The terrain depends on what the scenario includes and not on the scenario file itself,
which the launcher rewrites on every start; the logic depends on both, since events can
be written in either, and costs milliseconds to cook again. With the manifest vouching
for file content, a triangle node's key is its file, line, include parameters and
placement, without the date.

-cook loads a scenery, brings whatever is stale up to date, and quits: the step that
prepares a scenery ahead of play. It closes the window directly, as there is no
simulation to wind down; a quit command waited a minute in the queue for one. Baking on
a first start stays as the fallback, and -nobaketerrain still turns it off.

Tried on a copy of baltyk with a terrain include of its own: a fresh cook bakes and
cooks everything, a second finds nothing to do, a touched file changes nothing, an
edited include is caught by its hash and both artifacts are rebuilt, and a new include
has the logic recooked at once and the terrain on the next run. A cooking run of baltyk
takes 5.5 s with nothing stale and 10.7 s with everything to do.
2026-09-17 17:45:23 +02:00
maj00r
d2a5f284e3 Draw terrain one call per level, from texture arrays, culled to the view
Each resident tile had a height texture and a material texture of its own, uploaded with
glTexImage2D, and was drawn once per material it held with every other material's
fragments discarded, all of them whether in view or not. Thousands of draws a frame.

Now the tiles of a level live in one pair of texture arrays (terrain_tile_pool), a layer
per tile. A pool starts at sixteen layers and doubles when full, copying what it holds on
the gpu, so a level takes only the memory its tiles need; uploads go through a pixel
buffer orphaned for each tile. The ground textures of every material are gathered into
one sRGB array with mipmaps (terrain_ground) - drawn into it layer by layer by a small
shader once the engine has each texture ready, since the scenery's textures are
compressed and cannot be blitted - with a table of placeholder colours and measured
tiling beside it.

Each frame the tiles in range are tested against the frustum by bounding sphere and
gathered per level into a buffer texture of instance data, and each level is drawn in
one instanced call. The fragment shader reads the four samples around it, drops the cell
when any of them carries no ground - exactly the rule the bake already assumed - and
blends the ground textures of their materials by distance, so material boundaries
follow the samples smoothly instead of stepping along triangle edges.

State the copy pass changes is captured and put back, and the texture unit cache is
reset after raw binds, so the rest of the renderer finds things as it left them.

On baltyk: all 30 materials land in the array, 663 of the 1120 tiles in range are drawn
after culling, and a frame is at most six terrain draws. The residency line in the log
now says how many tiles in range were drawn.
2026-09-17 16:28:43 +02:00
maj00r
be4d691ae2 Choose terrain levels from what the screen can resolve
Levels changed at fixed distances - 512 m, 1 km, 2 km and on - whatever the window, the
field of view or the zoom. Now a level is used out to the distance where one of its sample
spacings covers terraindetail pixels on screen, 3 by default, which follows from the
viewport height and the vertical field of view with the zoom applied; each level doubles
the spacing, so each doubles the distance. Zooming in or a taller window pushes detail
further, a coarser setting pulls it in. The finest range never drops below four tile
sides, which the morphing needs to close the seams.

Levels and morphing both follow the finest range the last scan used, so they always agree,
and a change of more than a percent - a zoom, a resize - triggers a rescan.
tools/terraincook/seamcheck.py now also tries finest ranges of 5.7 and 11.3 tiles, and
finds no gap in any of them.

terraindetail is in the ini and on a slider in the debug panel; the panel's level table
and the residency line in the log show the resulting ranges. On baltyk the current window
works out to 484 m, so the 512 m minimum applies and nothing changes by default; a
scenery setting terraindetail 1 got 1349 m, and the samples on the gpu went from 3 MB to
10 MB with it.
2026-09-17 16:11:41 +02:00
maj00r
37ab4a35b3 Morph terrain levels into each other so neighbours meet without cracks
Tiles drawn at the same level met exactly, but a finer tile beside a coarser one has
samples on their shared edge that the coarser one skips, and the ground between them
could open up. This closes the gap the standard way (CDLOD): each vertex slides towards
the next coarser level as its distance nears the end of its level's range, so that by the
end of the band a tile is, vertex for vertex, the coarser tile beyond it. Levels also stop
popping when they change.

The mip chain is point sampled, so the next level's grid is this level's even samples. An
odd vertex slides onto its even neighbour and takes its height, and the grid, whose
diagonals all run the same way at every level, collapses exactly into the coarser
triangulation. No second texture and no neighbour information is needed; the morph is
computed per vertex from its distance to the camera, the same distance the level is
chosen by.

Two things the geometry demands, both settled by tools/terraincook/seamcheck.py, which
walks random camera positions over 128 m, 256 m and 12.8 km tiles and counts every edge
vertex where the finer side has not finished morphing or the coarser side has started:

- the band ends a rescan's worth of travel (a quarter tile) short of the level's range.
  Levels are chosen where the camera stood at the last rescan, the morph follows it every
  frame, and a band ending at the range itself left gaps at every level boundary;
- the band is a fifth of the range wide, and level ranges are counted in tiles (four for
  the finest level) rather than fixed metres, which keeps the coarser tile's band clear of
  any edge a finer tile reaches, whatever the tile size.

The script finds no gap with these values. In the game on baltyk, a low view differs from
the unmorphed shader only in a strip of distant ground at the horizon, and nearer than
the first band the picture is identical.

The cooker's comment claimed tile seams were identical at every level; that held only
for neighbours at the same level, and now says so.
2026-09-17 15:58:05 +02:00
maj00r
2660d14565 Stream the scenery: a baked terrain heightfield, a logic container, and prefabs
Today's work on the binary scene format plan (binary-scene-format-plan.md), from the
first measurement to a terrain that bakes itself on first run and streams from a
thread. The step-by-step history is kept on backup/scene-streaming-format-presquash-0916.

Measuring first. -loadprofile times a scenery load by stage and by node kind and reports
node counts and resident memory. On l204 terrain is 95% of the bytes and 99.8% of the
nodes; on quarkmce2007 vehicles, not models, dominate what is left. The terrain turned
out to be a heightfield in all but name, a ribbon covering 12% of its bounding box.

The heightfield. scene/heightfieldformat.h is the .ehf layout: Morton-ordered tiles
of 128 samples with one shared edge sample, a point-sampled mip chain, zstd per tile,
a nodata sentinel, a material palette with a measured tiling per material, and a stamp
of the rules that decided what counted as terrain. scene/heightfieldreader.h reads it
for the tool, the renderer and the loader alike, and scene/terraincooker.h writes it,
shared by the terraincook tool and the engine so a first-run bake is the file the tool
would have made. The cooker rasterises every triangle where it is drawn (through
scene::place_in_world, the placement the region applies too), keeps an upper surface
laid within half a metre over another and the lower of two further apart, and then
asks each triangle whether the heightfield shows it: laid, reaching a sample, keeping
nearly all it claimed, and with the ground around it drawn at every level out to 2 km.

Baking on first run. simulation/terrainbake bakes <scenario>.ehf the first time a
scenery is opened, choosing the grid step from the first 200,000 vertices, and on later
loads leaves out the triangles its node table says the heightfield shows. Everything
else - trees, grass tufts, gantries, bridge decks - is drawn as written, so a bad bake
can at worst draw something twice, and the heightfield sits 2 cm under the triangles so
the original wins there. Nodes are keyed by file, line, include parameters and
placement; an edited file changes its keys and those nodes are drawn again. A bake under
older rules, or without its table, is thrown away and redone. A launcher copy named
$scenario shares the scenario's files. -nobaketerrain shows the triangles as written.
A scenery can still name hand-cooked fields, several at different steps.

Drawing and streaming. rendering/terrainclipmap draws resident tiles at the mip level
their distance earns, deriving the grid from gl_VertexID and texturing in world space
with the scenery's own materials. rendering/terraintileloader reads and decodes tiles on
a thread of its own; the renderer rescans what is in range only after moving a quarter
of a tile, asks for what is missing nearest first, uploads at most 32 tiles a frame,
drops arrivals the camera has moved past, and keeps drawing a tile at the level it has
until the right one arrives. The terrain has its own range, terrainrange in the ini and
a slider in the debug panel, 20 km by default and independent of basedrawrange; the far
plane of an ordinary view is pushed out to it. The debug panel shows the range, tiles in
view, on the gpu, wanted and queued, and how many sit at each level.

Events. The event queue moved out of the events into a binary heap with a deterministic
tie-break, and an event is handed its activator instead of storing it, so basic_event
is a definition and nothing else.

The logic container. scene/logicformat.h and scene/logiccontainer write a scenery's
events, their targets and sibling chains as flat, deterministic arrays with a schema
hash that refuses a mismatched build. simulation/logiccook cooks it from the loaded
state whenever it is missing, stale or unreadable, reads it straight back to check it,
and resolves each target to a position in the registry its event kind searches.
-cooklogic forces a cook, -verifylogic only checks. Nothing reads it at load yet.

Prefabs. scene/prefab describes a signal by the aspects it can show - lights, speeds,
animation channels, a sound - instead of pasting an .inc per variant, and expands an
instance to ordinary scenery text. Tried on a light signal and a shape signal.

Screenshots without a person present: -shot x,y,z[,yaw[,pitch]], -shotsettle, -shotstay.
2026-09-16 23:18:09 +02:00
maj00r
366645ef59 Plan tool: cofanie, złącza, przejścia rozjazdowe, and a demo to open
What the plan tool was missing was not geometry but the ordinary business of
editing: taking a mistake back, dividing what is drawn, and seeing what the next
click will do before making it.

Cofanie. The document is plain data, so a step is a copy of it. What counts as one
step is decided by watching the document - a fingerprint over the tracks, turnouts
and catalogue - rather than by dressing every edit with a call: a frame that
changes anything opens a step, and the step closes when the drawing settles. That
is what makes a drag, or a number typed digit by digit, one step and not thirty,
and nothing can be forgotten on the way in. Ctrl+Z / Ctrl+Y; the scene editor
listens for the same keys, so it hands them over while the plan has the keyboard.

Złącze i wycięcie. `insert_joint` divides whichever element a station lands in and
leaves one chain: the ground does not move, the curvature at the new joint is the
one that was always there, and the fit reads the chain the same way afterwards. It
is refused on a straight, because two straights end to end are one straight and the
skeleton will not read that, and under a turnout, which is one piece of geometry on
one element. `cut_track` takes a piece out and does leave two tracks - a gap is the
ground between them, not a hole in a chain.

Przejście rozjazdowe. A turnout may now stand against another one: where it stands
is then worked out from that one's frog, not authored, so moving either carries the
other with it and a trapez stays shut. The frog has to land on the line leaving the
other frog - not on the track's axis, which is 1,469 m further on for a 1:9 - so
the station is settled by laying the turnout and walking it along until it does.

Drawing is a tool now, not what a click happens to do: until Rysuj is on, a click
picks what is under it. With it on, the cursor at the growing end shows the corner
and the straight the next click would lay, worked out by the same fit_corner the
click itself uses. A chain only grows at a loose end, and the first element of a
track is put down by two clicks instead of landing on the plan's zero.

Also here: the curvature strip under the panel, pikietaż in the rows and at the
cursor, how far the laid axis runs from the clicks it was drawn from, the file
format saying which frame and which units it is in (v9), plans chosen from the
directory instead of typed, and editor/samples/demo.m0s - two parallel tracks and a
trapez - with the generator that wrote it and a test that opens it and demands it
lays clean.
2026-09-14 16:35:36 +02:00
maj00r
321c9dd7ab Editor: fold the plan library into the tree as modules, drop its own name
The track layout library came in as a subproject of its own -- its own
CMakeLists, its own install and export rules, its own include tree -- which only
made the build harder to follow now that everything else is a module. It is part
of the editor, so it lives where the editor lives.

- editor/plan_*.cppm / plan_*.cpp: the fifteen interface units and twelve
  implementation units, flat in editor/ beside editorTerrain and orthophoto, with
  the plan_ prefix keeping them clear of what was already there (editor/turnouts
  is the preset catalogue, so the geometry is plan_turnout_geometry).
- modules are named eu07.editor.plan_*, like every other module in the tree.
- namespaces move under the editor's own: editor::plan, editor::plan::geometry,
  ::io, ::render. The library's old name is gone from the sources and the docs.
- every moved file carries the MPL header the rest of the tree carries; it had
  none. version.hpp went with the subproject: macros only, and unused.
- the umbrella header is dropped -- nothing imported it.
- the unit tests survive the fold: editor/tests, behind EU07_EDITOR_PLAN_TESTS
  (off by default), compiles the interfaces a second time into a small library of
  its own, because a test can only import modules from a target it links and
  linking the game would drag the whole simulator in. The code under test needs
  nothing but a compiler. 7/7 pass.
- a switch may now be written with two dashes as well: --editor beside -editor.

Builds with clang 22 (-DWITH_BETTER_RENDERER=OFF); -editor and --editor both come
up in the editor on empty ground.
2026-09-13 13:31:01 +02:00
maj00r
c15e200e37 Merge the track layout editor into cpp20-modules, ported to modules
The editor branch was written against headers and the PCH, both of which this
branch has done away with, so its own files were ported rather than merged: the
five headers it added are now interface units and their .cpp files module
implementation units.

- editor/{puwg1992,polandmap,turnouts,orthophoto} become eu07.editor.* modules,
  application/planpanel becomes eu07.application.planpanel. The plan tool keeps
  to its own module rather than joining the eu07.simcore partitions: it stands on
  the editor's libraries and eu07.application.uilayer, not on the simulation core,
  so :editoruilayer can export import it without a cycle.
- glm arrives by import eu07.glm, never by include, as the rest of the tree does.
  maj0sted is header-only and glm-free, so its headers stay includes in the global
  module fragment - every unit that names those types includes them itself, since
  a global fragment is not visible across units.
- the one textual conflict was :editoruilayer's include block against this
  branch's imports; resolved this branch's way plus the planpanel import.

Builds with clang 22 (-DWITH_BETTER_RENDERER=OFF) and starts into the editor on
empty ground with -editor alone.
2026-09-13 12:36:21 +02:00
maj00r
b8f040e6fd propagate template headers transitively, import Console where used
AnimModel.cpp reaches cParser through the simulation core, not a direct
import, so the previous pass missed it; the propagation now follows the
whole import closure. Train.cpp, drivermode.cpp and Globals.cpp call
Console:: inside _WIN32 guards without importing eu07.console - invisible
on Linux, where those blocks are skipped.
2026-09-10 01:41:24 +02:00
maj00r
6550c88536 give importers the headers module templates need
A template defined in a module interface is instantiated in the importing
unit, where its body's types must be complete. A declaration reaching the
importer only through the interface's global module fragment is not
enough for MSVC: cParser::operator>> uses std::stringstream, and every
unit that parsed a float got an incomplete type.

526 headers across 161 units, derived from what each interface's template
bodies actually name.
2026-09-10 01:20:33 +02:00
maj00r
2695c8e525 include what you use in betterRenderer
Its sources reached the standard library through the project headers they
included; those are imports now. 178 headers across 56 files, plus 18 C
library headers elsewhere for memcpy/printf and friends. MSVC's STL does
not supply these transitively the way libstdc++ does.
2026-09-10 00:26:55 +02:00
maj00r
2896c2dd60 add the C library headers MSVC needs
Unqualified uint8_t/size_t and friends were reaching 24 units through
some other header on libstdc++; MSVC's STL does not do that, which broke
gl/ubo.cppm (and made its UBS_PAD macro look like garbage). Also
silences C5244, MSVC's counterpart to the clang warning about glm being
included in the module purview, which is deliberate here.
2026-09-10 00:10:52 +02:00
maj00r
ce5ee288ff fix the clang CI build
Every module unit now includes the standard headers it uses. Declarations
reaching a unit only through an imported module's global module fragment
are reachable but not visible, so ADL misses the free operators older
libstdc++ declares beside a type - reproduced on ubuntu 24.04 (libstdc++
13), where gl/vao.cpp could not compare vector iterators.

Also drops a stale editor/editorSettings.hpp from the MSVC source list,
which broke the Windows configure step, silences the deliberate
include-in-purview warning in the glm wrapper, and removes GCC support:
clang or MSVC only.
2026-09-09 22:52:28 +02:00
maj00r
f16b5deefb build with clang
Linux CI switches to clang 22; fixes what that exposed - export on
out-of-line member definitions, missing std includes, missing glm
imports. GCC 16 still builds.
2026-09-09 22:34:32 +02:00
maj00r
3f7960aad5 pch migration to modules [wip] 2026-09-09 20:35:26 +02:00
jakubg1
ba19db8715 Revert "Remove training card window"
This reverts commit 363b50c9c2.
2026-09-02 12:32:35 +02:00
maj00r
61336a0660 sonar fix, registration to sbt.sources improved 2026-08-30 17:14:08 +02:00
maj00r
2376e4867b Editor: rebuild the plan library around a solved document, and bend turnouts onto curves
The library was a heap of half-built services - fitting, parallelism, niwelety,
an SVG writer - that never met each other. In their place is one authored
Document and one solver that lays it whole: tracks as element chains, turnouts
placed on them, and a Solution that is rebuilt from scratch every time and never
fed back into the document. What cannot be laid comes back as a diagnostic with
the number in it rather than as silence or a fitted-away error.

Turnouts are dimensioned the way the drawings dimension them. The blade follows
Koc's rys. 5.9 - the curve is theoretically tangent to the opornica at PR, the
blade starts at a with a planed nose of d, and A is where the rail stands u
thick - and a + d derived that way lands on his tablica 5.1. Bending follows
5.3.6: it rotates the triangle OBC about the centre of the turnout, so the
tangent t and the crossing angle stand and both radii follow from them. That is
Koc's (5.7) and (5.8) without coding either, and it puts a symmetrically bent
Rz 49E1-190-1:9 on the catalogue's Rlus 49E1-380,292-1:9. A rozjazd standing on
a curve is bent onto it; one on a track drawn straight can carry a bend of its
own. The szyny laczace take up the difference, because the switch and the frog
are only bent, not re-cut.

The plan panel draws all of it: the catalogue drawing of a type with its
construction points, the theoretical arc dashed where no rail runs, and the
blade nose as the straight it is cut as. Dragging a straight now stops where it
would pull the track out from under a turnout.

File format goes to m0s 6. Versions 3 to 5 still open.
2026-08-29 19:32:46 +02:00
maj00r
8cd42980e6 Switches: lay the pre-blade lead and the blade, not a tangent arc
A turnout's diverging track does not leave the through track tangentially. It
runs straight along the through track to the blade tip (odcinek przediglicowy),
breaks away there by the blade angle (kąt nagięcia iglicy, beta), runs straight
over the blade to its heel, and only then curves - through alfa - beta, not the
whole crossing angle - to the frog.

Give TurnoutSegment a turn_in: a signed heading break applied before the segment
is laid, which is the only way to express a track leaving by an angle rather than
a tangent. lay_turnout then places the lead, the break and the blade ahead of the
curve and hands the curve the angle that is actually left to spend; a zero blade
length keeps the break and starts the curve at the tip. Reject a blade angle that
is negative or already reaches the crossing angle.

All-zero blade figures lay exactly the old tangent arc from PR, so existing
layouts are untouched. Junction and turnout_preset carry the three new figures
through to the panel; the presets themselves still hold zeros until the Id-1
catalogue numbers are in.

Add test_turnout: the lead sits on the through track, the blade breaks by beta,
and the frog still lands on the full crossing angle and the catalogue length,
mirrored for a right-hand turnout.
2026-08-27 19:36:56 +02:00
maj00r
707b4de2f4 Editor checkpoint: turnout domain service, web->editor rename, Id-1 templates
Checkpoint of the editor branch so work isn't carried loose across a branch
switch. Builds.

- maj0sted turnout geometry moved into the domain: domain::lay_turnout lays the
  diverging path (pre-blade lead, blade at beta, arc of alfa-beta, frog rail) and
  solve_junction is a thin adapter onto it.
- the maj0sted web/ tree renamed to editor/ (namespace and paths) - there is no
  WASM here, it's plain native C++ the editor calls.
- click places the switch at its start (początek rozjazdu); the branch's leaving
  straight is locked tangent to the frog; switches can be slid along the through
  track and flipped L/P.
- Polish turnout templates carry catalogue length (Id-1-consistent lengths), plus
  blade-angle and pre-blade fields (left 0 until the catalogue figures are in).
- TURNOUT_SPEC.md: the Bezier-based runtime/model contract.
- also folds in other pending branch changes (scenery, tests) to leave a clean tree.
2026-08-27 19:36:55 +02:00
maj00r
d069743265 Switches: enforced branch tangency, internal basket, Polish turnout templates
A switch (rozjazd) is a relation on a through niweleta, not a niweleta - the
through track is never split. It marks the theoretical point on the through
centreline; two tangent lines cross there (the through heading and that heading
turned by the crossing mark 1:n), and the internal diverging curve is fitted to
round the corner between them, reusing the same GapFitter the gap fits go
through. So the curve is free: a plain arc, or a łuk koszowy right inside the
switch. The theoretical tangent lengths and the curve length follow from it and
are read off, not set.

The branch niweleta's leaving straight is now locked to the frog: it starts at
the frog and runs along the frog heading every solve, only its length is the
user's. That enforces tangency no matter what is drawn, and because the first
straight is pinned tangent, any fit in the branch's first gap - including a
basket - meets the switch tangentially by construction.

solve_layout solves the niwelety and the switches together in two passes: solve,
read each frog, pin the branches, solve again. Switches serialize as an ejuncs /
ejunc section; older files without it still load.

Typical Polish turnouts (rozjazdy zwyczajne) are offered as templates that set
the crossing mark and radius together; the length each spans is computed from
them (arc = R*atan(1/n)) and shown, so how long the switch is is known.
2026-08-27 19:36:55 +02:00
maj00r
d4b1d73338 Keep tiles on disk, add basket curves, undo the image flip
The flip was my mistake. Measured against the service instead of guessed:
fetching a box and its northern half shows the half matching the top of the
whole, so the images arrive with their first row at the north edge and the
default texture coordinates were right all along. Both the base map and the
orthophoto go back to them.

Imagery of a given piece of ground does not change, so a fetched tile is now
kept under editor/cache/orto and the next session starts from it. Until now
the cache lived only in memory, which meant going back to a slow and flaky
service for ground that had already been fetched once.

Gaps can be fitted with a basket curve as well: the library's compound mode,
with clothoids at both ends and a chain of arcs between them. Arcs can be
added and removed; the closing one takes whatever deflection the earlier
ones leave, so its length is not offered for editing.
2026-08-27 19:36:55 +02:00
maj00r
32a4df6111 Fix the base map orientation, drop the city overlay, keep td.scn out of the editor
The Geoportal images arrive with their first row at the north edge, which is
the opposite of what the default texture coordinates assume - the vertical
ones are now swapped, for the picker's base map and the orthophoto alike.

With the real map carrying its own detail as the view zooms in, the drawn
city markers had nothing to add and are gone, along with the coordinate
table behind them. Only the country's extent remains, to open the picker on.

-editor no longer inherits the scenery named in eu07.ini. That is whatever
was last driven, which has nothing to do with editing; without an explicit
-s the editor opens on empty ground.
2026-08-27 19:36:55 +02:00
maj00r
8d007291e2 Put Geoportal imagery behind the plan and clear the scenery on a new map
The location picker now shows the real topographic map instead of a
hand-drawn outline, fetched as one WMS image for whatever box is on screen;
the schematic border is gone and only the city list remains, to jump the
view somewhere recognisable. Under the plan itself a georeferenced map gets
the orthophoto, tiled through the layout library's own EPSG:2180 grid and
cache.

What a native build was missing is here: the download (WinHTTP, on worker
threads), the decode and the texture. Finished transfers are handed back on
the thread that drains them, so both the library's cache and the graphics
API stay on one thread. The imagery service turns down browser user agents
and fails the odd request even for a plain one, so requests go out as curl
and are retried a few times.

Starting a new map now drops the loaded scenery and comes back on empty
ground, instead of leaving the previous scenery's contents under a plan that
has nothing to do with them. The reload is deferred to the top of the frame:
taking the editor off the mode stack while it is drawing its own panels
would pull the ground out from under them.
2026-08-27 19:36:55 +02:00
maj00r
bf82c35734 Georeference a new map against a point in Poland
"New map" now asks what is being started. A fictional scenery keeps its own
frame, where coordinates mean nothing outside it. A real one pins the
scenery's zero to a point picked off a map of Poland, so the plan is drawn
in EPSG:2180 - the frame the layout library already works in, and the one
Geoportal serves imagery in.

The projection is the standard transverse Mercator series for PUWG 1992 on
GRS80. Checked against known values: the central meridian comes out at
easting 500000 exactly, Warszawa at 637382/486757, Krakow at 567614/244588,
Gdansk at 477038/720937.

The map behind the picker is a schematic border outline plus the larger
cities, projected from geographic coordinates on first use. It is coarse on
purpose - it is there to point at a region, not to measure against - and the
dialog says so. No network is involved.
2026-08-27 19:36:55 +02:00
maj00r
3fbc1aaaaf Draw the track plan on the scenery instead of on a canvas
The plan panel had its own 2D canvas, which was the wrong place for it: the
layout is drawn on the scenery. The canvas is gone. Points are laid by
clicking the ground under the cursor, the solved geometry is painted over
the rendered world through the colour pass camera, and the panel keeps only
the controls - fits, storage and what is being edited.

The left button belongs to the plan tool while it is up, so a click no
longer also picks or places a scenery node. Clicks landing on a panel stay
with the panel.

With no georeference set the plan frame and the world share an origin, so
easting runs along world x and northing against world z.
2026-08-27 19:36:55 +02:00
maj00r
db49039b9d Put the editor viewport into a top-down orthographic plan view
Opening the Plan tool now switches the whole colour pass to an orthographic
projection and pins the camera looking straight down, so the plan is drawn
against the loaded scenery rather than in a window of its own. Closing the
tool hands the camera's orientation back; leaving the editor clears the view
outright. The wheel changes how much ground fits on screen instead of moving
the camera, which with no perspective would change nothing but the clipping.

Both configured backends follow: the experimental renderer swaps the pass
projection (and fits its shadow cascades to it), the modern GL renderer uses
its existing ortho helpers. Everything reading the pass projection - the
mouse ray, the frustum test - picks the change up on its own.

The experimental renderer also publishes its colour pass camera now, which
it previously left at the interface defaults, so overlays can place world
points on screen.
2026-08-27 19:36:55 +02:00
maj00r
4bef28cf94 Add a track layout drawing board to the editor
A "Plan" panel, opened from Track layout in the editor's menu bar, draws a
niweleta the way the web frontend did: click to lay chained straights, drag
a corner to move it, and pick a fit for each gap between them. The geometry
is entirely maj0sted's - the panel gathers the input, hands the document to
solve_project and paints the rails that come back, coloured by whether they
came from a straight, an arc or a clothoid. Scenery nodes are untouched.

A fit the library cannot produce is simply absent from its answer, so the
gap is labelled as not fitting rather than left looking as though its
parameters took effect. Save/load go through the library's own document
format, so a project written here opens in the same state.

Also translates the comments added with -editor into English, matching the
files they sit in.
2026-08-27 19:36:55 +02:00
maj00r
e3237034db Give the editor a camera when it starts on its own
The scenery camera is set up by the driver mode, which never runs when the
exe starts straight in the editor - the editor then copied an untouched
global camera and rendered a black view with only the UI on top. Entering
the editor as the startup mode now initialises the camera itself, falling
back on a view above the origin for sceneries that name no camera, and
resets the timers so the loading time does not land in the first frame.
2026-08-27 19:36:55 +02:00
maj00r
94b01c8ca3 Add -editor startup so the exe comes up straight in the editor
The editor was reachable only from the driver mode with F11, which meant
loading a scenario and starting the whole simulation first. With -editor
the scenario loader hands over to the editor instead of the driver, so
physics, AI and timetables never run - only the loaded world is there to
be worked on.

Without -s the scenery from eu07.ini is used, falling back to pusta.scn
when the ini names none. F11 quits instead of popping the mode, since the
editor is now the bottom of the mode stack and popping it would leave the
rest of the frame reading an empty one. Editor entry forces free-fly even
with no vehicle to leave.
2026-08-27 19:36:55 +02:00
jakubg1
363b50c9c2 Remove training card window
This was a leftover from attempts at modifying MaSzyna for professional simulators.
Interaction with this window could cause a crash.
2026-08-23 22:37:33 +02:00
Sebastian Krzyszkowiak
22361eec1d Fix compilation with discord-rpc turned off (#135)
Closes #55
2026-07-15 21:50:16 +02:00
combolek
d863ec18c3 uilayer: Implement ui.bg.opacity setting (#139)
uilayer: Implement up ui.bg.opacity setting
2026-07-12 00:41:25 +02:00
0207c3f53c Merge pull request #128 from dos1/first-frame-pause
drivermode: Make sure to draw the first frame before pausing
2026-07-02 09:58:05 +02:00
e3ccdd98ab Merge pull request #127 from dos1/randomseed
Initialize random seed early in the init sequence
2026-07-02 09:57:46 +02:00
Sebastian Krzyszkowiak
f8339d9bfb drivermode: Make sure to draw the first frame before pausing
Otherwise the screen stays empty when the game is launched paused.
2026-07-02 09:37:07 +02:00
Sebastian Krzyszkowiak
86e91b187a scenarioloaderuilayer: Make the bottom gradient smoother
Since gamma factor is used, we need more than 256 steps to make
the gradient smooth.
2026-07-02 06:49:52 +02:00
Sebastian Krzyszkowiak
8b47d2af51 scenarioloaderuilayer: Scale the loading screen to the window size 2026-07-02 06:49:52 +02:00
Sebastian Krzyszkowiak
c98441d1aa Initialize random seed early in the init sequence
Network client will overwrite the seed if necessary.
2026-07-02 03:14:54 +02:00
d60841944d Merge pull request #124 from xwizard/refactor/user-config-path
refactor: extract user_config_path() helper
2026-06-30 22:35:53 +02:00
Mateusz Włodarczyk
fe603f15eb refactor: extract user_config_path() helper
Consolidates platform-specific config-directory logic (APPDATA on Windows,
~/Library/Application Support on macOS, ~/.config on Linux) into a single
user_config_path(filename) function in utilities/utilities.{cpp,h}, removing
copy-pasted #ifdef blocks from application.cpp, editor/editorSettings.cpp,
input/{keyboard,gamepad,drivermouse,uart}input.cpp.
2026-06-30 21:30:36 +02:00
jerrrrycho
d85096f64d reformat: remove redundant parentheses 2026-06-30 21:19:46 +02:00
Sebastian Krzyszkowiak
2f187b470f drivermode: Allow to (un)set picking mode by combining Alt with Shift/Ctrl
Pressing Alt toggles picking mode. This is fine for keyboard and mouse usage,
but some use cases, such as disabling picking mode when a finger is placed
on a thumbstick and enabling it back when it's lifted, require finer control.
Allow to always enable picking mode when pressing Alt with Shift, and to always
disable picking mode when pressing Alt with Ctrl.
2026-06-30 06:14:43 +02:00
jerrrrycho
7a32e7e9ab Merge pull request #113 from MaSzyna-EU07/refactor-nullptr
refactor: replace null with nullptr
2026-06-29 13:40:06 +02:00
jerrrrycho
bf147dd155 refactor: replace null with nullptr 2026-06-29 02:48:25 +02:00
75461eaf86 World Streaming Part 1 2026-06-18 18:18:02 +02:00
449609d817 Snap to ground || New terrain system || Terrain sculping || Mesh simplification 2026-06-17 08:56:42 +02:00
5a39ed5193 ImGuizmo System OpenGL 2026-06-17 01:19:59 +02:00
3bed2212d7 Editor settings for New/Legacy camera controll 2026-06-16 23:53:01 +02:00
c0abea1473 Merge pull request #109 from docentYT/fix-compilation-warnings
Fix compilation warnings
2026-06-16 11:14:25 +02:00