Defensive fix: when starting the deferred visual pass, clear the origin/scale
stacks and rotation before the directives are replayed. A scenery that leaves an
unbalanced origin on the stack during the infrastructure pass would otherwise have
it applied a second time, shifting every deferred visual node. Balanced sceneries
(the common case) are unaffected -- the directives rebuild the same state.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Splits the progressive load across modes: the loader runs only the first
(infrastructure) pass and then hands off to the driver, so play begins as soon
as tracks/traction/events/signals/the player train are ready. The deferred
visual nodes (3d models, terrain shapes/lines) then stream in from the driver,
a small budget per frame, so the world fills in without a long up-front wait.
- state_serializer: the infrastructure pass now returns to the caller (instead of
chaining the visual pass), restarting the twin for the visual pass; the visual
pass uses a small ~8 ms/frame budget and marks the load done at the end. A
text/compile load (no twin) stays single-pass and finishes as before.
- state_manager: retains the load state across the loader->driver hand-off and
exposes loading_visuals()/continue_loading_visuals().
- driver_mode::update(): advances the deferred visual load each frame.
Verified on td.scn (playable in ~2 s, visuals complete a few seconds later, no
duplicate objects) and on the large tomaszewo scenery (flora/terrain/models
deferred while infrastructure loads). Inserts run on the main thread before the
render step, so progressive loading needs no locking.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
When replaying a binary twin, load in two passes over the same data: the first
pass loads infrastructure (tracks/traction/events/memcells/sounds + directives),
the second pass loads the visual nodes (3d models, terrain shapes/lines) that the
reader skipped via the v6 node-class markers.
- cParser: setReplayPass() selects the served node class (propagated to include
children); restartReplay() rewinds the twin for the second pass.
- state_serializer: first pass uses the infrastructure pass; on completion it
restarts the twin for the visual pass. Stateful directives (trainset, event,
camera, light, sky, time, ...) are skipped on the visual pass so their side
effects do not duplicate; transform/group directives re-run so deferred visual
nodes get correct placement. A text/compile load (no twin) stays single-pass.
Verified: td.scn replays through both passes with no duplicate vehicles/events
and reaches the normal load endpoint. This is the sequential foundation; moving
the visual pass into the driver (so play starts after the infrastructure pass) is
the next step.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Text scenery component files (.scn/.inc/.scm/.ctr) are compiled into
per-file binary twins (.scnb/.incb/.scmb/.ctrb), handled transparently
at the cParser layer: a fresh twin (mtime-checked, version-matched) is
replayed instead of re-tokenizing text; otherwise the text is parsed and
a twin is compiled alongside it for next time.
Format details:
- per-file string interning: keywords/paths stored once, referenced by
varint index (so node/endmodel/... are not repeated as text)
- numeric tokens stored as 8-byte IEEE doubles, not ASCII
- includes kept as references with parameters; random sets stored verbatim
and re-evaluated on every load (choice not frozen at compile time)
- twin writing offloaded to a bounded thread pool so baking overlaps
scene construction instead of blocking the load
The legacy terrain-only .sbt path is removed; terrain now loads as
ordinary scenery content.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>