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

85 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
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
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
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
76eede79aa Merge pull request #154 from MaSzyna-EU07/uart-independent-dynamic-brake
Add independent dynamic brake support (eg. Vectron) to UART controllers
2026-09-05 20:04:34 +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
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
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
maj00r
0aceb120e2 make targetfps great again 2026-08-25 18:03:55 +02:00
Ryba04
72f932e5d9 Add independent dynamic brake support (eg. Vectron) to UART controllers 2026-08-16 21:34:22 +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
maj00r
5fce417a51 fix: guard Random/LocalRandom against inverted min>max bounds
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.
2026-07-02 00:37:52 +02:00
maj00r
03bfbb1345 fix: restore min>max tolerance lost in clamp->std::clamp migration
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.
2026-07-02 00:37:52 +02:00
80c3feac03 Merge branch 'master' of https://github.com/MaSzyna-EU07/maszyna 2026-06-30 23:35:46 +02:00
4867f60a65 parser: keep UTF-8 multibyte chars intact when lowercasing tokens 2026-06-30 23:35:33 +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
1aac9b4e76 Merge pull request #111 from MaSzyna-EU07/opengl-instancing
Some rendering improvements
2026-06-16 23:09:12 +02:00
306ed64f38 Merge pull request #110 from xwizard/macos-arm64-build
Add macOS ARM64 build support
2026-06-16 11:15:46 +02:00
1be56de0fa Add sleepermodel optional parameter for tracks 2026-05-19 22:10:47 +02:00
Mateusz Włodarczyk
dd412894bb Add macOS ARM64 build support
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.
2026-05-19 10:45:18 +02:00
Christopher Kwiatkowski
3707a7845b Merge pull request #1 from docentYT/fix-clamp-exception
Merge with fix-clamp-exception
2026-05-12 17:01:47 +02:00
docentYT
a787bdd77c fix min max in Random 2026-05-12 16:56:48 +02:00
docentYT
be91c4da70 Fix C4566 2026-05-12 16:36:32 +02:00
docentYT
6542021253 Optimize substr_path implementation and change return type to sd::string 2026-05-07 12:59:57 +02:00
f4f4897798 Revert substr_path function 2026-05-06 22:20:32 +02:00
cde62d42e3 Merge pull request #99 from docentYT/utilities-simplification
Utilities simplification
2026-05-06 18:35:29 +02:00
docentYT
5367cb9940 Merge remote-tracking branch 'upstream/master' into utilities-simplification 2026-05-05 23:08:43 +02:00
Christopher Kwiatkowski
112c714f79 Resolve conflict 2026-05-04 11:47:38 +02:00
docentYT
d75301f3c7 Add once again parser optimizations 2026-05-04 11:45:44 +02:00
Christopher Kwiatkowski
d64dfe29bf Merge branch 'master' into faster-parser 2026-05-04 11:39:19 +02:00
docentYT
e8335a316b Fix not all cars are loaded 2026-05-04 11:31:42 +02:00
3a42a8c158 Revert "Reduce string creations and deletions" 2026-05-04 01:24:11 +02:00
e7edf601c2 Merge pull request #100 from docentYT/faster-parser
Reduce string creations and deletions
2026-05-04 00:03:41 +02:00
docentYT
13c7317f6e Do not allow implicit cast for inline std::string to_string(bool Value) and change to_string to std::string for non boolean values 2026-05-03 15:49:29 +02:00
docentYT
cef6b258a1 Reduce string creations and deletions 2026-05-02 22:17:20 +02:00
docentYT
7081dab3c2 Replace RadToDeg and DegToRad with glm::degrees and glm::radians 2026-05-02 14:51:13 +02:00
docentYT
ca699d117f Remove not needed includes 2026-05-02 14:40:31 +02:00
docentYT
b734cbcd51 Refactor smoothInterpolate 2026-05-02 01:37:22 +02:00
docentYT
3628eb0fc3 Replace interpolate with std::lerp and glm::mix 2026-05-02 01:31:38 +02:00
docentYT
362338ee5e Refactor min_speed 2026-05-02 00:43:02 +02:00