16
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-07-18 01:59:19 +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

@@ -319,7 +319,7 @@ void cParser::bakeFinishNode()
// flush a node still open at end-of-file or one whose type was unrecognized
if (m_bakenode_active && m_writer)
{
m_writer->end_node(m_bakenode_visual, m_bakenode_haspos, m_bakenode_pos[0], m_bakenode_pos[1], m_bakenode_pos[2]);
m_writer->end_node(m_bakenode_visual, m_bakenode_haspos, m_bakenode_pos[0], m_bakenode_pos[1], m_bakenode_pos[2], m_bakenode_rangemax);
}
m_bakenode_active = false;
}
@@ -817,22 +817,48 @@ void cParser::readToken(std::string &out, bool ToLower, const char *Break)
if (m_bakenode_active)
{
++m_bakenode_count;
if (m_bakenode_count == 2)
{
// 2nd node entry is range_max (visibility range); kept in the model marker
// so the streamer builds far-but-large-range models eagerly. -1 (unlimited)
// when it isn't a plain number (e.g. an unresolved parameter).
double rmax;
m_bakenode_rangemax = (sniffNumber(rawtoken, rmax) ? rmax : -1.0);
}
if ((m_bakenode_count == 5) && m_bakenode_end.empty())
{
// 5th node entry is the type token (node, range_max, range_min, name, type)
classifyNodeType(lowered, m_bakenode_visual, m_bakenode_end);
// a model node's entries 6,7,8 are its local X Y Z -- record them in the
// marker so the camera-ring load can skip the node without reading its body
m_bakenode_haspos = (lowered == "model");
{
bool const isshape =
(lowered == "triangles") || (lowered == "triangle_strip") || (lowered == "triangle_fan")
|| (lowered == "lines") || (lowered == "line_strip") || (lowered == "line_loop");
m_bakenode_haspos = (lowered == "model") || isshape;
m_bakenode_shape = isshape;
m_bakenode_posstate = 0;
m_bakenode_posidx = 0;
}
}
else if (m_bakenode_haspos && (m_bakenode_count >= 6) && (m_bakenode_count <= 8))
else if (m_bakenode_haspos && (false == m_bakenode_shape) && (m_bakenode_count >= 6) && (m_bakenode_count <= 8))
{
m_bakenode_pos[m_bakenode_count - 6] = value;
}
else if ((false == m_bakenode_end.empty()) && (lowered == m_bakenode_end))
else if (m_bakenode_shape && (m_bakenode_posstate < 3))
{
// walk a shape to its first vertex: 0=before material -> if "material" enter the
// block (1), else it is a shortcut material (start seeking numbers, 2); 1=skip until
// "endmaterial"; 2=skip the texture string, then take the 3 vertex numbers.
double sv;
if (m_bakenode_posstate == 0) { m_bakenode_posstate = (lowered == "material") ? 1 : 2; }
else if (m_bakenode_posstate == 1) { if (lowered == "endmaterial") m_bakenode_posstate = 2; }
else if (sniffNumber(rawtoken, sv)) { m_bakenode_pos[m_bakenode_posidx++] = sv; if (m_bakenode_posidx == 3) m_bakenode_posstate = 3; }
}
else if ((false == m_bakenode_end.empty()) && (lowered == m_bakenode_end))
{
// terminator captured: close the node
m_writer->end_node(m_bakenode_visual, m_bakenode_haspos, m_bakenode_pos[0], m_bakenode_pos[1], m_bakenode_pos[2]);
m_writer->end_node(m_bakenode_visual, m_bakenode_haspos, m_bakenode_pos[0], m_bakenode_pos[1], m_bakenode_pos[2], m_bakenode_rangemax);
m_bakenode_active = false;
}
}
@@ -924,11 +950,54 @@ bool cParser::skipReplayNode()
return false;
}
bool cParser::currentNodePosition(double &X, double &Y, double &Z)
bool cParser::currentNodePosition(double &X, double &Y, double &Z, double &Range)
{
// delegate to the deepest active include child (it serves the current node), like skip
if (mIncludeParser) { return mIncludeParser->currentNodePosition(X, Y, Z); }
return (m_replay && m_reader && m_reader->node_position(X, Y, Z));
if (mIncludeParser) { return mIncludeParser->currentNodePosition(X, Y, Z, Range); }
return (m_replay && m_reader && m_reader->node_position(X, Y, Z, Range));
}
std::string cParser::currentReplayFile()
{
if (mIncludeParser) { return mIncludeParser->currentReplayFile(); }
return mFile;
}
std::string cParser::currentReplayPath()
{
if (mIncludeParser) { return mIncludeParser->currentReplayPath(); }
return mPath;
}
std::size_t cParser::currentReplayOffset()
{
if (mIncludeParser) { return mIncludeParser->currentReplayOffset(); }
return (m_reader ? m_reader->node_offset() : 0);
}
std::vector<std::string> cParser::currentReplayParams()
{
if (mIncludeParser) { return mIncludeParser->currentReplayParams(); }
return parameters;
}
void cParser::seekReplayNode(std::size_t Offset)
{
// rebuild serves a single self-contained node, so drop any open include child and rewind
// this twin's reader to the node's marker; the next getToken() decodes it
mIncludeParser = nullptr;
tokens.clear();
if (m_replay && m_reader)
{
m_reader->set_pass(scene::scenery_load_pass::all);
m_reader->seek_node(Offset);
m_replayexhausted = false;
}
}
void cParser::setReplayParams(std::vector<std::string> Params)
{
parameters = std::move(Params);
}
std::vector<std::string> cParser::readParameters(cParser &Input)