16
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-07-21 17:09:19 +02:00

Stream deferred visual nodes in camera-distance order (nearest first)

The progressive load previously streamed the deferred visual nodes (3d model
instances + terrain shapes/lines) in file order, so distant scenery could load
before the player's surroundings. This builds them nearest-camera first instead.

The visual pass now runs in two steps. Enumeration replays the twin and captures
each visual node verbatim (its resolved tokens as text -- numbers round-trip
losslessly through cParser) together with the transform/group context it was read
under and, for models, its transformed world position. Once the replay is
exhausted the records are sorted by squared distance to the camera (terrain shapes
first so the ground appears before the props on it), then built a budgeted slice
per frame through the normal node path with the captured transform and group
restored -- so placement, grouping and the per-cell instance buckets come out
identical to an in-order load.

Two supporting fixes make out-of-order/late insertion correct:
- a cell/section whose geometry was already baked (the renderer finalised it
  before a deferred node arrived) now appends the new shape/lines straight into
  its live geometry bank instead of merging into vertex-freed geometry, which
  would silently drop it; create_geometry() remembers the bank for every cell.
- events that bind to visual model instances (lights/animation/texture/visible)
  are deferred from InitEvents() to a new InitInstanceEvents() run after the
  visual nodes are built, so their target models exist when they initialise.

Verified on td.scn: playable ~2s, 540 deferred nodes enumerated and built
nearest-first ~0.5s later, no duplicate instances.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
maj00r
2026-06-23 17:11:39 +02:00
parent c10fadbfc9
commit 1160bfecac
8 changed files with 326 additions and 4 deletions

View File

@@ -14,6 +14,24 @@ http://mozilla.org/MPL/2.0/.
namespace simulation {
// a deferred visual node captured during the visual enumeration pass, to be (re)built
// later in camera-distance order. holds the node's fully resolved tokens as text -- numbers
// come through cParser losslessly (std::to_chars shortest round-trip), so rebuilding from
// this text reproduces the exact same node -- plus a snapshot of the transform/group context
// it was read under, so the out-of-order rebuild places and groups it identically.
struct visual_record {
std::string text; // "node <range> <range> <name> <type> ... end<type>"
glm::dvec3 offset { 0.0 }; // top of the origin stack at capture (identity if none)
glm::vec3 scale { 1.f }; // top of the scale stack at capture (identity if none)
glm::vec3 rotation { 0.f }; // active rotation at capture
bool has_offset { false };
bool has_scale { false };
scene::group_handle group {}; // group the node was read under
glm::dvec3 worldpos { 0.0 }; // transformed position (for models; sort key source)
bool isshape { false }; // terrain shape/lines -> load before models (ground first)
double sortkey { 0.0 }; // squared distance to camera; lower = built sooner
};
struct deserializer_state {
std::string scenariofile;
cParser input;
@@ -27,6 +45,13 @@ struct deserializer_state {
bool visualphase { false };
// set once the whole load (both passes / single text pass) has fully finished
bool done { false };
// visual phase sub-state: while enumerate is true the visual pass captures deferred
// nodes into records instead of building them; once the replay is exhausted they are
// sorted by camera distance and built in that order (enumdone).
bool enumerate { false };
bool enumdone { false };
std::vector<visual_record> records;
std::size_t buildcursor { 0 };
deserializer_state(std::string const &File, cParser::buffertype const Type, const std::string &Path, bool const Loadtraction)
: scenariofile(File), input(File, Type, Path, Loadtraction) { }
@@ -68,6 +93,10 @@ private:
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 );
// visual streaming (camera-ordered deferred load): capture one visual node (already
// past its "node" token) into a record; build budgeted records in sorted order.
void enumerate_visual_node( deserializer_state &State );
bool build_visual_records( std::shared_ptr<deserializer_state> State );
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 );