16
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-07-19 06:29:18 +02:00

Section-index visual streaming (no per-cycle twin re-scan)

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).
This commit is contained in:
maj00r
2026-06-24 23:56:53 +02:00
parent 16b39a21b4
commit 4a9bfdc0aa
6 changed files with 461 additions and 149 deletions

View File

@@ -178,7 +178,7 @@ scenery_binary_writer::begin_node() {
}
void
scenery_binary_writer::end_node( bool Visual, bool Haspos, double X, double Y, double Z ) {
scenery_binary_writer::end_node( bool Visual, bool Haspos, double X, double Y, double Z, double Range ) {
if( false == m_innode ) { return; }
m_innode = false;
auto const body = m_nodebuf.str();
@@ -189,12 +189,13 @@ scenery_binary_writer::end_node( bool Visual, bool Haspos, double X, double Y, d
NODECLASS_VISUAL;
write_varint( m_entries, cls );
write_varint( m_entries, body.size() );
// a visual model node stores its local position right after the span, so the reader can
// hand it to the camera-ring load without the node body being decoded
// a visual model node stores its local position + visibility range right after the span, so
// the reader can hand them to the streaming load without the node body being decoded
if( cls == NODECLASS_VISUAL_POS ) {
sn_utils::ls_float32( m_entries, static_cast<float>( X ) );
sn_utils::ls_float32( m_entries, static_cast<float>( Y ) );
sn_utils::ls_float32( m_entries, static_cast<float>( Z ) );
sn_utils::ls_float32( m_entries, static_cast<float>( Range ) );
}
m_entries.write( body.data(), static_cast<std::streamsize>( body.size() ) );
}
@@ -271,9 +272,13 @@ scenery_binary_reader::next( scenery_entry_view &Out ) {
std::uint64_t tag = 0;
for( ;; ) {
if( m_cursor >= m_end ) { return false; }
char const *markerstart = m_cursor; // where this entry's marker begins (for seek_node)
head = read_varint( m_cursor, m_end );
tag = head & TAG_MASK;
if( tag != TAG_NODE ) { break; }
// record the served node's marker offset so the section-index streamer can seek back to
// it and rebuild the node on demand without re-scanning the whole twin
m_nodeoffset = static_cast<std::size_t>( markerstart - m_begin );
auto const cls = read_varint( m_cursor, m_end );
auto const span = read_varint( m_cursor, m_end );
bool const isvisual = ( cls == NODECLASS_VISUAL ) || ( cls == NODECLASS_VISUAL_POS );
@@ -283,6 +288,7 @@ scenery_binary_reader::next( scenery_entry_view &Out ) {
m_nodepos[ 0 ] = static_cast<double>( read_f32le( m_cursor, m_end ) );
m_nodepos[ 1 ] = static_cast<double>( read_f32le( m_cursor, m_end ) );
m_nodepos[ 2 ] = static_cast<double>( read_f32le( m_cursor, m_end ) );
m_noderange = static_cast<double>( read_f32le( m_cursor, m_end ) );
}
bool const process =
( m_pass == scenery_load_pass::all )