mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-07-18 04:19:19 +02:00
Replaces the camera-distance ring passes with section-following streaming and a persistent section index, so a million-node scenery no longer re-scans the whole twin every time the camera moves into new ground. - v9 node marker also stores range_max; a model visible from beyond the stream radius (or unlimited) is built once up front, the rest stream by section so distant landmarks/traction/buildings don't pop out while flora stays local. - Reader gains node_offset()/seek_node(); cParser exposes the deepest twin's file/path/offset/params and seekReplayNode/setReplayParams to rebuild one node. - First visual pass indexes every deferred node (models via the dispatch fast path, origin-placed flora/shapes in deserialize_node) under its region section while building the spawn area; later cycles rebuild only the newly-wanted sections by seeking straight to their nodes -- O(visible), not O(whole twin). - Build-all fallback retained for ghostview with no camera centre. Known: absolute terrain triangles (no origin) still build in the first pass; only origin-placed content section-streams. Untested in-game (format bump needs a rebake).
170 lines
10 KiB
C++
170 lines
10 KiB
C++
/*
|
|
This Source Code Form is subject to the
|
|
terms of the Mozilla Public License, v.
|
|
2.0. If a copy of the MPL was not
|
|
distributed with this file, You can
|
|
obtain one at
|
|
http://mozilla.org/MPL/2.0/.
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include "utilities/parser.h"
|
|
#include "scene/scene.h"
|
|
|
|
namespace simulation {
|
|
|
|
// a deferred visual node recorded during the first (indexing) replay pass, so it can be rebuilt
|
|
// on demand when its region section enters camera range -- without re-scanning the whole twin.
|
|
// holds where to find the node (which twin + byte offset of its marker) and the context needed to
|
|
// place it identically (the transform active at that point, and the include parameters its "(pN)"
|
|
// tokens resolve against).
|
|
struct visual_ref {
|
|
int twin { -1 }; // index into deserializer_state::twins (file+path to re-open)
|
|
std::size_t offset { 0 }; // byte offset of the node's marker within that twin
|
|
glm::dvec3 t_offset { 0.0 };
|
|
glm::vec3 t_rotation { 0.f };
|
|
glm::vec3 t_scale { 1.f };
|
|
bool has_offset { false };
|
|
bool has_scale { false };
|
|
std::vector<std::string> params; // include parameters in effect (empty for a direct node)
|
|
};
|
|
|
|
struct deserializer_state {
|
|
std::string scenariofile;
|
|
cParser input;
|
|
scene::scratch_data scratchpad;
|
|
using deserializefunctionbind = std::function<void()>;
|
|
std::unordered_map<
|
|
std::string,
|
|
deserializefunctionbind> functionmap;
|
|
// progressive (two-pass) load over a binary twin: first pass loads infrastructure,
|
|
// second pass loads visual nodes. false while in the first (infrastructure) pass.
|
|
bool visualphase { false };
|
|
// set once the whole load (both passes / single text pass) has fully finished
|
|
bool done { false };
|
|
// camera-following visual streaming. once infrastructure is up, visual nodes (3d models,
|
|
// terrain shapes) stream in only for the region sections currently within STREAM_RADIUS of
|
|
// the camera; as the camera moves into new sections they are built too. nothing is unloaded.
|
|
// the twin is replayed once per "build cycle" (a set of newly-wanted sections), building
|
|
// only nodes whose section is in that set and O(1)-skipping the rest by their v7 marker.
|
|
glm::dvec3 ringeye { 0.0 };
|
|
// the camera centre is only meaningful once control reaches the driver (the loader hasn't
|
|
// positioned the camera yet). sampled lazily on the first driver pass; if still unusable
|
|
// (e.g. ghostview at the origin) we fall back to building everything in one pass (ringall).
|
|
bool ringeye_valid { false };
|
|
int ringeye_waits { 0 }; // frames spent waiting for the camera to be positioned
|
|
bool ringall { false }; // no usable camera centre -> build all visual nodes in one pass
|
|
bool sectionmode { false }; // usable camera centre -> stream sections within range, follow camera
|
|
bool shapes_built { false }; // first cycle done: explicit shapes + large-range (eager) models built
|
|
bool initial_done { false }; // the first cycle finished -> scenario finalised (map/events/twin)
|
|
bool pass_active { false }; // a section build cycle's replay pass is currently in progress
|
|
std::unordered_set<int> built; // region-section indices already streamed in
|
|
std::unordered_set<int> tobuild; // section indices targeted by the current/next cycle
|
|
// section index, built during the first replay pass: every deferred visual node is recorded
|
|
// under its region section, so later sections rebuild by seeking straight to their nodes
|
|
// instead of replaying (re-scanning) the whole million-node twin every cycle.
|
|
bool indexed { false }; // first pass finished -> the index below is complete
|
|
std::vector<std::pair<std::string, std::string>> twins; // (file, path) to re-open, interned
|
|
std::unordered_map<std::string, int> twinids; // "path|file" -> index into twins
|
|
std::unordered_map<int, std::vector<visual_ref>> index; // section -> deferred nodes there
|
|
std::unordered_map<int, std::unique_ptr<cParser>> rebuild_parsers; // one reused parser per twin
|
|
|
|
deserializer_state(std::string const &File, cParser::buffertype const Type, const std::string &Path, bool const Loadtraction)
|
|
: scenariofile(File), input(File, Type, Path, Loadtraction) { }
|
|
};
|
|
|
|
class state_serializer {
|
|
|
|
public:
|
|
|
|
// methods
|
|
// starts deserialization from specified file, returns context pointer on success, throws otherwise
|
|
std::shared_ptr<deserializer_state>
|
|
deserialize_begin(std::string const &Scenariofile);
|
|
// continues deserialization for given context, amount limited by time, returns true if needs to be called again
|
|
bool
|
|
deserialize_continue(std::shared_ptr<deserializer_state> state);
|
|
// stores class data in specified file, in legacy (text) format
|
|
void
|
|
export_as_text( std::string const &Scenariofile ) const;
|
|
// create new model from node stirng
|
|
TAnimModel * create_model(std::string const &src, std::string const &name, const glm::dvec3 &position);
|
|
// create new eventlauncher from node stirng
|
|
TEventLauncher * create_eventlauncher(std::string const &src, std::string const &name, const glm::dvec3 &position);
|
|
|
|
private:
|
|
// methods
|
|
// restores class data from provided stream
|
|
void deserialize_area( cParser &Input, scene::scratch_data &Scratchpad );
|
|
void deserialize_isolated( cParser &Input, scene::scratch_data &Scratchpad );
|
|
void deserialize_assignment( cParser &Input, scene::scratch_data &Scratchpad );
|
|
void deserialize_atmo( cParser &Input, scene::scratch_data &Scratchpad );
|
|
void deserialize_camera( cParser &Input, scene::scratch_data &Scratchpad );
|
|
void deserialize_config( cParser &Input, scene::scratch_data &Scratchpad );
|
|
void deserialize_description( cParser &Input, scene::scratch_data &Scratchpad );
|
|
void deserialize_event( cParser &Input, scene::scratch_data &Scratchpad );
|
|
void deserialize_lua( cParser &Input, scene::scratch_data &Scratchpad );
|
|
void deserialize_firstinit( cParser &Input, scene::scratch_data &Scratchpad );
|
|
void deserialize_group( cParser &Input, scene::scratch_data &Scratchpad );
|
|
void deserialize_endgroup( cParser &Input, scene::scratch_data &Scratchpad );
|
|
void deserialize_light( cParser &Input, scene::scratch_data &Scratchpad );
|
|
void deserialize_node( cParser &Input, scene::scratch_data &Scratchpad );
|
|
void deserialize_origin( cParser &Input, scene::scratch_data &Scratchpad );
|
|
void deserialize_endorigin( cParser &Input, scene::scratch_data &Scratchpad );
|
|
void deserialize_scale( cParser &Input, scene::scratch_data &Scratchpad );
|
|
void deserialize_endscale( cParser &Input, scene::scratch_data &Scratchpad );
|
|
void deserialize_rotate( cParser &Input, scene::scratch_data &Scratchpad );
|
|
void deserialize_sky( cParser &Input, scene::scratch_data &Scratchpad );
|
|
void deserialize_test( cParser &Input, scene::scratch_data &Scratchpad );
|
|
void deserialize_time( cParser &Input, scene::scratch_data &Scratchpad );
|
|
void deserialize_trainset( cParser &Input, scene::scratch_data &Scratchpad );
|
|
void deserialize_terrain( cParser &Input, scene::scratch_data &Scratchpad );
|
|
void deserialize_endtrainset( cParser &Input, scene::scratch_data &Scratchpad );
|
|
TTrack * deserialize_path( cParser &Input, scene::scratch_data &Scratchpad, scene::node_data const &Nodedata );
|
|
TTraction * deserialize_traction( cParser &Input, scene::scratch_data &Scratchpad, scene::node_data const &Nodedata );
|
|
TTractionPowerSource * deserialize_tractionpowersource( cParser &Input, scene::scratch_data &Scratchpad, scene::node_data const &Nodedata );
|
|
TMemCell * deserialize_memorycell( cParser &Input, scene::scratch_data &Scratchpad, scene::node_data const &Nodedata );
|
|
TEventLauncher * deserialize_eventlauncher( cParser &Input, scene::scratch_data &Scratchpad, scene::node_data const &Nodedata );
|
|
TAnimModel * deserialize_model( cParser &Input, scene::scratch_data &Scratchpad, scene::node_data const &Nodedata );
|
|
TDynamicObject * deserialize_dynamic( cParser &Input, scene::scratch_data &Scratchpad, scene::node_data const &Nodedata );
|
|
sound_source * deserialize_sound( cParser &Input, scene::scratch_data &Scratchpad, scene::node_data const &Nodedata );
|
|
void init_time();
|
|
// skips content of stream until specified token
|
|
void skip_until( cParser &Input, std::string const &Token );
|
|
// transforms provided location by specifed rotation and offset
|
|
glm::dvec3 transform( glm::dvec3 Location, scene::scratch_data const &Scratchpad );
|
|
void export_nodes_to_stream( std::ostream &, bool Dirty ) const;
|
|
// region-section index (row-major, clamped to the grid) enclosing a world position --
|
|
// matches basic_region::section()'s indexing, used to bucket visual nodes for streaming.
|
|
static int section_index( glm::dvec3 const &World );
|
|
// record the visual node currently being replayed (twin/offset/transform/params) under its
|
|
// section in the index, so it can be rebuilt later without re-scanning the twin.
|
|
void capture_node( cParser &Input, scene::scratch_data const &Scratchpad, glm::dvec3 const &World );
|
|
// rebuild every indexed node of one section by seeking straight to it (no twin re-scan).
|
|
void rebuild_section( deserializer_state &State, int Section );
|
|
// interns a (file, path) pair into State.twins, returning its index
|
|
static int twin_id( deserializer_state &State, std::string const &File, std::string const &Path );
|
|
// members
|
|
// camera-following visual streaming state, mirrored from deserializer_state each
|
|
// deserialize_continue() call so deserialize_model()/deserialize_node() can decide whether
|
|
// a node's section is in the current build set. inactive (builds everything) outside the
|
|
// visual phase, or in ringall (no camera centre) where every node is built in one pass.
|
|
bool m_ringactive { false };
|
|
bool m_ringall { false }; // no camera centre available -> build every node in one pass
|
|
bool m_sectionmode { false }; // stream by camera-range sections
|
|
bool m_shapes_built { false }; // first cycle done: shapes + large-range (eager) models built (skip them)
|
|
glm::dvec3 m_ringeye { 0.0 };
|
|
std::unordered_set<int> const *m_tobuild { nullptr }; // section indices to build this cycle
|
|
// first replay pass records each deferred node into the index (m_indexing); later cycles
|
|
// rebuild sections straight from it (m_rebuilding bypasses the section test so the chosen
|
|
// node always builds). m_state gives deserialize_model()/node() access for capture.
|
|
bool m_indexing { false };
|
|
bool m_rebuilding { false };
|
|
deserializer_state *m_state { nullptr };
|
|
};
|
|
|
|
} // simulation
|
|
|
|
//---------------------------------------------------------------------------
|