mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-07-18 01:59:19 +02:00
Add two-pass progressive scenery load (sequential; infra then visuals)
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>
This commit is contained in:
@@ -49,6 +49,10 @@ state_serializer::deserialize_begin( std::string const &Scenariofile ) {
|
||||
std::make_shared<deserializer_state>( Scenariofile, cParser::buffer_FILE, Global.asCurrentSceneryPath, Global.bLoadTraction );
|
||||
state->scenariofile = Scenariofile;
|
||||
state->scratchpad.name = Scenariofile;
|
||||
// first pass loads infrastructure (tracks/traction/events/memcells/sounds + directives);
|
||||
// visual nodes are skipped by the reader and loaded in a second pass. for a text/compile
|
||||
// load (no twin) this is a no-op and everything loads in a single pass.
|
||||
state->input.setReplayPass( scene::scenery_load_pass::infrastructure );
|
||||
scene::Groups.create();
|
||||
|
||||
if( false == state->input.ok() )
|
||||
@@ -104,11 +108,37 @@ state_serializer::deserialize_continue(std::shared_ptr<deserializer_state> state
|
||||
cParser &Input = state->input;
|
||||
scene::scratch_data &Scratchpad = state->scratchpad;
|
||||
|
||||
// stateful directives that build objects/lists; on the visual (second) pass they are
|
||||
// skipped wholesale so their side effects (trainsets, events, cameras, ...) don't
|
||||
// duplicate. transform/group directives (origin/rotate/scale/group) and idempotent
|
||||
// setters are re-run, so deferred visual nodes get the correct placement.
|
||||
static std::unordered_map<std::string, std::string> const visualskip {
|
||||
{ "trainset", "endtrainset" },
|
||||
{ "event", "endevent" },
|
||||
{ "camera", "endcamera" },
|
||||
{ "light", "endlight" },
|
||||
{ "description", "enddescription" },
|
||||
{ "test", "endtest" },
|
||||
{ "sky", "endsky" },
|
||||
{ "time", "endtime" },
|
||||
{ "terrain", "endterrain" },
|
||||
};
|
||||
|
||||
// deserialize content from the provided input
|
||||
auto timelast { std::chrono::steady_clock::now() };
|
||||
std::string token { Input.getToken<std::string>() };
|
||||
while( false == token.empty() ) {
|
||||
|
||||
if( state->visualphase ) {
|
||||
auto const skip = visualskip.find( token );
|
||||
if( skip != visualskip.end() ) {
|
||||
// consume the stateful directive without running its handler
|
||||
skip_until( Input, skip->second );
|
||||
token = Input.getToken<std::string>();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
auto lookup = state->functionmap.find( token );
|
||||
if( lookup != state->functionmap.end() ) {
|
||||
lookup->second();
|
||||
@@ -131,6 +161,15 @@ state_serializer::deserialize_continue(std::shared_ptr<deserializer_state> state
|
||||
deserialize_firstinit( Input, Scratchpad );
|
||||
}
|
||||
|
||||
// first (infrastructure) pass finished: run a second pass over the same twin to load
|
||||
// the visual nodes that were skipped. only possible when replaying a binary twin; a
|
||||
// text/compile load did everything in one pass (restartReplay returns false).
|
||||
if( ( false == state->visualphase )
|
||||
&& ( true == Input.restartReplay( scene::scenery_load_pass::visual ) ) ) {
|
||||
state->visualphase = true;
|
||||
return true; // continue with the visual pass
|
||||
}
|
||||
|
||||
scene::Groups.close();
|
||||
|
||||
scene::Groups.update_map();
|
||||
|
||||
Reference in New Issue
Block a user