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.
"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.
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.
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.
std::uniform_real/int_distribution has UB when min>max. The old "fancy Random"
used interpolate(a,b,t) which tolerated a>b; the migration to
std::uniform_real_distribution did not, and only one inverted call site was
fixed by hand. Swap the bounds inside Random()/Random(int)/LocalRandom() so
every call site is safe, and reorder the two inverted literal calls in
Driver.cpp (fActionTime buzzer / departure delay) for readability.
The dumb3d->glm / utilities-simplification refactor replaced the old custom
clamp() (which tolerated Lo>Hi, returning the upper bound) with std::clamp,
where inverted bounds are undefined behaviour. Only a few sites were patched
afterwards, by hand, with std::minmax plumbing (incl. a stray `static` inside
a per-axle loop). Others silently produced wrong results in the AI braking /
acceleration path.
Add a safe_clamp() helper (normalizes inverted bounds) and use it ONLY where
Lo<=Hi cannot be proven at compile time (config min/max pairs, sign-dependent
expressions, container underflow). Sites with constant bounds or [0, x>=0]
keep std::clamp. Remove the manual std::minmax workarounds and rewrite the
damaged-track jolt code value-first.
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.
Three small source tweaks that let the OpenGL renderer build and run
on Apple Silicon without affecting the Windows/Linux paths:
- stdafx.h: use GLM_FORCE_NEON on ARM64 instead of GLM_FORCE_AVX2.
AVX2 pulls <immintrin.h>, which clang on ARM64 rejects because
the x86 intrinsics are unimplemented.
- utilities/utilities.cpp: provide an Apple libc++ fallback for
Now() using localtime_r + strftime, since
std::chrono::current_zone() is not yet implemented there.
- audio/audio.h: prefer Khronos-style <AL/al.h> headers when they
are on the include path (brew openal-soft) and fall back to the
deprecated Apple OpenAL.framework's <OpenAL/al.h> only otherwise.